CREATE TABLE Updates ( UpdateId INT NOT NULL IDENTITY(1,1) PRIMARY KEY,ObjectId INT NOT NULL )
基本上跟踪具有增加ID的对象的更新.
此表的使用者将选择100个不同对象ID的块,按UpdateId排序并从特定的UpdateId开始.基本上,跟踪它停止的位置,然后查询任何更新.
我发现这是一个有趣的优化问题,因为我只能通过编写因索引而碰巧做我想要的查询来生成最佳的查询计划,但不能保证我想要的:
SELECT DISTINCT TOP 100 ObjectId FROM Updates WHERE UpdateId > @fromUpdateId
其中@fromUpdateId是存储过程参数.
有一个计划:
SELECT <- TOP <- Hash match (flow distinct,100 rows touched) <- Index seek
由于正在使用的UpdateId索引上的搜索,结果已经很好并且从我想要的从最低到最高的更新ID排序.这会产生一个流量不同的计划,这就是我想要的.但顺序显然不是保证行为,所以我不想使用它.
这个技巧也会产生相同的查询计划(尽管有一个冗余的TOP):
WITH ids AS ( SELECT ObjectId FROM Updates WHERE UpdateId > @fromUpdateId ORDER BY UpdateId OFFSET 0 ROWS ) SELECT DISTINCT TOP 100 ObjectId FROM ids
虽然,我不确定(并且怀疑不是)这是否真的能保证订购.
我希望sql Server能够简化智能化的一个问题就是这样,但最终会产生一个非常糟糕的查询计划:
SELECT TOP 100 ObjectId FROM Updates WHERE UpdateId > @fromUpdateId GROUP BY ObjectId ORDER BY MIN(UpdateId)
有一个计划:
SELECT <- Top N Sort <- Hash Match aggregate (50,000+ rows touched) <- Index Seek
我正在尝试找到一种方法来生成一个最佳计划,在UpdateId上使用索引查找,并且使用不同的流来删除重复的ObjectId.有任何想法吗?
Sample data如果你想要它.对象很少会有一个以上的更新,并且在一组100行中几乎不会有多个,这就是为什么我在流量不同之后,除非有一些我不知道的更好的东西?但是,无法保证单个ObjectId在表中的行数不会超过100行.该表有超过1,000,000行,预计将迅速增长.
解决方法
Though,I’m not sure (and suspect not) if this truly guarantees ordering.
在许多情况下,您可能会观察到订单保留,但这是一个实施细节;没有保证,所以你不能依赖它.与往常一样,演示顺序只能由顶级ORDER BY子句保证.
例
下面的脚本显示Hash Match Flow Distinct不保留顺序.它设置了相关表格,并在两列中匹配数字1-50,000:
IF OBJECT_ID(N'dbo.Updates',N'U') IS NOT NULL DROP TABLE dbo.Updates; GO CREATE TABLE Updates ( UpdateId INT NOT NULL IDENTITY(1,1),ObjectId INT NOT NULL,CONSTRAINT PK_Updates_UpdateId PRIMARY KEY (UpdateId) ); GO INSERT dbo.Updates (ObjectId) SELECT TOP (50000) ObjectId = ROW_NUMBER() OVER ( ORDER BY C1.[object_id]) FROM sys.columns AS C1 CROSS JOIN sys.columns AS C2 ORDER BY ObjectId;
测试查询是:
DECLARE @Rows bigint = 50000; -- Optimized for 1 row,but will be 50,000 when executed SELECT DISTINCT TOP (@Rows) U.ObjectId FROM dbo.Updates AS U WHERE U.UpdateId > 0 OPTION (OPTIMIZE FOR (@Rows = 1));
输出肯定有序开始:
……但是进一步下降的价值开始“失踪”:
……最终:
在这种特殊情况下的解释是哈希运算符溢出:
分区溢出后,散列到同一分区的所有行也会溢出.稍后处理溢出的分区,从而打破了遇到的不同值将按接收顺序立即发出的期望.
有许多方法可以编写有效的查询来生成所需的有序结果,例如递归或使用游标.但是,使用Hash Match Flow Distinct无法完成.