mysql中exists巧用与在oracle中的实现方式

发布时间: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 是否存在关联关系。具体分析如下:

  • SELECT a.*, EXISTS (select 1 from TABLE_B b where b.group != ‘xxx’ and b.a_id = a.id) as “status”: 选择要查询的字段,包括表 TABLE_A 的所有字段以及一个新的名为 status 的字段。该字段的值为 0 或 1,表示查询结果与另一个表 lb_serving 是否具有关联关系。
  • FROM TABLE_A a: 指定查询的数据来源为表 TABLE_A,a 为该表的别名。
  • WHERE a.type = ‘00’: 筛选符合条件的记录,其中 a.type 为表 TABLE_A 的字段,‘00’ 为该字段需要匹配的值,表示查询只包含 type 字段等于 ‘00’ 的记录。
  • EXISTS (select 1 from TABLE_B b where b.group!= ‘xxx’ and b.a_id = a.id): 使用 EXISTS 子查询来检查当前的 TABLE_A 记录是否与 TABLE_B 表中的某些记录具有关联关系。如果子查询返回至少一行记录,则 status 字段的值为 1,否则为 0。
  • 这里的 0 和 1 值是通过 EXISTS 子查询的结果集是否为空来判断的,如果子查询返回的结果集为空,那么 EXISTS 的结果为 false,对应的值就是 0;反之,如果子查询返回的结果集不为空,那么 EXISTS 的结果为 true,对应的值就是 1。因此,这个结果是通过 EXISTS 函数的功能来实现的。

    oracle实现

    当我模仿这个语句,在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。到达了优化目的。

    需要做网站?需要网络推广?欢迎咨询客户经理 13272073477