比EXISTS条件

时间:2015-11-04 12:55:55

标签: sql

我正在阅读SQL EXISTS 条件并从Techonthenet.com找到此代码段

  

注意:使用SQL EXISTS条件的SQL语句效率非常低,因为子查询对于外部查询表中的每一行都是RE-RUN。有更有效的方法来编写大多数查询,不使用SQL EXISTS条件

除非我跳过它,否则本文不会解释一个不需要这种条件的更有效的查询。任何人都知道他们可能指的是什么?

4 个答案:

答案 0 :(得分:3)

你通常可以使用一些“聪明的”inner join或类似的东西。

然而,总而言之,这些建议已经过时了。是的,曾经有一段时间子查询有巨大的成本,但不一定是这种情况 - 一如既往,简介。并检查执行计划。您的数据库引擎很可能可以很好地处理子查询 - 实际上,它可以比hacky inner join(以及类似的解决方案)更快 :)

始终确保您了解建议背后的理由以及建议的实际应用。关于MS SQL的一个简单示例:

select * from order
where exists (select * from user where order.CreatedBy = user.Id)

多么可怕的子查询,对吧?完全为order表的每一行运行子查询,对吧?好吧,执行计划器足够聪明,可以将其转换为简单的左连接 - 只涉及两次表扫描(或者,如果适用,还有索引搜索)。在其他情况下,引擎可能决定构建哈希集或临时表,或做任何其他智能事情以确保查询快速(在其他权衡中,如内存使用)。如今,如果您的数据库引擎完成任务,您很少会发现您的查询调整比执行计划程序更聪明。事实上,这就是我们首先使用SQL(一种声明性语言)的全部原因!你没有说出应该如何获得结果,而是说出了什么样的关系导致了你想要的结果集,让数据库引擎在如何实际获取数据方面获得了巨大的自由 - 这是否意味着要经历每一个表中的单行逐一,或通过索引寻找。

默认值应始终是以最有意义的方式编写查询。一旦你有了一个漂亮,干净且易于理解的查询,就要考虑任何性能影响,并对结果进行分析(使用真实的测试数据)。查看查询的执行计划 - 如果您关心SQL性能,您真的需要了解执行计划;他们告诉你所有关于查询实际执行方式的信息,以及如何改进查询的各个部分(或者更常见的是涉及的索引和统计信息)。

答案 1 :(得分:1)

首先,不要信任一般性陈述,例如

  

注意:使用SQL EXISTS条件的SQL语句非常多   由于子查询对于外部的每一行都是RE-RUN,因此效率很低   查询表。

对于某些数据库系统,情况可能如此,但其他数据库系统可能能够为此类语句找到更有效的执行计划。 例如,我在我的Oracle数据库上尝试了这样的语句,它使用散列连接来有效地执行语句。

现在有替代方案:

在许多情况下,您可以使用IN子查询。即使在无效率执行EXISTS的数据库系统上,这也可能很有效。

所以,而不是

select * from foo where exists (select 1 from bar where foo.x = bar.y)

select * from foo where x in (select y from bar)

使用ANY

可以写同样的内容
select * from foo where x = any (select y from bar)

在许多情况下,最理想的是使用连接,例如

select foo.* from foo inner join bar on foo.x = bar.y

您可能必须使用DISTINCT确保在foo中的行与bar中的多行匹配时,不会获得重复结果。

select distinct foo.* from foo inner join bar on foo.x = bar.y

答案 2 :(得分:0)

当主查询结果集较小且子查询的结果集为大且子查询使用适当的索引时 - EXISTS / NOT EXISTS是替代IN / NOT IN子句的更好选项。

当您在主查询的较大结果集上有索引并且在子查询中有较小的结果集时 - IN / NOT IN是更好的选项,而不是EXISTS / NOT EXISTS子句。

答案 3 :(得分:0)

Oracle Document回答了这个问题  11.5.3.4 Use of EXISTS versus IN for Subqueries

11.5.3.4将EXISTS与IN用于子查询

在某些情况下,最好使用IN而不是EXISTS。通常,如果选择谓词在子查询中,则使用IN。如果选择谓词在父查询中,则使用EXISTS。

有时,Oracle与IN子句一起使用时,Oracle可以重写子查询,以利用子查询中指定的选择性。当最有选择性的过滤器出现在子查询中并且联接列上有索引时,这是最有用的。相反,当最有选择性的过滤器位于父查询中时,使用EXISTS会很有用。这样可以在根据EXISTS标准过滤行之前,先应用父查询中的选择性谓词。

“示例1:在子查询中使用IN-选择性过滤器” “示例2:在子查询中使用EXISTS-选择性谓词” 是两个示例,它们说明了IN和EXISTS的好处。两个示例都使用具有以下特征的相同架构:

  • employees.employee_id字段上有一个唯一索引。

  • orders.customer_id字段上有一个索引。

  • employees.department_id字段上有一个索引。

  • employee表具有27,000行。

  • 订单表有10,000行。

  • 分析了拥有这些细分的OE和HR模式 使用COMPUTE。

    11.5.3.4.1示例1:在子查询中使用IN-选择性过滤器

