我有一个存储过程,它发出类似于下面的查询(伪tsql)。
多个ParentIds
作为参数(csv)传入,解析并插入表变量@i
。对于传入的每个ParentId
,我们会查找StorageTable
并将其包含在@i
中。现在,根据StorageTable
列的值,我们需要通过{{1}从适当的表格(Table1
,Table2
或Table3
)中提取数据}。多个表中没有重复的可能性 - 因此ParentId
。
当我检查实际的执行计划时,我发现我的大部分成本/子树成本(超过一半)花费在UNION ALL
上,甚至没有作为输入提供。
例如,如果我包含StorageTable
,那么Table2的索引扫描会在执行计划中显示成本高。
正如我所料,StorageTable = 'Table1'
没有显示针对Table2的任何读取,但根据实际执行计划,数据访问点看起来很昂贵。
在我看来,如果某个特定的StorageTable不存在,那么STATISTICS IO
的内部联接将返回一个空结果集并“短路”任何其他工作,不是吗?
可能是什么解决方案?
@i
答案 0 :(得分:1)
在这种情况下,你应该忽略子树的成本。
即使在实际计划中,它们也只是基于估算。
根据您对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)
在编译时删除未执行的计划分支,而不是显示估计单次执行的成本。