【问题标题】:SQL optimization - execution plan changes based on constraint value - Why?SQL 优化 - 基于约束值更改执行计划 - 为什么?
【发布时间】:2011-01-24 05:14:54
【问题描述】:

我在以 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 约束为

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

而且速度很快!我可以在大约一秒钟内拉回 100,000+ 条记录,这符合我们的需求。

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

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

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

如果你能做到……你会采取什么不同的做法?计划在几个月后升级 SQL,但我需要在此之前解决这个问题。

【问题讨论】:

  • 我会做不同的事情是不使用该表结构。对于 99% 的数据需求,使用具有已定义字段的结构比使用 EAV 表要好得多。顺便说一句,兼容模式不会阻止您使用新功能,它是允许使用不再被允许的功能。但是,如果您在 2005 数据库和 2000 生产数据库上进行开发,最好避免使用新功能。

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


【解决方案1】:

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

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

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

能否发布查询的全部计划?只需运行SET SHOWPLAN_TEXT ON,然后运行查询。

更新:

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

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

集合操作(​​SQL 所做的)没有单一最有效的算法:每个算法的效率很大程度上取决于集合中的数据分布。

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

前者的效率是m × const,后者的效率是n,其中m是满足条件的记录数,n是表中的总记录数,@987654334 @。

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

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

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

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

【讨论】:

  • 我将发布计划,但我必须进行一些更改,以便我的真实表格不会提供任何识别信息(我的示例已被篡改)。
  • @Remus:当我读到它时,我相信 当 FieldNumber 约束是 &lt;= 51 是关于 CHECK 约束,但你可能是对的,@ op much 意味着WHERE 条件。
  • 我接受这个答案作为您的描述和回答我的问题,尽管我会考虑其他一些答案,因为它们也提供了一些见解。谢谢!
【解决方案2】:

它被一大堆并行块取代

试试这个:

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
OPTION (Maxdop 1)

通过使用 Option(Maxdop 1),这应该可以防止执行计划中的并行性。

【讨论】:

  • 所以这确实有效,但是您不能根据需要将 OPTION 放在视图中。
【解决方案3】:

在 66 岁时,您正在达到某个内部成本估算阈值,该阈值决定使用一个计划而不是另一个计划。该阈值是什么以及为什么会发生并不重要。请注意,您的查询因每个 FieldNumber 值而异,因为您不仅更改了 WHERE:还更改了伪“透视”投影字段。

现在我不知道您的表和查询以及插入/更新/删除/模式的所有详细信息,但是对于您发布的 ItemValue 表的正确聚集索引结构的特定查询是这样的:

CREATE CLUSTERED INDEX  [cdxItemValue] ON ItemValue (FieldID, ItemNumber);

这种结构消除了对这个“透视”查询的结果进行中间排序的需要。

【讨论】:

    猜你喜欢
    • 2019-10-20
    • 2019-06-21
    • 2016-03-05
    • 2017-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-02
    • 1970-01-01
    相关资源
    最近更新 更多