此示例演示了如何重写查询以使用IN来提高性能。此查询标识代表客户144下订单的所有员工。

以下SQL语句使用EXISTS:

SELECT /* EXISTS example */
         e.employee_id, e.first_name, e.last_name, e.salary
  FROM employees e
 WHERE EXISTS (SELECT 1 FROM orders o                  /* Note 1 */
                  WHERE e.employee_id = o.sales_rep_id   /* Note 2 */
                    AND o.customer_id = 144);            /* Note 3 */

以下计划输出是前一条语句的执行计划(来自V $ SQL_PLAN)。该计划需要对employees表进行全表扫描,并返回许多行。然后根据订单表(通过索引)过滤所有这些行。

 ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
---- -------------------- --------------- ---------------------- --- ----------
   0 SELECT STATEMENT                                            CHO
   1  FILTER
   2   TABLE ACCESS       FULL            EMPLOYEES              ANA        155
   3   TABLE ACCESS       BY INDEX ROWID  ORDERS                 ANA          3
   4    INDEX             RANGE SCAN      ORD_CUSTOMER_IX        ANA          1

使用IN重写语句将大大减少所使用的资源。

使用IN的SQL语句:

SELECT /* IN example */
         e.employee_id, e.first_name, e.last_name, e.salary
    FROM employees e
   WHERE e.employee_id IN (SELECT o.sales_rep_id         /* Note 4 */
                             FROM orders o
                            WHERE o.customer_id = 144);  /* Note 3 */

以下计划输出是前一条语句的执行计划(来自V $ SQL_PLAN)。优化器将子查询重写到视图中,然后通过唯一索引将其连接到employee表。这将导致更好的计划,因为视图(即子查询)具有选择谓词,因此仅返回几个employee_id。然后,这些employee_id用于通过唯一索引访问employees表。

  ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
---- -------------------- --------------- ---------------------- --- ----------
   0 SELECT STATEMENT                                            CHO
   1  NESTED LOOPS                                                            5
   2   VIEW                                                                   3
   3    SORT              UNIQUE                                              3
   4     TABLE ACCESS     FULL            ORDERS                 ANA          1
   5   TABLE ACCESS       BY INDEX ROWID  EMPLOYEES              ANA          1
   6    INDEX             UNIQUE SCAN     EMP_EMP_ID_PK          ANA

11.5.3.4.2示例2:使用EXISTS-父级中的选择性谓词

此示例演示了如何重写查询以使用EXISTS来提高性能。此查询标识部门80中所有已下订单的销售代表。

以下SQL语句使用IN:

SELECT /* IN example */
         e.employee_id, e.first_name, e.last_name, e.department_id, e.salary
    FROM employees e
   WHERE e.department_id = 80                                    /* Note 5 */
     AND e.job_id        = 'SA_REP'                              /* Note 6 */
     AND e.employee_id IN (SELECT o.sales_rep_id FROM orders o); /* Note 4 */

以下计划输出是前一条语句的执行计划(来自V $ SQL_PLAN)。优化程序已重写SQL语句,以使用orders表上的视图,该视图需要对数据进行排序以返回orders表中存在的所有唯一的employee_id。因为没有谓词,所以会返回许多employee_id。然后使用大量的employee_id列表通过唯一索引访问employees表。

  ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
---- -------------------- --------------- ---------------------- --- ----------
   0 SELECT STATEMENT                                            CHO
   1  NESTED LOOPS                                                          125
   2   VIEW                                                                 116
   3    SORT              UNIQUE                                            116
   4     TABLE ACCESS     FULL            ORDERS                 ANA         40
   5   TABLE ACCESS       BY INDEX ROWID  EMPLOYEES              ANA          1
   6    INDEX             UNIQUE SCAN     EMP_EMP_ID_PK          ANA

以下SQL语句使用EXISTS:

SELECT /* EXISTS example */
         e.employee_id, e.first_name, e.last_name, e.salary
    FROM employees e
   WHERE e.department_id = 80                           /* Note 5 */
     AND e.job_id        = 'SA_REP'                     /* Note 6 */
     AND EXISTS (SELECT 1                               /* Note 1 */
                   FROM orders o
                  WHERE e.employee_id = o.sales_rep_id);  /* Note 2 */

以下计划输出是前一条语句的执行计划(来自V $ SQL_PLAN)。通过重写SQL语句以使用EXISTS可以减少计划的成本。该计划更有效,因为使用了两个索引来满足父查询中的谓词,从而仅返回几个employee_id。然后使用employee_id通过索引访问订单表。

  ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
---- -------------------- --------------- ---------------------- --- ----------
   0 SELECT STATEMENT                                            CHO
   1  FILTER
   2   TABLE ACCESS       BY INDEX ROWID  EMPLOYEES              ANA         98
   3    AND-EQUAL
   4     INDEX            RANGE SCAN      EMP_JOB_IX             ANA
   5     INDEX            RANGE SCAN      EMP_DEPARTMENT_IX      ANA
   6   INDEX              RANGE SCAN      ORD_SALES_REP_IX       ANA          8