SQL 计算相同数字的块的长度,同时忽略变化数字的块
SQL count the length of blocks of identical numbers, while ignoring blocks of changing numbers
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 之前保持不变,我只想对块进行计数。
我想要的结果是:
产品
块长度
产品一
1
产品一
4
产品 b
2
解释:
对于产品a,我们将第一个“3”算作长度1,因为它从0到3又回到0。
我们接下来计算产品 a 的块“7”连续出现 4 次,从 0 开始切换。
我们跳过计算产品 a 的最后一个块,因为它从 1 到 3 切换回 1。
我们得到的长度是 1 和 4。
对于产品 b,我们跳过第一个块,因为它在结束变回 0 之前从 2 变为 3。
产品b的第二个block算作12出现两次不变,长度为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);
您可以将其视为间隙和孤岛问题。我认为最简单的方法是使用 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 是一个 db<>fiddle.
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 之前保持不变,我只想对块进行计数。
我想要的结果是:
产品 | 块长度 |
---|---|
产品一 | 1 |
产品一 | 4 |
产品 b | 2 |
解释:
对于产品a,我们将第一个“3”算作长度1,因为它从0到3又回到0。
我们接下来计算产品 a 的块“7”连续出现 4 次,从 0 开始切换。
我们跳过计算产品 a 的最后一个块,因为它从 1 到 3 切换回 1。
我们得到的长度是 1 和 4。
对于产品 b,我们跳过第一个块,因为它在结束变回 0 之前从 2 变为 3。
产品b的第二个block算作12出现两次不变,长度为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);
您可以将其视为间隙和孤岛问题。我认为最简单的方法是使用 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 是一个 db<>fiddle.