4

我对以下查询的性能有疑问:

DECLARE @detail_level INT = 1,
        @DETAIL_1     INT = 1,
        @DETAIL_2     INT = 2,
        @DETAIL_3     INT = 4

SELECT mtbl.*,
       CASE
         WHEN @detail_level & @DETAIL_1 <> 0 THEN tbl_1.value
         ELSE NULL
       END,
       CASE
         WHEN @detail_level & @DETAIL_2 <> 0 THEN tbl_2.value
         ELSE NULL
       END,
       CASE
         WHEN @detail_level & @DETAIL_3 <> 0 THEN tbl_3.value
         ELSE NULL
       END
FROM   mtbl
       LEFT OUTER JOIN tbl_1
         ON @detail_level & @DETAIL_1 <> 0
            AND mtbl.KEY = tbl_1.KEY
       LEFT OUTER JOIN tbl_2
         ON @detail_level & @DETAIL_2 <> 0
            AND mtbl.KEY = tbl_2.KEY
       LEFT OUTER JOIN tbl_3
         ON @detail_level & @DETAIL_3 <> 0
            AND mtbl.KEY = tbl_3.KEY
WHERE  mtbl.KEY = @something 

查询引擎是否会通过使用详细级别过滤器来优化查询,以避免对这些详细表列进行不必要的表连接和不必要的表行访问?

如果是,查询引擎甚至不会为那些不必要的表获取索引锁吗?

我在 SQL Server 2008 中捕获了查询计划,即使细节级别不匹配,索引搜索操作仍然存在。但是细节较少的查询比具有完整细节(数据量约 500,000)的查询快(约​​ 30%)。

4

2 回答 2

4

您需要检查执行计划以确定。当我创建以下示例表时

CREATE TABLE tbl_1([KEY] INT PRIMARY KEY, value INT)
CREATE TABLE tbl_2([KEY] INT PRIMARY KEY, value INT)
CREATE TABLE tbl_3([KEY] INT PRIMARY KEY, value INT)
CREATE TABLE mtbl([KEY] INT PRIMARY KEY, value INT)

INSERT INTO mtbl VALUES(1,1),(2,2) 
INSERT INTO tbl_1 VALUES(1,1),(2,2)
INSERT INTO tbl_2 VALUES(1,1),(2,2)
INSERT INTO tbl_3 VALUES(1,1),(2,2)

您的查询给出了执行计划

计划

虽然这似乎显示了针对所有四个成本相同的表的操作,但这有点误导。实际上只有其中两个被访问。(即使在实际执行计划中显示的成本也只是估计成本。)

运行后SET STATISTICS IO ON;,您的示例查询返回

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'tbl_1'. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'mtbl'. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

如您所见,结果中既没有tbl_2也没有tbl_3特征。查看索引 seek 的属性tbl_3表明该运算符从未被执行(执行次数 = 0)

寻找

这是因为外连接位于带有启动谓词的过滤器下方,并且仅在其评估结果为 true 时调用。

筛选

关于您关于锁定的问题,没有对对象进行行或页面锁定, tbl_2或者tbl_3仍然IS对对象进行锁定。这可以通过运行看到

DBCC TRACEON(1200,3604,-1)
/*Your query*/ 
DBCC TRACEOFF(1200,3604,-1)

IS锁与模式修改、批量更新或对象上的独占锁不兼容。

于 2012-09-09T17:17:20.370 回答
1

SQL Server 将创建一个适用于任何参数值的查询计划。但它会针对它遇到的参数的第一个版本进行优化。这称为“参数嗅探”。

option (recompile)您可以使用查询提示强制重新编译。重新编译会增加 20-30ms 的查询时间,但查询计划将针对特定的参数值进行优化。

另一种选择是使用动态 SQL 生成只连接所需表的查询。这会导致多个缓存的执行计划。这表现最好,但代价是难以维护。

作为旁注,左连接表的列设置为null当没有行匹配on条件时。所以你可以缩短:

case when @detail_level & @DETAIL_1 <> 0 then tbl_1.value else null end

至:

tbl_1.value
于 2012-09-09T14:54:29.857 回答