SQL:检测具有相同键的连续行的连续块
Posted
技术标签:
【中文标题】SQL:检测具有相同键的连续行的连续块【英文标题】:SQL: detecting consecutive blocks of sequential rows with same key 【发布时间】:2021-03-14 15:55:03 【问题描述】:我的问题归结为以下几点。我有一个带有一些自然顺序的表,其中我有一个可能会随着时间重复的键值。我想找到键相同的块,然后更改,然后恢复原样。示例:
-
一个
一个
B
B
B
C
C
一个
一个
C
C
这里我想要的结果是
-
A,1-2
乙,3-5
C,6-7
A, 8-9
C,10-11
所以我不能用那个键值A、B、C来分组,因为同一个键可以出现多次,我只是想挤出不间断的重复出现。
不用说,我想要最简单的 SQL。它将使用 OLAP 窗口函数。
我通常擅长处理复杂的 SQL,但处理序列我就不太擅长了。当然,我会自己做一些工作,并在后续编辑中在此问题下方附上一些想法。
让我们从定义表格开始讨论:
CREATE TABLE Seq (
num integer,
key char
);
更新 1:做一些研究,我在这里发现了一个类似的问题:How to find consecutive rows based on the value of a column?,但问题和答案都包含在很多额外的东西中并且令人困惑。
更新 2:我已经得到了一个答案,谢谢。现在检查它。这是我在说话时正在输入 PostgreSQL 的测试:
CREATE TABLE Seq ( num int, key char );
INSERT INTO Seq VALUES
(1, 'A'), (2, 'A'),
(2, 'B'), (3, 'B'), (5, 'B'),
(6, 'C'), (7, 'C'),
(8, 'A'), (9, 'A'),
(10, 'C'), (11, 'C');
更新 3:解决方案的第一个竞争者是这个
SELECT key, min(num), max(num)
FROM (
SELECT seq.*,
row_number() over (partition by key order by num) as seqnum
FROM Seq
) s
GROUP BY key, (num - seqnum)
ORDER BY min;
产量:
key | min | max
-----+-----+-----
A | 1 | 2
B | 2 | 3
B | 5 | 5
C | 6 | 7
A | 8 | 9
C | 10 | 11
(6 rows)
由于某种原因,B 重复了两次,我明白为什么,我在测试数据中犯了一个“错误”,跳过序列号 4 并直接从 3 到 5。
这个错误是幸运的,因为它让我指出,虽然在这个例子中序列号是离散的,但我打算让序列来自某个连续域(例如时间)。
我犯了另一个“错误”,我重复了 num 2。这是允许的吗?可能不是。所以清理示例,删除重复但留下空白:
DROP TABLE Seq;
CREATE TABLE Seq ( num int, key char );
INSERT INTO Seq VALUES
(1, 'A'), (2, 'A'),
(3, 'B'), (4, 'B'), (6, 'B'),
(7, 'C'), (8, 'C'),
(9, 'A'), (10, 'A'),
(11, 'C'), (12, 'C');
这仍然给我们留下了重复的 B 块:
key | min | max
-----+-----+-----
A | 1 | 2
B | 3 | 4
B | 6 | 6
C | 7 | 8
A | 9 | 10
C | 11 | 12
(6 rows)
现在按照 Gordon Linoff 的第一个直觉,尝试理解它并加以补充:
SELECT s.*, num - seqnum AS diff
FROM (
SELECT seq.*,
row_number() over (partition by key order by num) as seqnum
FROM Seq
) s
ORDER BY num;
这是分组前的 num - seqnum 技巧:
num | key | seqnum | diff
-----+-----+--------+------
1 | A | 1 | 0
2 | A | 2 | 0
3 | B | 1 | 2
4 | B | 2 | 2
6 | B | 3 | 3
7 | C | 1 | 6
8 | C | 2 | 6
9 | A | 3 | 6
10 | A | 4 | 6
11 | C | 3 | 8
12 | C | 4 | 8
(11 rows)
我怀疑这还不是答案。
【问题讨论】:
【参考方案1】:由于 Gordon 的解决方案建议,您不能直接使用 num
。 Row_number
也是。
select key, min(num), max(num)
from (select seq.*,
row_number() over (order by num) as rn,
row_number() over (partition by key order by num) as seqnum
from seq
) s
group by key, (rn - seqnum)
order by min(num);
【讨论】:
【参考方案2】:这回答了原来的问题。
您可以枚举每个键的行并将其从num
中减去。瞧!当key
在相邻行上为常数时,此数字为常数:
select key, min(num), max(num)
from (select seq.*,
row_number() over (partition by key order by num) as seqnum
from seq
) s
group by key, (num - seqnum);
Here 是一个 dbfiddle,表明它可以工作。
【讨论】:
不完全,我在上面显示你的查询结果。 @GuntherSchadow 。 . .您更改了数据——以及问题的定义。最初的问题暗示(非常强烈地)数字必须相差 1 才能在同一组中,并且是无间隙和连续的。你完全改变了问题;问一个新的问题比让答案无效要好。以上是关于SQL:检测具有相同键的连续行的连续块的主要内容,如果未能解决你的问题,请参考以下文章