Muh*_*han 2 mysql gaps-and-islands
我很难定义问题,也许你通过我的虚拟数据理解它。
我有这个数据:
PK, TaskPK
1, 1
2, 1
3, 2
4, 2
5, 5
6, 1
7, 1
8, 2
9, 2
10, 5
11, 5
Run Code Online (Sandbox Code Playgroud)
现在我必须数TaskPK
一下,我提出这个查询
Select PK, TaskPK, Count(*)
From tbl
Group by TaskPK
Run Code Online (Sandbox Code Playgroud)
带来了这样的结果
TaskPK, Count(*)
1, 4
2, 4
5, 3
Run Code Online (Sandbox Code Playgroud)
但我想要稍微不同的结果
像这样
TaskPK, Count(*)
1, 2
2, 2
5, 1
1, 2
2, 2
5, 2
Run Code Online (Sandbox Code Playgroud)
上面的结果基于连续的数据出现,因为TaskPK从1开始(它分组在一起),然后它改变它2(它分组在一起),然后5(它分组在一起)taskPK。但是当TaskPK再次变为1时,那么它应该单独分组,不与之前出现的1联系起来,这个任务单独计数。
这可能吗?
此类问题是通常所说的差距和孤岛问题的变体。您可以在序列中的间隙和岛屿的 SQL中看到很好的概述
这可以通过几个中间步骤来完成。首先,让我们创建一个(派生)表,我们将其存储为t1
,找出在哪些点开始一系列新的TaskPK
值。
CREATE TEMPORARY TABLE t1 AS
SELECT
PK, TaskPK,
case when
(SELECT TaskPK
FROM tbl t_prv
WHERE t_prv.PK < tbl.PK
ORDER BY PK DESC
LIMIT 1
) <=> TaskPK
then 0
else 1
end AS start_of_series
FROM
tbl ;
Run Code Online (Sandbox Code Playgroud)
t1
包含:
PK | 任务PK | 系列开始 -: | -----: | --------------: 1 | 1 | 1 2 | 1 | 0 3 | 2 | 1 4 | 2 | 0 5 | 5 | 1 6 | 1 | 1 7 | 1 | 0 8 | 2 | 1 9 | 2 | 0 10 | 10 5 | 1 11 | 11 5 | 0
注意:MySQL 中的 <=> 运算符与NOT DISTINCT FROM
标准 SQL 中的运算符等效。它允许与 NULL 值以及非 NULL 值进行相等比较。
现在我们知道每个系列从哪里开始,我们可以通过将列的所有当前值和以前的值加在一起(即累积和)来给出每个PK
a 。如果没有窗口函数,这必须通过(糟糕的、低效的)子查询来完成。series_number
start_of_series
我们将结果存储在表中t2
:
CREATE TEMPORARY TABLE t2 AS
SELECT
PK, TaskPK,
(SELECT
sum(start_of_series)
FROM
t1
WHERE
t1.PK <= tbl.PK
) AS series_number
FROM
tbl
;
Run Code Online (Sandbox Code Playgroud)
其内容为t2
:
PK | 任务PK | 系列号 -: | -----: | ------------: 1 | 1 | 1 2 | 1 | 1 3 | 2 | 2 4 | 2 | 2 5 | 5 | 3 6 | 1 | 4 7 | 1 | 4 8 | 2 | 5 9 | 2 | 5 10 | 10 5 | 6 11 | 11 5 | 6
此时,您要做的只是GROUP BY
Series_number,并取值TaskPK
(只需min(TaskPK)
完成这项工作),以及count(*)
:
SELECT
min(TaskPK) AS TaskPK, count(*) AS count
FROM
t2
GROUP BY
series_number
ORDER BY
series_number
Run Code Online (Sandbox Code Playgroud)
这是您真正想要的结果:
任务PK | 数数 -----: | ----: 1 | 2 2 | 2 5 | 1 1 | 2 2 | 2 5 | 2
通过替换t2
和t1
的定义,您可以将所有内容放在一个查询中:
SELECT
min(TaskPK) AS TaskPK, count(*) AS count
FROM
(SELECT
PK, TaskPK,
(SELECT
sum(start_of_series)
FROM
(SELECT
PK, TaskPK,
case when
(SELECT TaskPK FROM tbl t_prv WHERE t_prv.PK < tbl.PK ORDER BY PK DESC LIMIT 1) <=> TaskPK
then 0
else 1
end AS start_of_series
FROM
tbl
) AS t1
WHERE
t1.PK <= tbl.PK
) AS series_number
FROM
tbl
) AS t2
GROUP BY
series_number
ORDER BY
series_number
;
Run Code Online (Sandbox Code Playgroud)
任务PK | 数数 -----: | ----: 1 | 2 2 | 2 5 | 1 1 | 2 2 | 2 5 | 2
这个查询非常复杂(其中有五个 SELECT
),并且可能仍然可以简化一些。
您可以在dbfiddle找到完整的代码和数据
如果您可以使用MariaDB 的 WINDOW 函数或MySQL 8.0 窗口函数(目前尚未发布用于生产),则可以非常简化。
归档时间: |
|
查看次数: |
4683 次 |
最近记录: |