我刚刚开始学习MySQL,我遇到了这个问题,我急需解决方案或只是逻辑。
例如我有这张表:
id a
-- --
1 1
2 2
3 3
4 5
5 6
6 7
7 9
8 10
9 11
10 12
现在,我想要的是显示列a
中的所有数据,这些数据应按系列分组。在这种情况下,结果应为:
series_start|series_end|count
------------+----------+-----
1 3 3
5 7 3
9 12 4
这需要很多子查询和连接。我现在还想不出来。
答案 0 :(得分:2)
这是一个gaps-and-islands问题,这是解决它的另一种方法,它也使用变量:
SELECT
MIN(a) AS series_start,
MAX(a) AS series_end,
MAX(a) - MIN(a) + 1 AS series_count
FROM (
SELECT
a,
@r := @r + 1 AS r
FROM
yourtable,
(SELECT @r := 0) AS x
ORDER BY
a
) s
GROUP BY
a - r
ORDER BY
a - r
;
这是它的工作原理。
子查询将行号分配给表行并返回此行集:
a r
-- --
1 1
2 2
3 3
5 4
6 5
7 6
9 7
10 8
11 9
12 10
在这种情况下,存储行号的r
列恰好与数据样本中的id
列匹配,但我假设通常是id
列可能有差距,因此不能在这里使用。
主查询按r
和a
之间的差异对结果进行分组:对于顺序值,它始终是相同的:
a r a - r
-- -- -----
1 1 0
2 2 0
3 3 0
5 4 1
6 5 1
7 6 1
9 7 2
10 8 2
11 9 2
12 10 2
这允许我们将这些行组合在一起。此时剩下的就是获得最小值,最大值和计数值,从而得到这个输出:
series_start series_end series_count
------------ ---------- ------------
1 3 3
5 7 3
9 12 4
这个查询的SQL小提示演示,我借用了@ sgeddes的模式,可以找到here。
<强>更新强>
由于无法使用数字变量(根据注释),您可以使用三角形自连接分配行号,但效率远低于使用变量。无论如何,这是修改后的版本,对上一个查询的更改以粗体突出显示:
SELECT MIN(a) AS series_start, MAX(a) AS series_end, MAX(a) - MIN(a) + 1 AS series_count FROM ( SELECT data.a, COUNT(*) AS r FROM yourtable AS data INNER JOIN yourtable AS tally ON data.id >= tally.id GROUP BY data.a ) s GROUP BY a - r ORDER BY a - r ;
方法本身保持不变:子查询返回排名的行集,然后与之前的处理相同。
修改后的查询的SQL小提琴演示可用here。
答案 1 :(得分:0)
以下是使用user defined variables
的一个解决方案:
select min(series_start) series_start,
max(series_end) series_end,
1 + max(series_end) - min(series_start) count
from (
select t1.a series_start,
t2.a series_end,
@val:=IF(@prev=t2.a-1,@val,@val+1) val,
@prev:=t2.a
from yourtable t1
join yourtable t2 on t1.a = t2.a-1
join (select @val:= 0, @prev:= 0) t
order by t2.a
) t
group by val