SQL 当 IN 子句中的项目数超过 4 时,服务器查询非常慢
SQL Server query is very slow when number of items inside IN clause more than 4
我有一些复杂的查询,其中包含许多表的连接。由于复杂性,很难进行真正的查询。
有点像
select t1.id, t2.id, t1.name, t2.name
from table1 t1, table2 t2
left join table3 t3 ON t2.id = t3.id
where t2.id = t1.ref_id
and t1.ref_id IN ('id1', 'id2', 'id3', 'id4', 'id5', ...)
我发现,如果我在 IN 子句中只有 4 个或更少这样的值 t1.ref_id IN ('id1', 'id2', 'id3', 'id4') 它工作得非常快(16 毫秒)。如果我只添加一个 id 并使它像这样 t1.ref_id IN ('id1', 'id2', 'id3', 'id4', 'id5') 执行时间增加到40倍,变成600毫秒
我在 SQL Server 2014 上得到它。
似乎有一些参数可以控制此行为。我在另一个 SQL 服务器(SQL Server 2008)中尝试了这个查询,但我找不到任何限制。
我的问题:是否有控制这种行为的参数?或者如何将这个奇怪的限制增加到 50。
我只想将它增加到 30-50 而不是 4。当然我不想创建具有成百上千个值的 IN 子句。
更新 1
抱歉,忘记把t3.name改成select了,不然t3好像不需要了:
select t1.id, t2.id, t1.name, t2.name, t3.name
from table1 t1, table2 t2
left join table3 t3 ON t2.id = t3.id
where t2.id = t1.ref_id
and t1.ref_id IN ('id1', 'id2', 'id3', 'id4', 'id5', ...)
更新2
看来我找到原因了。问题不在于 IN 中的项目数量。后来我用少于 4 个 id 重现了这个问题(即使有 1 个)。它发生了,因为一些 ids 没有出现在 t1.ref_id 中。当存在 t1.ref_id 中不存在的 id 时,当它很快时,当我添加 id 时,t1.ref_id 中确实存在,当它变慢时。在我之前的示例中,id1 - id4 未出现在 t1.ref_id 中,而 id5 出现了。这就是为什么当我添加 id5 时它变慢了。即使我只在 IN 子句中放入 1 个 id (id5),它也会变慢。最后 t1.ref_id 上的索引解决了这个问题。围绕 4 或 5 个 id 没有魔法。我的具体例子纯属巧合
首先,修复查询。简单规则:从不 在 FROM
子句中使用逗号。
select t1.id, t2.id, t1.name, t2.name
from table1 t1 join
table2 t2
on t2.id = t1.ref_id left join
table3 t3
on t2.id = t3.id
where t1.ref_id in ('id1', 'id2', 'id3', 'id4', 'id5', ...);
根据查询,您不需要 table3
-- 除非您关心重复的行。我会删除它。
然后,你需要考虑索引。我会建议 table1(ref_id, id, name)
和 table2(id, name)
.
此外,如果 ref_id
确实是一个数字,则不要在列表中的值两边加上单引号。混合字符串和数字会使优化器感到困惑。
我有一些复杂的查询,其中包含许多表的连接。由于复杂性,很难进行真正的查询。
有点像
select t1.id, t2.id, t1.name, t2.name
from table1 t1, table2 t2
left join table3 t3 ON t2.id = t3.id
where t2.id = t1.ref_id
and t1.ref_id IN ('id1', 'id2', 'id3', 'id4', 'id5', ...)
我发现,如果我在 IN 子句中只有 4 个或更少这样的值 t1.ref_id IN ('id1', 'id2', 'id3', 'id4') 它工作得非常快(16 毫秒)。如果我只添加一个 id 并使它像这样 t1.ref_id IN ('id1', 'id2', 'id3', 'id4', 'id5') 执行时间增加到40倍,变成600毫秒
我在 SQL Server 2014 上得到它。
似乎有一些参数可以控制此行为。我在另一个 SQL 服务器(SQL Server 2008)中尝试了这个查询,但我找不到任何限制。
我的问题:是否有控制这种行为的参数?或者如何将这个奇怪的限制增加到 50。
我只想将它增加到 30-50 而不是 4。当然我不想创建具有成百上千个值的 IN 子句。
更新 1
抱歉,忘记把t3.name改成select了,不然t3好像不需要了:
select t1.id, t2.id, t1.name, t2.name, t3.name
from table1 t1, table2 t2
left join table3 t3 ON t2.id = t3.id
where t2.id = t1.ref_id
and t1.ref_id IN ('id1', 'id2', 'id3', 'id4', 'id5', ...)
更新2
看来我找到原因了。问题不在于 IN 中的项目数量。后来我用少于 4 个 id 重现了这个问题(即使有 1 个)。它发生了,因为一些 ids 没有出现在 t1.ref_id 中。当存在 t1.ref_id 中不存在的 id 时,当它很快时,当我添加 id 时,t1.ref_id 中确实存在,当它变慢时。在我之前的示例中,id1 - id4 未出现在 t1.ref_id 中,而 id5 出现了。这就是为什么当我添加 id5 时它变慢了。即使我只在 IN 子句中放入 1 个 id (id5),它也会变慢。最后 t1.ref_id 上的索引解决了这个问题。围绕 4 或 5 个 id 没有魔法。我的具体例子纯属巧合
首先,修复查询。简单规则:从不 在 FROM
子句中使用逗号。
select t1.id, t2.id, t1.name, t2.name
from table1 t1 join
table2 t2
on t2.id = t1.ref_id left join
table3 t3
on t2.id = t3.id
where t1.ref_id in ('id1', 'id2', 'id3', 'id4', 'id5', ...);
根据查询,您不需要 table3
-- 除非您关心重复的行。我会删除它。
然后,你需要考虑索引。我会建议 table1(ref_id, id, name)
和 table2(id, name)
.
此外,如果 ref_id
确实是一个数字,则不要在列表中的值两边加上单引号。混合字符串和数字会使优化器感到困惑。