【问题标题】:SQL count the length of blocks of identical numbers, while ignoring blocks of changing numbersSQL计算相同数字块的长度,而忽略变化数字块
【发布时间】:2021-09-25 09:04:16
【问题描述】:
CREATE TABLE identicalblocks
(
    [sortid] int, 
    [product] varchar(57), 
    [changing] int
)
;

INSERT INTO identicalblocks ([sortid], [product], [changing])
VALUES
(1, 'product a', 0),
(2, 'product a', 3),
(3, 'product a', 0),
(4, 'product a', 7),
(5, 'product a', 7),
(6, 'product a', 7),
(7, 'product a', 7),
(8, 'product a', 0),
(9, 'product a', 0),
(10, 'product a', 1),
(11, 'product a', 3),
(12, 'product a', 1),
(13, 'product a', 0),
(14, 'product a', 0),
(1, 'product b', 0),
(2, 'product b', 2),
(3, 'product b', 2),
(4, 'product b', 2),
(5, 'product b', 3),
(6, 'product b', 0),
(7, 'product b', 0),
(8, 'product b', 12),
(9, 'product b', 12),
(10, 'product b', 0),
(11, 'product b', 0),
(12, 'product b', 0);

我在这里建了一个sqlfiddle:
http://sqlfiddle.com/#!18/555ace

我要实现的逻辑是,当“变化”列从 0 切换到某个正整数时,我想开始计数。如果在任何时候整数发生变化,我想放弃计数。如果整数保持不变直到块结束并回到0,我只想计算块。

我想要的结果是:

Product blocklengths
product a 1
product a 4
product b 2

解释:

对于产品 a,我们将第一个“3”计为长度 1,因为它从 0 切换到 3 又回到 0。

接下来我们将产品 a 的块“7”计数为连续出现 4 次,从 0 开始切换。

我们跳过计算产品 a 的最后一个块,因为它从 1 切换到 3 又回到 1。

我们得到的长度是 1 和 4。

对于产品 b,我们跳过第一个块,因为它从 2 变为 3,然后又回到 0。

产品b的第二块算作12出现两次不变,长度为2。

【问题讨论】:

    标签: sql sql-server window-functions


    【解决方案1】:

    您可以将此视为间隙和孤岛问题。我认为最简单的方法是通过使用0 值的累积和来分配“岛屿”。然后聚合过滤:

    select product, sum(case when changing <> 0 then 1 else 0 end)
    from (select ib.*,
                 sum(case when changing = 0 then 1 else 0 end) over (partition by product order by sortid) as grp
          from identicalblocks ib
         ) ib
    group by product, grp
    having count(distinct changing) = 2;  -- 0 and the other value
    

    Here 是一个 dbfiddle。

    【讨论】:

      【解决方案2】:

      这是一个缝隙和岛屿问题的例子。

      有很多解决方案,这里是一个。

      • 我们使用LAG来获取每个岛的起点,在这种情况下:之前的值为0
      • 我们使用运行中的COUNT 来获取每个岛的分组编号(请记住COUNT 忽略空值)
      • 然后我们只需 GROUP BY 分组编号并计算我们有多少行,并排除所有具有不同编号的组
      WITH WithGroupStarts AS (
          SELECT *,
              IsStartOfGroup = CASE WHEN LAG(changing, 1, 0) OVER (PARTITION BY ib.product
                                       ORDER BY ib.sortid) = 0 THEN 1 END
          FROM identicalblocks ib
      ),
      WithGroups AS (
          SELECT *,
              GroupId = COUNT(ib.IsStartOfGroup) OVER (PARTITION BY ib.product
                        ORDER BY ib.sortid ROWS UNBOUNDED PRECEDING)
          FROM WithGroupStarts ib
          WHERE ib.changing <> 0
      )
      SELECT
          ib.Product,
          blocklengths = COUNT(*)
      FROM WithGroups ib
      GROUP BY
        ib.Product,
        ib.GroupId
      HAVING MIN(ib.changing) = MAX(ib.changing);
      

      SQL Fiddle

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-19
        • 2016-05-03
        • 1970-01-01
        • 1970-01-01
        • 2021-10-13
        • 1970-01-01
        相关资源
        最近更新 更多