【问题标题】:Grouping data into fuzzy gaps and islands将数据分组为模糊的间隙和孤岛
【发布时间】:2018-10-25 19:57:51
【问题描述】:

这本质上是一个差距和孤岛问题,但它是非典型的。我确实将示例缩减到最低限度。我需要识别超过某个阈值的间隙,并且重复不会成为问题,尽管此示例删除了它们。
在任何情况下,使用 ROW_NUMBER() 的常见解决方案都无济于事,因为甚至 1 的间隙都无法处理,并且间隙值是“现实生活”中的参数。

下面的代码实际上可以正常工作。而且速度超级快!但是,如果您看一下它,您就会明白为什么人们对依赖它相当害羞。该方法于 9 年前首次发布在这里http://www.sqlservercentral.com/articles/T-SQL/68467/,我已经阅读了所有 32 页的 cmets。除了说“它没有记录在案的行为”之外,没有人成功地戳破了它。从 2005 年到 2019 年,我在每个版本上都进行了尝试,并且可以正常工作。

问题是,除了使用游标或 while 循环逐一查看数百万行之外,我不知道需要多长时间,因为我会在 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 的值,直到检测到另一个更改。因此,在 6 秒后,我可以运行按 FuzzyGroup 分组的查询并分析这些岛屿。

在实践中,我也会在同一遍中进行一些运行计数和总计,因此需要 8 秒而不是 6 秒,但如果需要,我可以很容易地使用窗口函数完成这些事情,所以我将它们关闭。

这是最小的表,我最终需要处理 100M 行。因此 4.4M 的 10 分钟可能不够好,但它是一个开始的地方。

【问题讨论】:

    标签: sql-server tsql


    【解决方案1】:

    这应该是相当有效的,避免依赖无证行为

    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 
    

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

    【讨论】:

    • 42 秒。谢谢。
    • @bielawski 如果这解决了您的问题,请接受答案(在投票柜台下方查看)。说 thank you 的方式是赞成(如果有很多有用的贡献,则对所有有用的贡献)和接受最好的。编码快乐!
    • 我找不到按钮。我检查了[有用的]箭头!为什么网站需要用这么多完全没有意义的符号来点缀他们的网站,除非你漂浮在每一个符号上,直到你找到有用的东西。触摸屏没有任何浮动选项当然无济于事,因此如果不尝试就几乎不可能发现某些东西的作用,并且永远无法保证如果您遇到错误就可以撤消!我很抱歉继续运行 - 而且我没有立即找到并检查它。
    猜你喜欢
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 2021-03-07
    • 1970-01-01
    • 2022-09-27
    • 1970-01-01
    • 2019-04-14
    • 1970-01-01
    相关资源
    最近更新 更多