2009-04-17 88 views
20

在为this question准备了答案之后,我发现我无法验证我的答案。IN谓词如何在SQL中工作?

在我的第一个编程工作中,我被告知IN()谓词中的查询会针对父查询中包含的每一行执行,因此应避免使用IN

例如,假设查询:

SELECT count(*) FROM Table1 WHERE Table1Id NOT IN (
SELECT Table1Id FROM Table2 WHERE id_user = 1) 
 
Table1 Rows | # of "IN" executions 
---------------------------------- 
     10 |  10 
    100 |  100 
    1000 |  1000 
    10000 | 10000 

这是正确的吗? IN谓词如何实际工作?

+4

非常好的问题。没有看到许多问题要求对SQL中某些事情的实际工作进行细分。人们经常更关心获得他们想要的结果。 – TheTXI 2009-04-17 16:35:08

回答

16

你有关于每一行执行子查询的警告是真实的 - 为相关子查询。

SELECT COUNT(*) FROM Table1 a 
WHERE a.Table1id NOT IN (
    SELECT b.Table1Id FROM Table2 b WHERE b.id_user = a.id_user 
); 

注意,子查询引用外部查询的id_user列。 Table1的每一行上的值id_user可能不同。因此,子查询的结果可能会有所不同,具体取决于外部查询中的当前行。 RDBMS 必须多次执行子查询,一次为外部查询中的每一行执行一次。

您测试的示例是非相关子查询。大多数现代的RDBMS优化器值得他们的盐应该能够告诉子查询的结果何时不取决于外部查询的每一行中的值。在这种情况下,RDBMS会一次运行子查询,缓存其结果,并在外部查询中重复使用它作为谓词。

PS:在SQL中,IN()被称为“谓词”,而不是语句。谓语是评估为真或假的语言的一部分,但不一定能作为语句独立执行。也就是说,你不能只将它作为SQL查询来运行:“2 IN(1,2,3);”虽然这是一个有效的谓词,但它不是一个有效的陈述。

0

不是。但它是用黄色来写这样的查询使用JOIN

4

取决于优化器。检查每个特定查询的确切query plan以查看RDBMS如何实际执行该查询。

在Oracle这会是:

EXPLAIN PLAN FOR «your query» 

在MySQL或PostgreSQL

EXPLAIN «your query» 
+1

@vartec - 阅读查询计划的任何资源?这是我的知识的一个盲目的一面,我还没有找到一个体面的文章在线。 – 2009-04-17 16:36:12

+0

这是数据库管理系统的依赖,但基本上,这让你知道它会做什么基本的操作,并以什么顺序,它会使用哪个索引等。 – vartec 2009-04-17 16:39:32

+0

我认为你必须寻找什么,将是关于数据库调整的文章,作为这首先是查看查询计划的目的。 – vartec 2009-04-17 16:42:22

8

这将完全取决于你使用的数据库,以及准确的查询。

查询优化器有时候非常聪明 - 在您的示例查询中,我期望更好的数据库能够使用与连接相同的技术。更幼稚的数据库可能会多次执行相同的查询。

3

大多数SQL引擎如今几乎总是产生相同的执行计划LEFT JOIN,NOT IN和NOT EXISTS

我会说看你的执行计划,如果你发现了:-)

而且对于Table1Id列有NULL值,您将不会收到任何数据

5

这取决于有问题的RDBMS

详细信息,在这里分析:

单位秒园艺:

  1. MySQL将优化查询,以这样的:

    SELECT COUNT(*) 
    FROM Table1 t1 
    WHERE NOT EXISTS 
         (
         SELECT 1 
         FROM Table2 t2 
         WHERE t2.id_user = 1 
           AND t2.Table1ID = t1.Table2ID 
         ) 
    

    和在一个循环中运行子查询内,每次使用索引查找。

    • SQL Server将使用MERGE ANTI JOIN

    内部子查询不会以通常的词义“执行”,相反,查询和子查询的结果将同时获取。

    有关详细说明,请参阅上面的链接。

    • Oracle将使用HASH ANTI JOIN

    内部子查询将被执行一次,并且将从结果集中构建一个哈希表。

    来自外部查询的值将在哈希表中查找。

    • PostgreSQL将使用NOT (HASHED SUBPLAN)

    很像Oracle

注意,重写查询,因为这:

SELECT (
     SELECT COUNT(*) 
     FROM Table1 
     ) - 
     (
     SELECT COUNT(*) 
     FROM Table2 t2 
     WHERE (t2.id_user, t2.Table1ID) IN 
       (
       SELECT 1, Table1ID 
       FROM Table1 
       ) 
     ) 

将大大提高这4个系统的性能。

0

是的,但只要查询处理器“找到”您正在查找的值就停止执行......因此,例如,如果外部select中的第一行的Table1Id = 32,并且Table2有记录用TABLEID = 32,然后 只要子查询发现表2中的行,其中TABLEID = 32,它停止...