发布时间:2025-12-09 20:11:13 浏览次数:4
在mysql中见过一个这样的语句,执行之后,会多出一列status,这个status列的值有0或1。
select a.*, exists (select 1 from TABLE_B b where b.group != 'xxx' and b.a_id = a.id) as "status" from TABLE_A where a.type = '00';这个查询语句的作用是查询符合条件的 TABLE_A 表中的记录,并添加一个名为 status 的新字段。该新字段表示该记录与另一个表 TABLE_B 是否存在关联关系。具体分析如下:
这里的 0 和 1 值是通过 EXISTS 子查询的结果集是否为空来判断的,如果子查询返回的结果集为空,那么 EXISTS 的结果为 false,对应的值就是 0;反之,如果子查询返回的结果集不为空,那么 EXISTS 的结果为 true,对应的值就是 1。因此,这个结果是通过 EXISTS 函数的功能来实现的。
当我模仿这个语句,在oracle数据库执行一下,发现报ORA-00936:缺失表达式。经过各种检查,判断是exists子查询语句的问题。于是我基于子查询有结果返回1,无结果返回0的原理,造了一个联合查询。
select a.* '1' "status" from TABLE_A a where a.type = '00' and exists(select 1 from TABLE_B b where b.group = 'xxx' and b.a_id = a.id)union allselect a.* '0' "status" from TABLE_A a where a.type = '00' and not exists(select 1 from TABLE_B b where b.group = 'xxx' and b.a_id = a.id)这个语句就非常通俗易懂,就是把有结果和无结果拼装一起。但我很奇怪为什么前面的exists子查询会不行呢
我找了一下资料,发现了好几个原因。
在 Oracle 数据库中,不允许在 SELECT 语句中使用 AS 关键字给别名加双引号。因此,如果要在 Oracle 数据库中运行这个查询,需要将语句中的双引号去掉,改成使用单引号来表示别名。
select a.*, exists (select 1 from TABLE_B b where b.group != 'xxx' and b.a_id = a.id) as 'status' from TABLE_A where a.type = '00';改了之后只是换了一个报错提示,说未找到’from’关键字,这肯定是假的。
在 Oracle 中使用 EXISTS 子查询时,需要使用 FROM DUAL 来表示子查询的结果。所以exists如果在作为子查询,得嵌套在其它的语句里。我在这里选择了case语句,正好符合我的预期,当有结果显示1,无结果显示0。
select a.*, (case when exists (select 1 from TABLE_B b where b.group != 'xxx' and b.a_id = a.id)then '1' else '0' end) as "status" from TABLE_A where a.type = '00';如果使用了联合查询与子查询等方式,性能分析是不可避免的。在这里我使用最简单的方式去判断sql语句的性能。当其它变量都一致时,可能造成结果偏差的因素就是网络。
经过多次测试,在Oracle中,联合查询的执行时间在0.013s0.014s;子查询在0.015s0.020s。
这样的结果并不意外,联合查询中的exists语句是连接查询,子查询对性能消耗并不可忽视。
因此,我们要写出一个效果差不多的连接查询,又不用联合两个结果的语句。
他的思路无非就是想新建一列status,这个status在当TABLE_B中有结果时显示1,无结果时显示0。那其实没必要一定要考虑使用exists作为子查询语句。直接暴力左外连接,然后在返回结果集上直接对这列进行判断。
select a.*, case when b.a_id is not null then '1' else '0' end as "status"from TABLE_A aleft join TABLE_B b on b.group != 'xxx' and b.a_id = a.idwhere a.type = '00';经过性能测试,这个语句执行时间在0.011s~0.012s。到达了优化目的。