【问题标题】:SQL Server query is very slow when number of items inside IN clause more than 4当 IN 子句中的项目数超过 4 时,SQL Server 查询非常慢
【发布时间】:2017-05-14 17:44:00
【问题描述】:

我有一些复杂的查询,其中包含许多表的连接。由于复杂,很难进行真正的查询。

有点像

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 并将其设置为 5 像这样 t1.ref_id IN ('id1', 'id2', 'id3', 'id4', 'id5') 执行时间增加到 40 倍并变为 600 ms。

我在 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 个)重现了这个问题。发生这种情况是因为某些 id 未在 t1.ref_id 中显示。当有 t1.ref_id 中不存在的 id 时,它很快,当我添加 t1.ref_id 中确实存在的 id 时,它变慢了。在我之前的示例中,t1.ref_id 中没有显示 id1 - id4 并且显示了 id5。这就是为什么当我添加 id5 时它会变慢。即使我只在 IN 子句中放入 1 个 id (id5),它也会变得很慢。最后索引 t1.ref_id 解决了这个问题。 4 或 5 个 id 周围没有魔法。在我的具体例子中这只是一个巧合。

【问题讨论】:

  • 您是否在 t1.ref_id 上创建了索引?
  • 您在缩小差异方面做得很好。现在比较慢版本和快版本之间的查询计划。你看到了什么。按 CTRL-L 查看查询计划。
  • 你不应该混合隐式和显式连接语法,你应该重写为显式语法。它是 1992 年以来的 ANSI 标准!此查询是存储过程的一部分吗?加OPTION(RECOMPILE)有帮助吗?
  • @Nick.McDermaid 是的,谢谢。我现在就在做。
  • @Zlelik 我明白你的意思,你现在可以停止对我开枪了

标签: sql sql-server sqlperformance in-clause


【解决方案1】:

首先,修复查询。简单规则:永远不要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 真的是一个数字,那么不要在列表中的值周围加上单引号。混合字符串和数字会混淆优化器。

【讨论】:

  • 感谢您的回答。但是我无法将逗号分隔表的语法更改为 JOIN,因为我使用了一些产品,不允许我直接更改 SQL。 Table3 我需要,我忘了从中选择数据。我在 UPDATE1 中更改了我的查询。 ref_id 是一个字符串。 id 是一种 GUID,其中包含数字和字母,例如 097777778018f16b。如果它总是很慢,我会理解的索引。但为什么 4 个 id 快而 5 个 id 慢?我还有另一个查询,它看起来相似,但从不同的表中获取数据,它有同样的问题。 4个值如果没问题,5个很慢。
  • 使用旧连接语法生成查询的产品?扔掉它,现在
  • @GuidoG 它被称为“DELL EMC Documentum”,他们提供 DQL 语言(Documentum 查询语言),由 Documentum Content Server 自动转换为 SQL。我正在编写这个 DQL 查询。有 LEFT 连接语法,但没有 JOIN 语法。我尝试使用 LEFT JOIN 模拟 JOIN,例如 LEFT JOIN table2 t2 on t2.id = t1.ref_id ... where t2.id != '' 并且它可以工作。但它仍然存在与 IN 相同的问题。无论如何,用 join 重写这个查询将无济于事。它只是为漂亮的语法而战:)
  • @Gordon Linoff 实际上你是对的。发生这种情况是因为某些 id 未在 t1.ref_id 中显示。当有 t1.ref_id 中不存在的 id 时,它很快,当我添加 t1.ref_id 中确实存在的 id 时,它变慢了。您对索引table1(ref_id, id, name) 的建议解决了这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-06
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
  • 2022-10-13
  • 1970-01-01
相关资源
最近更新 更多