【发布时间】:2014-02-10 11:32:16
【问题描述】:
我有一个存储过程,它发出类似于下面的查询(伪 tsql)。
多个ParentIds 作为参数(csv) 传入、解析并插入到表变量@i 中。对于传入的每个ParentId,我们查找StorageTable 并将其包含在@i 中。现在,根据StorageTable 列的值,我们需要通过ParentId 从相应的表(Table1、Table2 或Table3)中提取数据。多个表之间没有重复的机会 - 因此UNION ALL。
当我检查实际的执行计划时,我发现我的大部分成本/子树成本(超过一半)都花在了 StorageTable 上,甚至没有作为输入提供。
例如,如果我包含一个StorageTable = 'Table1',Table2 的索引扫描将在执行计划中出现高成本。
正如我所料,STATISTICS IO 没有显示对 Table2 的任何读取,但根据实际执行计划,数据访问点看起来很昂贵。
在我看来,如果特定的 StorageTable 不存在,与 @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>
【问题讨论】:
-
您的联接也可以写成:
INNER JOIN @i AS I ON T1.ParentId = I.ParentId AND I.StorageTable = 'Table1',因此子查询不是必需的。你能添加一个执行计划吗? -
执行计划中显示的成本在这种情况下是不可靠的。即使在实际计划中,它们也只是估计成本,因此不一定反映实际运行时成本。
-
请发布(实际)计划。它可以有任意数量的形状。
-
表变量没有统计信息。此处的统计信息与选择正确的连接运算符高度相关。即使您使用带有统计信息的临时表,统计信息也可能会过时。
-
@usr - 大概以
@i作为外部表的嵌套循环STATISTICS IO没有显示针对Table2的任何读取
标签: sql-server tsql sql-server-2012 sql-execution-plan