【问题标题】:MySQL group by number series [closed]MySQL按数字系列分组[关闭]
【发布时间】:2013-10-10 01:26:38
【问题描述】:

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

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

【问题讨论】:

  • 要求代码的问题必须表明对所解决问题的最低理解。包括尝试过的解决方案、它们为什么不起作用以及预期的结果。
  • 它需要几个 JOIN,但没有子查询 ;-)
  • SELECT a.a start , MIN(c.a) end , MIN(c.a) - a.a + 1 diff FROM my_table a LEFT JOIN my_table b ON a.a = b.a + 1 LEFT JOIN my_table c ON c.a >= a.a LEFT JOIN my_table d ON d.a = c.a+1 WHERE b.a IS NULL AND c.a IS NOT NULL AND d.a IS NULL GROUP BY a.a;
  • @Strawberry :这就是我要找的。​​span>

标签: mysql sql


【解决方案1】:

这是一个的问题,这里有另一种解决方法,同样使用变量:

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

可以在here 找到我借用@sgeddes 架构的该查询的SQL Fiddle 演示。


更新

由于不能使用数字变量(根据 cmets),您可以使用三角形自连接来分配行号,但它比使用变量效率低得多。无论如何,这是修改后的版本,对先前查询的更改以粗体突出显示:

选择
  MIN(a) AS series_start,
  MAX(a) AS series_end,
  MAX(a) - MIN(a) + 1 AS series_count
从 (
  选择
    数据.a,
    计数(*)作为 r
  从
    yourtable AS 数据
  内部联接
    yourtable AS 计数
  上
    data.id >= tally.id
  通过...分组
    数据.a
) 年代
通过...分组
  a - r
订购方式
  a - r
;

方法本身保持不变:子查询返回一个排序的行集,然后像以前一样处理。

修改后查询的 SQL Fiddle 演示可用here

【讨论】:

  • O.O 我不能使用上面的方法。我们公司正在使用 CGI,它将任何带有“@”星号的单词视为变量,并将其视为将连接到语句本身的普通字符串。如果没有初始值,则会将其保留为空白,从而导致错误。
  • @r 在这种情况下确实是一个变量,但我真的不明白你在说你的 CGI 如何处理以@ 开头的单词。毕竟,它是将其视为变量还是字符串?无论如何,如果只是 (SELECT @r := 0) 子查询是问题所在,您可以尝试删除它并将 @r := @r + 1 AS r 替换为 @r := IFNULL(@r, 0) + 1 AS r
  • 任何以“@”开头的单词都被视为一个变量(字符串类型)。我也不能使用 := 来分配值,它会导致错误。
  • @Tuyhakaw:啊,我现在明白了。请查看我的更新以获取替代版本。
【解决方案2】:

这是使用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

【讨论】:

  • 它工作正常,但我需要阅读更多内容才能完全理解这个用户定义的变量方法。感谢你的回答。 :)
  • @Tuyhakaw -- 不用担心,很高兴我能帮上忙!基本上,val 变量会跟踪顺序记录。按该变量分组,您可以使用聚合函数来实现您的结果。最好的问候。
  • 顺便说一句,我还有一个问题:简单的join 对表有什么作用?我知道leftright 和其他joins,但不是这个。
  • @Tuyhakaw -- 单独使用join 就像说inner join -- 它只是一个简写。这是一篇关于不同类型连接(内部与外部)的好文章:codinghorror.com/blog/2007/10/…
  • O.O 我不能使用上面的方法。我们公司正在使用 CGI,它将任何带有“@”星号的单词视为变量,并将其视为将连接到语句本身的普通字符串。如果没有初始值,它将留空导致错误。
猜你喜欢
  • 2012-09-03
  • 2013-03-26
  • 1970-01-01
  • 1970-01-01
  • 2021-01-21
  • 2012-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多