SQL Server:IN 子查询的选择 vs 值性能

Posted

技术标签:

【中文标题】SQL Server:IN 子查询的选择 vs 值性能【英文标题】:SQL Server: select vs values performance for IN subquery 【发布时间】:2019-06-10 10:30:54 【问题描述】:

考虑 2 个需要返回所有列的查询:

查询 #1

select * 
from TA 
where id in (select id from TB where filterColumn = ?) 

查询 #2

select * 
from TA 
where id in (?, ?, ? ... n)

TA 包含数百万条记录,TB 上的子查询返回 1-2000 条记录(但通常只有几百条)。

比如说,这些查询的结果将是 10k 条记录。

对于第一个查询,将对所有记录进行 1 次聚集索引扫描。

对于第 2 次 - 10k 键查找。此外,如果参数超过 2.1k - 将会有多个查询(极少数情况)。

就一般性能和同时插入/更新/删除语句而言,首选哪个查询?

【问题讨论】:

查询说明了什么?什么跑得更快?查询计划显示了什么?这也可能在DBA 上问得更好。 对于大量的ID,无论是在文字列表中还是从子查询返回,那么子查询版本可能会运行得更快,因为它可以通过索引进行调整。 如果您有两匹马并且您想知道这两匹马中哪一匹更快,那么赛马 - 请参阅 Eric 的 Which is faster? Lippert 了解更多背景 Larnu 运行时间或多或少相同,但 cpu 时间(有值查询低 10 倍)和解析时间(有值查询高 x 倍)存在差异。我在这里寻求的是更少的等待时间,因为在查找时锁定更少。 TimBiegeleisen 索引并没有真正的帮助,因为我需要结果中的所有列。在这种情况下,SQL 服务器将选择使用扫描而不是索引。 【参考方案1】:

TB 上的子查询返回 1-2000 条记录

...

在一般性能和同时插入/更新/删除方面 语句,首选哪些查询

第一种方法,因为:

    解析数千甚至数百个文字可能会消耗查询优化器的额外资源。 如果 IN 值发生更改,查询计划将不会被重用,因此需要重新编译 Microsoft 在文档中对此发出警告:

显式包含大量值(许多 以逗号分隔的数千个值)在括号内,在 IN 子句会消耗资源并返回错误 8623 或 8632。 解决此问题,将 IN 列表中的项目存储在表中, 并在 IN 子句中使用 SELECT 子查询。

错误 8623:

查询处理器用尽了内部资源,无法 生成查询计划。这是一个罕见的事件,只预计 极其复杂的查询或引用非常大的查询 表或分区的数量。请简化查询。如果你 相信您错误地收到了此消息,请联系客户 支持服务了解更多信息。

错误 8632:

内部错误:已达到表达式服务限制。请 在查询中寻找可能复杂的表达式,并尝试 简化它们。

参考资料:

IN (Transact-SQL) "IN" clause limitation in Sql Server

【讨论】:

是的,我知道这些限制。但正如我所说,在 90% 的情况下,最多只有几百个参数。我准备用几(毫)秒来换取更少的阻塞。另外,我写道 1-2000 是过滤的结果,但表本身更大(几百万条记录)。你还觉得这样更好吗? @vlaku,根据您的 cmets,主要目标是避免在 TA 上(长时间运行)聚集索引扫描期间锁定 TB。如果我理解正确,也许 memory-optimized 表变量(或参数)会比“如果有超过 2.1k 个参数 - 会有多个查询(极少数情况)”更好。 "另一件事:在定义像create index ix1 on TB (filterColumn) INCLUDE(id)这样的覆盖索引后,TB上的键查找将消失。 TB 已经有索引,键查找仅在使用参数时在 TA 上发生(第二次查询)。扫描和阻止查询在 TA 上。事实上,我对这些查询没有太多控制权(因为我有多数据库目标应用程序并为它们使用 orm)。所以我需要知道我应该选择第一个还是第二个查询。

以上是关于SQL Server:IN 子查询的选择 vs 值性能的主要内容,如果未能解决你的问题,请参考以下文章

在SQL Server中为什么不建议使用Not In子查询

Sql server not in优化

in在sql中是啥意思

在SQL Server中为啥不建议使用Not In子查询

SQL Server:表值函数不适用于子查询

WHERE 子句中的 SQL 查询子选择优化 (SQL Server)