WHERE IN 使用 Table 类型比使用 SQL 服务器中的硬编码值执行得更好
WHERE IN performs much better with Table Type than with hardcoded values in SQL Server
我有 2 个基本相同的查询(至少如果我没有遗漏任何内容)。
DECLARE @siloIds SiloIdsTableType
INSERT INTO @siloIds VALUES
(1),(2),(3)
-- Query 1
SELECT *
FROM [Transaction]
WHERE
SiloId IN (1,2,3)
AND Time > '2000-02-01'
-- Query 2
SELECT *
FROM [Transaction]
WHERE
SiloId IN (select SiloId from @siloIds)
AND Time > '2000-02-01'
我在想一个人不能击败查询本身声明的常量,但显然第一个查询比第二个查询慢几倍。似乎 SQL 服务器不够智能,无法为硬编码值提供良好的计划,或者我在这里遗漏了什么?
长长的列表似乎不应该使用where in,TVP应该总是受到青睐
P.S。我在查询中使用千值而不是 1,2,3
P.P.S。我在 SiloId ASC、Time ASC 上有一个非聚集索引,但由于某种原因,第一个查询似乎没有使用它来支持聚集索引扫描。
P.P.P.S。执行计划分担成本 14% 到 86% 有利于第二个查询
执行计划:
当您使用 table 变量(或 TVP,两者是一回事)时,SQL 服务器使用固定估计,它只会从中获取 1 行(更多信息下面这个),基数为 1。这意味着它假设 SiloId
连接过滤器非常有选择性,它将优先考虑它并进行嵌套循环连接以获取那些行,然后在 Time
上过滤.
而当您使用常量时,确切的大小是硬编码的。无论出于何种原因(可能是错误的统计数据),它都假定 Time
更具选择性,因此优先于其他过滤器。
table 变量计划失败的地方是其中有很多行,或者在主 table 中,因为那样你会得到很多键查找,这可以慢一点。
理想情况下,您希望编译器预先知道 table 变量的大小。您可以通过多种方式执行此操作,如 Brent Ozar explains:
- 跟踪标志 2453,如果基数非常不同,这将导致重新编译(如果您可以冒 TF 的风险,这是个好主意)
OPTION (RECOMPILE)
(这个每次都要重新编译,本身可能效率不高)
- 一个临时的table(不可能作为参数)
我有 2 个基本相同的查询(至少如果我没有遗漏任何内容)。
DECLARE @siloIds SiloIdsTableType
INSERT INTO @siloIds VALUES
(1),(2),(3)
-- Query 1
SELECT *
FROM [Transaction]
WHERE
SiloId IN (1,2,3)
AND Time > '2000-02-01'
-- Query 2
SELECT *
FROM [Transaction]
WHERE
SiloId IN (select SiloId from @siloIds)
AND Time > '2000-02-01'
我在想一个人不能击败查询本身声明的常量,但显然第一个查询比第二个查询慢几倍。似乎 SQL 服务器不够智能,无法为硬编码值提供良好的计划,或者我在这里遗漏了什么?
长长的列表似乎不应该使用where in,TVP应该总是受到青睐
P.S。我在查询中使用千值而不是 1,2,3
P.P.S。我在 SiloId ASC、Time ASC 上有一个非聚集索引,但由于某种原因,第一个查询似乎没有使用它来支持聚集索引扫描。
P.P.P.S。执行计划分担成本 14% 到 86% 有利于第二个查询
执行计划:
当您使用 table 变量(或 TVP,两者是一回事)时,SQL 服务器使用固定估计,它只会从中获取 1 行(更多信息下面这个),基数为 1。这意味着它假设 SiloId
连接过滤器非常有选择性,它将优先考虑它并进行嵌套循环连接以获取那些行,然后在 Time
上过滤.
而当您使用常量时,确切的大小是硬编码的。无论出于何种原因(可能是错误的统计数据),它都假定 Time
更具选择性,因此优先于其他过滤器。
table 变量计划失败的地方是其中有很多行,或者在主 table 中,因为那样你会得到很多键查找,这可以慢一点。
理想情况下,您希望编译器预先知道 table 变量的大小。您可以通过多种方式执行此操作,如 Brent Ozar explains:
- 跟踪标志 2453,如果基数非常不同,这将导致重新编译(如果您可以冒 TF 的风险,这是个好主意)
OPTION (RECOMPILE)
(这个每次都要重新编译,本身可能效率不高)- 一个临时的table(不可能作为参数)