IN谓词在SQL中如何工作?

Gav*_*ler 20 sql optimization performance

在准备好这个问题的答案后,我发现我无法验证我的答案.

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

例如,给定查询:

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

它是否正确?IN谓词实际上如何工作?

Bil*_*win 18

关于每行执行的子查询的警告是真的 - 对于相关子查询.

SELECT COUNT(*) FROM Table1 a 
WHERE a.Table1id NOT IN (
  SELECT b.Table1Id FROM Table2 b WHERE b.id_user = a.id_user
);
Run Code Online (Sandbox Code Playgroud)

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

您测试的示例是非相关子查询.大多数现代RDBMS优化器都应该能够判断子查询的结果何时依赖于外部查询的每一行中的值.在这种情况下,RDBMS一次运行子查询,缓存其结果,并将其重复用于外部查询中的谓词.

PS:在SQL中,IN()被称为"谓词",而不是语句.谓词是评估为true或false的语言的一部分,但不一定能作为语句独立执行.也就是说,您不能将其作为SQL查询运行:"2 IN(1,2,3);" 虽然这是一个有效的谓词,但它不是一个有效的陈述.


Jon*_*eet 8

它完全取决于您正在使用的数据库以及确切的查询.

查询优化器有时非常聪明 - 在您的示例查询中,我希望更好的数据库能够使用与连接相同的技术.更天真的数据库可能只是多次执行相同的查询.


Qua*_*noi 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
            )
    
    Run Code Online (Sandbox Code Playgroud)

    并使用每次索引查找在循环中运行内部子查询.

    • 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
                )
        )
Run Code Online (Sandbox Code Playgroud)

将大大提高所有四个系统的性能.