根据从下一行开始的当前值对行进行分组

Posted

技术标签:

【中文标题】根据从下一行开始的当前值对行进行分组【英文标题】:Group rows based on the current value starting from the next row 【发布时间】:2019-01-25 09:39:24 【问题描述】:

我需要使用value <> z 增加所有下一行的组号。 z 意味着根据b 的所有下一行将具有相同的组号。

CREATE TABLE #tmp
(
    a CHAR(1)
  , b INT
);

INSERT INTO #tmp (a
                , b)
VALUES ('a', 1)
     , ('b', 2)
     , ('z', 3)
     , ('c', 4)
     , ('z', 5)
     , ('z', 6)
     , ('d', 7);

SELECT       t.a
           , t.b
           , SUM(v.is_z) OVER (ORDER BY t.b ROWS UNBOUNDED PRECEDING) - ROW_NUMBER() OVER (ORDER BY t.b) group_nbr
  FROM       #tmp                                                 AS t
 CROSS APPLY (SELECT CASE WHEN a = 'z' THEN 2 ELSE 1 END AS is_z) AS v
 ORDER BY 2;

DROP TABLE #tmp;

在我的查询中,组从值为z 的行开始递增,但我需要为下一行开始递增。

预期输出:

【问题讨论】:

'下一步'根据什么(b)? 是的。修改了问题。 【参考方案1】:

一种解决方案是每次找到z 时加1,然后LAG 最后将结果加1。

;WITH CumulativeZ AS
(
    SELECT
        T.*,
        CumulativeZ = SUM(CASE WHEN T.a = 'z' THEN 1 ELSE 0 END) OVER(ORDER BY T.b ASC)
    FROM
        #tmp AS T
)
SELECT
    C.a,
    C.b,
    C.CumulativeZ,
    [Group] = LAG(C.CumulativeZ, 1, 0) OVER (ORDER BY C.b ASC)
FROM
    CumulativeZ AS C

结果:

a   b   CumulativeZ Group
a   1   0           0
b   2   0           0
z   3   1           0
c   4   1           1
z   5   2           1
z   6   3           2
d   7   3           3

请注意,LAG 函数适用于 SQL Server 2012+。您可以在 2008+ 版本上使用 ROW_NUMBER 模仿它的功能。

【讨论】:

【参考方案2】:

您希望“z”s 标识组的结尾。您可以使用累积和分配组编号。我认为这是做你想做的最简单的方法。

如果您不关心数字的实际顺序,那么您可以这样做:

select t.*,
       sum(case when a = 'z' then 1 else 0 end) over (order by b desc) as grp_desc 
from #tmp t;

如果您希望组以“正确的方式”编号,您可以使用稍微复杂一点的表达式:

select t.*,
       coalesce(sum(case when a = 'z' then 1 else 0 end) over
                    (order by b
                     rows between unbounded preceding and 1 preceding), 0
               ) as grp
from #tmp t;

Here 是一个 dbfiddle。

【讨论】:

估计的计划比接受的答案差大约两倍 @DmitrijKultasev 。 . .如果您的数据实际上有 7 行,那么没关系。对于任何规模很大的数据,交叉应用可能会变得很昂贵。 好的,你说服了我 :) 刚刚检查了真实数据(约 32M 行),它的性能好于所有提议的解决方案数倍。【参考方案3】:

我会简单地使用apply

select t.a, t.b, tt.grp
from #tmp t outer apply
     (select count(*) as grp
      from #tmp t1 
      where t1.b < t.b and t1.a = 'z'
     ) tt;

【讨论】:

@DmitrijKultasev 。 . .这是一种非常低效的方式来做你想做的事。使用窗口函数通常要快得多,它们可以使用。

以上是关于根据从下一行开始的当前值对行进行分组的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Python 在一个时间段内对行进行分组

按值对行进行分组,直到它更改(分组包括第一个更改的值)

使用 ag 网格,尝试按一个值对行分组并显示另一个

根据列值对行进行排名/计数

根据 SQL Server 2008 R2 中特定列中的模式更改对行进行分组

如何动态生成表并对行进行分组