SQL 优化 - 执行计划根据约束值更改 - 为什么?

标签 sql sql-server query-optimization sql-execution-plan

我在以 2000 兼容模式运行的 SQL 2005 Server 上有一个表 ItemValue,其中充满了数据,看起来类似于(它是一个用户定义的值表):

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.

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)

因为我无法在 2000 模式下进行 PIVOT,所以我们不得不使用 CASE 和 GROUP BY 构建一个丑陋的查询来获取数据,以了解某些旧版应用程序的情况,即:

ItemNumber   Field1   Field2    Field3 .... Field51
----------   ------   -------   ------
    abc123   D        287.23    NULL
    xyz789   A        3782.23   23
    mno456   W        NULL      45

您可以看到我们只需要这个表来显示第 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

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

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

而且速度很快!我可以在一秒左右拉回10万+条记录,这符合我们的需求。

但是,如果我们有更多 UDF,并且我将约束更改为高于 66(是的,我一一测试了它们),或者如果我完全删除它,我会在执行中丢失排序计划,它被一大堆收集、重新分区和分发流的并行 block 取代,整个过程很慢(即使只有 1 条记录也需要 30 秒)。

FieldNumber 具有聚集的唯一索引,并且是 ItemFieldID 列(非聚集索引)的复合主键的一部分强>表。 ItemValue 表的 IDItemNumber 列构成 PK,并且 ItemNumber 上有一个额外的非聚集索引> 栏目。

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

如果您能胜任...您会采取什么不同的做法?计划在几个月后进行 SQL 升级,但我需要在此之前解决此问题。

最佳答案

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

您的f.FieldNumber <= 51被优化掉,优化器认为应该连接整个两个表(最好使用 HASH JOIN 来完成)。

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

能否请您发布查询的完整计划?只需运行 SET SHOWPLAN_TEXT ON然后是查询。

更新:

What is the reasoning behind this? Why does changing my simple integer constraint change the entire execution plan?

如果约束条件是指 WHERE条件,这可能是另一回事。

集合运算(这就是 SQL 所做的)没有单一最有效的算法:每种算法的效率在很大程度上取决于集合中的数据分布。

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

前一次操作的效率为m × const ,后者是n ,其中m是满足条件的记录条数,n是表中的记录总数,const > 1 .

这意味着对于较大的 m 值全扫描效率更高。

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

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

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

关于SQL 优化 - 执行计划根据约束值更改 - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2388566/

相关文章:

c# - 动态更改 SQL Server 数据库连接

sql - 需要有关SQL聚合查询的帮助

sql-server - SQL Server 查询优化 : why CPU Time is higher than Elapsed Time ? 它们与设置操作相关吗?

MySQL优化/性能,如何高效使用limit |基于位置的选择

MySQL 使用 WHERE 子句平均分布随机行

sql - 当前月份的当前周数

sql - 如何从数据 View 中忽略与 'Arithmetic Overflow' 相关的错误?

sql - 如何在 SQL Server 中正确分组结果

c# - 将 LocalDB 部署到 IIS7 的 ASP.net 网站

日志表的 mysql 表类型?