组合多个表时的SQL Server执行计划

时间:2014-01-21 03:24:38

标签: sql-server tsql sql-server-2012 sql-execution-plan

我有一个存储过程,它发出类似于下面的查询(伪tsql)。

多个ParentIds作为参数(csv)传入,解析并插入表变量@i。对于传入的每个ParentId,我们会查找StorageTable并将其包含在@i中。现在,根据StorageTable列的值,我们需要通过{{1}从适当的表格(Table1Table2Table3)中提取数据}。多个表中没有重复的可能性 - 因此ParentId

当我检查实际的执行计划时,我发现我的大部分成本/子树成本(超过一半)花费在UNION ALL上,甚至没有作为输入提供。

例如,如果我包含StorageTable,那么Table2的索引扫描会在执行计划中显示成本高。

正如我所料,StorageTable = 'Table1'没有显示针对Table2的任何读取,但根据实际执行计划,数据访问点看起来很昂贵。

在我看来,如果某个特定的StorageTable不存在,那么STATISTICS IO的内部联接将返回一个空结果集并“短路”任何其他工作,不是吗?

可能是什么解决方案?

@i

1 个答案:

答案 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) 

在编译时删除未执行的计划分支,而不是显示估计单次执行的成本。