这本质上是一个鸿沟和孤岛的问题,但这是非典型的。我确实将示例缩减到最低限度。我需要确定超过一定阈值的差距,尽管此示例将其删除,但重复不是问题。
在任何情况下,使用ROW_NUMBER()的通用解决方案都无济于事,因为无法处理偶数为1的间隙,并且间隙值是“现实生活”中的参数。
以下代码实际上可以正常工作。而且超级快!但是,如果您看一下它,就会明白为什么人们对依靠它持相当害羞的态度。该方法最早于9年前在这里http://www.sqlservercentral.com/articles/T-SQL/68467/上发布,我已经阅读了全部32页的注释。除了说“没有记录在案的行为”外,没有人成功戳破它。我已经在2005年至2019年的每个版本上试用了它,并且可以正常工作。
问题是,除了使用游标或while循环以1到1的方式查看数百万行之外,这不知道要花多长时间,因为30分钟后我取消了。 -是否有“受支持的”方法在合理的时间内获得相同的结果?甚至慢100倍,也可以在10分钟内完成4M行,而我找不到找到这种方法的方法!
CREATE TABLE #t (CreateDate date not null
,TufpID int not null
,Cnt int not null
,FuzzyGroup int null);
ALTER TABLE #t ADD CONSTRAINT PK_temp PRIMARY KEY CLUSTERED (CreateDate,TufpID);
-- Takes 40 seconds to write 4.4M rows from a source of 70M rows.
INSERT INTO #T
SELECT X.CreateDate
,X.TufpID
,Cnt = COUNT(*)
,FuzzyGroup = null
FROM SessionState SS
CROSS APPLY(VALUES (CAST(SS.CreateDate as date),SS.TestUser_Form_Part_id)) X(CreateDate,TufpID)
GROUP BY X.CreateDate
,X.TufpID
ORDER BY x.CreateDate,x.TufpID;
-- Takes 6 seconds to update 4.4M rows. They WILL update in clustered index order!
-- (Provided all the rules are followed - see the link above)
DECLARE @FuzzFactor int = 38
DECLARE @Prior int = -@FuzzFactor; -- Insure 1st row has it's own group
DECLARE @Group int;
DECLARE @CDate date;
UPDATE #T
SET @Group = FuzzyGroup = CASE WHEN t.TufpID - @PRIOR < @FuzzFactor AND t.CreateDate = @CDate
THEN @Group ELSE t.TufpID END
,@CDate = CASE WHEN @CDate = t.CreateDate THEN @CDate ELSE t.CreateDate END
,@Prior = CASE WHEN @Prior = t.TufpID-1 THEN @Prior + 1 ELSE t.TufpID END
FROM #t t WITH (TABLOCKX) OPTION(MAXDOP 1);
上面的执行后,FuzzyGroup列包含该组中最低的TufpID值。 IOW第一行(按聚集索引顺序)包含其自己的TufpID列的值。此后,每一行都会获得相同的值,直到日期更改或间隔大小(在这种情况下为38)超过为止。在这些情况下,当前的TufpID会变成放置在FuzzyGroup中的值,直到检测到另一个更改为止。因此,六秒钟后,我可以运行按FuzzyGroup分组的查询并分析孤岛。
实际上,我在同一遍中也进行了一些运行计数和总计,因此花费了8秒钟而不是6秒钟,但是如果需要的话,我可以很容易地使用窗口函数来完成这些操作,所以我将它们省略了。
这是最小的表,我最终将需要处理100M行。因此,4.4M的10分钟可能不够好,但这是一个开始的地方。
答案 0 :(得分:4)
这应该相当有效,并且避免依赖未记录的行为
WITH T1
AS (SELECT *,
PrevTufpID = LAG(TufpID)
OVER (PARTITION BY CreateDate
ORDER BY TufpID)
FROM #T),
T2
AS (SELECT *,
_FuzzyGroup = MAX(CASE
WHEN PrevTufpID IS NULL
OR TufpID - PrevTufpID >= @FuzzFactor
THEN TufpID
END)
OVER (PARTITION BY CreateDate
ORDER BY TufpID ROWS UNBOUNDED PRECEDING)
FROM T1)
UPDATE T2
SET FuzzyGroup = _FuzzyGroup
执行计划对聚簇索引进行一次有序扫描,然后使行值流经某些窗口函数运算符并进入更新。