MySQL按编号系列分组

时间:2013-10-10 01:26:39

标签: mysql sql

我刚刚开始学习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

这需要很多子查询和连接。我现在还想不出来。

2 个答案:

答案 0 :(得分:2)

这是一个问题,这是解决它的另一种方法,它也使用变量:

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列可能有差距,因此不能在这里使用。

主查询按ra之间的差异对结果进行分组:对于顺序值,它始终是相同的:

 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