SQL优化 - 基于约束值的执行计划更改 - 为什么?

Cᴏʀ*_*ᴏʀʏ 6 sql sql-server query-optimization sql-execution-plan

我有一个表ItemValue,其中包含运行在2000兼容模式下的SQL 2005 Server上的数据,这些模式类似于(它是用户定义的值表):

ID    ItemCode     FieldID   Value
--    ----------   -------   ------
 1    abc123             1   D
 2    abc123             2   287.23
 4    xyz789             1   A
 5    xyz789             2   3782.23
 6    xyz789             3   23
 7    mno456             1   W
 9    mno456             3   45
                                 ... and so on.
Run Code Online (Sandbox Code Playgroud)

FieldID来自ItemField表:

ID   FieldNumber   DataFormatID   Description   ...
--   -----------   ------------   -----------
 1             1              1   Weight class
 2             2              4   Cost
 3             3              3   Another made up description
 .             .              x   xxx
 .             .              x   xxx
 .             .              x   xxx
 x             91  (we have 91 user-defined fields)
Run Code Online (Sandbox Code Playgroud)

因为我无法在2000模式下进行PIVOT,所以我们不得不使用CASE和GROUP BY来构建一个丑陋的查询,以使数据看起来应该如何应对一些遗留应用程序,即:

ItemNumber   Field1   Field2    Field3 .... Field51
----------   ------   -------   ------
    abc123   D        287.23    NULL
    xyz789   A        3782.23   23
    mno456   W        NULL      45
Run Code Online (Sandbox Code Playgroud)

您可以看到我们只需要此表来显示第51个UDF的值.这是查询:

SELECT
    iv.ItemNumber,
    ,MAX(CASE WHEN f.FieldNumber = 1 THEN iv.[Value] ELSE NULL END) [Field1]
    ,MAX(CASE WHEN f.FieldNumber = 2 THEN iv.[Value] ELSE NULL END) [Field2]
    ,MAX(CASE WHEN f.FieldNumber = 3 THEN iv.[Value] ELSE NULL END) [Field3]
        ...
    ,MAX(CASE WHEN f.FieldNumber = 51 THEN iv.[Value] ELSE NULL END) [Field51]
FROM ItemField f
LEFT JOIN ItemValue iv ON f.ID = iv.FieldID
WHERE f.FieldNumber <= 51
GROUP BY iv.ItemNumber
Run Code Online (Sandbox Code Playgroud)

FieldNumber约束<= 51时,执行计划类似于:

SELECT <== Computer Scalar <== Stream Aggregate <== Sort (Cost: 70%) <== Hash Match <== (Clustered Index Seek && Table Scan)

它很快!我可以在大约一秒钟内回收100,000多条记录,这符合我们的需求.

但是,如果我们有更多的UDF并且我将约束更改为高于66的任何值(是的,我逐个测试它们)或者如果我完全删除它,我会丢失执行计划中的排序,它会被一大堆替换掉收集,重新分配和分发流的Parallelism块,整个过程很慢(即使只有1个记录也是30秒).

FieldNumber具有集群的唯一索引,并且是ItemField表中具有ID列(非聚集索引)的复合主键的一部分.该项目值表的IDItemNumber列进行PK,并没有对额外的非聚集索引ItemNumber列.

这背后的原因是什么?为什么更改我的简单整数约束会改变整个执行计划?

如果你接受它...... 你会采取哪些不同的做法?从现在开始计划在几个月内进行SQL升级,但我需要在此之前修复此问题.

Qua*_*noi 5

SQL Server足够聪明,可以CHECK在优化查询时考虑约束。

\n\n

你的f.FieldNumber <= 51被优化了,优化器发现整个两个表应该被连接(最好用 a 来完成HASH JOIN)。

\n\n

如果没有约束,引擎需要检查条件,并且很可能使用索引遍历来执行此操作。这可能会更慢。

\n\n

能否请您发布整个计划以供查询?只需运行SET SHOWPLAN_TEXT ON然后查询即可。

\n\n

更新:

\n\n
\n

这背后的原因是什么?为什么改变我的简单整数约束会改变整个执行计划?

\n
\n\n

如果你所说的约束是指WHERE条件,那么这可能是另一回事。

\n\n

集合运算(确实如此SQL)没有单一最有效的算法:每个算法的效率在很大程度上取决于集合中的数据分布。

\n\n

比如说,对于获取子集(这就是子句的WHERE作用),您可以找到索引中记录的范围并使用索引记录指针来定位表中的数据行,或者只是扫描表中的所有记录并过滤它们使用WHERE条件。

\n\n

前者操作效率为m \xc3\x97 const,后者操作效率为n,其中m是满足条件的记录条数,n是表中的记录总数const > 1

\n\n

这意味着m全扫描的值越大,效率越高。

\n\n

SQL Server意识到这一点,并根据影响集合操作中数据分布的常量相应地更改执行计划。

\n\n

为此,需要SQL Server维护统计信息:每个索引列中数据分布的聚合直方图,并使用它们来构建查询计划。

\n\n

因此,更改条件中的整数WHERE实际上会影响基础集合的大小和数据分布,并导致SQL Server重新考虑最适合处理该大小和布局的集合的算法。

\n