将数据分组为模糊的间隙和孤岛

时间:2018-10-25 19:57:52

标签: sql-server tsql

这本质上是一个鸿沟和孤岛的问题,但这是非典型的。我确实将示例缩减到最低限度。我需要确定超过一定阈值的差距,尽管此示例将其删除,但重复不是问题。
在任何情况下,使用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分钟可能不够好,但这是一个开始的地方。

1 个答案:

答案 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 

执行计划对聚簇索引进行一次有序扫描,然后使行值流经某些窗口函数运算符并进入更新。

enter image description here