[英]SQL Server Execution plan when combining multiple tables
我有一个存储过程,发出与以下查询类似的查询(pseudo-tsql)。
多个ParentIds
作为参数(csv)传递,解析并插入到表变量@i
。 对于每个传入的ParentId
,我们查找StorageTable
并将其包含在@i
。 现在,根据StorageTable
列的值,我们需要通过ParentId
从适当的表( Table1
, Table2
或Table3
)中提取数据。 在多个表之间没有重复的机会-因此是UNION ALL
。
当我检查实际的执行计划时,我发现我的大部分成本/子树成本(一半以上)都花在了StorageTable
上,而这些甚至没有作为输入提供。
例如,如果我包括一个StorageTable = 'Table1'
,那么在执行计划中Table2的索引扫描将以很高的成本显示。
如我所料, STATISTICS IO
不会显示对Table2的任何读取,但是根据实际的执行计划,数据访问点看起来很昂贵。
在我看来,如果不存在特定的StorageTable,则使用@i
进行内部@i
将返回空结果集,并使任何其他工作“短路”,不是吗?
有什么解决方案?
DECLARE @i AS TABLE
(
ParentId INT,
StorageTable VARCHAR(10)
)
INSERT INTO @i...
INSERT INTO @i...
INSERT INTO @i...
SELECT Col1, Col2, Col3
FROM dbo.Table1 AS T1
INNER JOIN (SELECT * FROM @i WHERE StorageTable = 'Table1') AS I
ON T1.ParentId = I.ParentId
<joins>
<where clause>
UNION ALL
SELECT Col1, Col2, Col3
FROM dbo.Table2 AS T2
INNER JOIN (SELECT * FROM @i WHERE StorageTable = 'Table2') AS I
ON T2.ParentId = I.ParentId
<joins>
<where clause>
UNION ALL
SELECT Col1, Col2, Col3
FROM dbo.Table3 AS T3
INNER JOIN (SELECT * FROM @i WHERE StorageTable = 'Table3') AS I
ON T3.ParentId = I.ParentId
<joins>
<where clause>
在这种情况下,您应该几乎忽略子树成本。
即使在实际计划中,它们也仅基于估计。
根据您所说的STATISTICS IO
输出,例如访问Table2
的操作员的实际执行次数为0
。
但是,该计划可能会估计执行次数= 1。
(选择一个运算符后,您可以在SSMS的属性窗口中查看估计和实际数字)
如果该计划的一些分支具有执行数的低估 ,你可以尝试使用#temp
代替列统计都考虑到表。
通过添加一些辅助变量和OPTION (RECOMPILE)
您可以得到更具代表性的子树成本,但是它们仍然仅与建模假设和估计一样准确。
例如
DECLARE @T TABLE(
X INT,
StorageTable VARCHAR(50));
INSERT INTO @T
VALUES (1, 'Table1')
DECLARE @Branch1Exists BIT = iif(EXISTS(SELECT * FROM @T WHERE StorageTable = 'Table1'), 1, 0)
DECLARE @Branch2Exists BIT = iif(EXISTS(SELECT * FROM @T WHERE StorageTable = 'Table2'), 1, 0)
SELECT X
FROM @T
JOIN master..spt_values V
ON [@T].X = number
WHERE @Branch1Exists = 1
UNION ALL
SELECT X
FROM @T
JOIN sys.objects
ON [@T].X = object_id
WHERE @Branch2Exists = 1
OPTION (recompile)
在编译时删除未执行的计划分支,而不是显示估计的单个执行成本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.