我在以 2000 兼容模式运行的 SQL 2005 服务器上有一个充满数据的表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)
而且速度很快!我可以在大约一秒钟内拉回 100,000+ 条记录,这符合我们的需求。
但是,如果我们有更多的 UDF,并且我将约束更改为66以上的任何值(是的,我对它们进行了逐个测试),或者如果我完全删除它,我会丢失执行计划中的排序,它会被一大堆替换并行块收集、重新分区和分发流,整个过程很慢(即使只有 1 条记录也需要 30 秒)。
FieldNumber有一个聚集的唯一索引,并且是ItemField表中具有ID列(非聚集索引)的复合主键的一部分。ItemValue表的ID和ItemNumber列构成一个 PK, ItemNumber列上有一个额外的非聚集索引。
这背后的原因是什么?为什么更改我的简单整数约束会更改整个执行计划?
如果你能做到……你会做些什么不同的事情?从现在开始有几个月的 SQL 升级计划,但我需要在此之前解决这个问题。