准备好this question的答案后,我发现无法验证我的答案。
在我的第一个编程工作中,我被告知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
谓词实际上如何工作?
答案 0 :(得分:17)
对于每行执行的子查询的警告是正确的 - 对于相关子查询。
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
列。 id_user
每行Table1
的值可能不同。因此子查询的结果可能会有所不同,具体取决于外部查询中的当前行。 RDBMS 必须多次执行子查询,对于外部查询中的每一行执行一次。
您测试的示例是非相关子查询。大多数现代RDBMS优化器都应该能够判断子查询的结果不何时依赖于外部查询的每一行中的值。在这种情况下,RDBMS一次运行子查询,缓存其结果,并将其重复用于外部查询中的谓词。
PS:在SQL中,IN()
被称为“谓词”,而不是语句。谓词是评估为true或false的语言的一部分,但不一定能作为语句独立执行。也就是说,您不能将其作为SQL查询运行:“2 IN(1,2,3);”虽然这是一个有效的谓词,但它不是一个有效的陈述。
答案 1 :(得分:8)
它完全取决于您正在使用的数据库以及确切的查询。
查询优化器有时非常聪明 - 在您的示例查询中,我希望更好的数据库能够使用与连接相同的技术。更天真的数据库可能只是多次执行相同的查询。
答案 2 :(得分:5)
这取决于相关的RDBMS
。
请参阅此处的详细分析:
简而言之:
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
)
)
将大大提高所有四个系统的性能。
答案 3 :(得分:4)
取决于优化器。检查每个特定查询的确切query plan,以查看RDBMS将如何实际执行该查询。
在甲骨文那里:
EXPLAIN PLAN FOR «your query»
在MySQL或PostgreSQL中
EXPLAIN «your query»
答案 4 :(得分:3)
现在大多数SQL引擎几乎都会为LEFT JOIN创建相同的执行计划,而不是IN和NOT EXISTS
我会说看看你的执行计划并找出: - )
此外,如果Table1Id列的值为NULL,则不会返回任何数据
答案 5 :(得分:0)
不是真的。但使用JOIN
编写此类查询真是太棒了答案 6 :(得分:0)
是的,但是一旦查询处理器“找到”您要查找的值,执行就会停止...例如,如果外部选择中的第一行具有Table1Id = 32,并且Table2具有记录一个TableId = 32,然后 一旦子查询在Table2中找到TableId = 32的行,它就会停止......