【问题标题】:SQL frequency distribution query to count ranges with group-by and include 0 countsSQL 频率分布查询使用 group-by 计算范围并包括 0 个计数
【发布时间】:2012-03-29 10:59:05
【问题描述】:

给定:

table 'thing':

age
---
3.4
3.4
10.1
40
45
49

我想计算每个 10 年范围内的事物数量,例如,

age_range | count
----------+-------
        0 |     2
        10|     1
        20|     0
        30|     0
        40|     3

这个查询很接近:

SELECT FLOOR(age / 10) as age_range, COUNT(*)
FROM thing
GROUP BY FLOOR(age / 10) ORDER BY FLOOR(age / 10);

输出:

 age_range | count 
-----------+-------
         0 |     1
         1 |     2
         4 |     3

但是,它没有向我显示计数为 0 的范围。如何修改查询,使其也显示介于 0 计数之间的范围?

我发现了关于计数范围的类似 stackoverflow 问题,其中一些计数为 0,但它们涉及必须指定每个范围(将范围硬编码到查询中,或者将范围放入表中)。我更喜欢使用像上面这样的通用查询,我不必明确指定每个范围(例如,0-10、10-20、20-30,...)。我正在使用 PostgreSQL 9.1.3。

有没有办法修改上面的简单查询以包含 0 个计数?

类似:
Oracle: how to "group by" over a range?
Get frequency distribution of a decimal range in MySQL

【问题讨论】:

    标签: sql postgresql group-by aggregate-functions


    【解决方案1】:

    generate_series 救援:

    select 10 * s.d, count(t.age)
    from generate_series(0, 10) s(d)
    left outer join thing t on s.d = floor(t.age / 10)
    group by s.d
    order by s.d
    

    通过单独的查询来计算generate_series 的上限应该很简单,我只是使用 10 作为占位符。

    这个:

    generate_series(0, 10) s(d)
    

    本质上生成一个名为s 的内联表,其中包含一列d,其中包含从0 到10(含)的值。

    如有必要,您可以将两个查询(一个用于计算范围,一个用于计算计数)包装到一个函数中。

    【讨论】:

    • 很好。要知道的好功能。
    • 优雅的答案,@mu_is_too_short!我试过了,它奏效了。正是我想要的。谢谢!
    • @Glenn:是的,generate_series 非常有用,它还可以处理时间戳,所以再见日历表。
    【解决方案2】:

    您需要一些方法来发明年龄范围表。行号通常效果很好。对一张大桌子做笛卡尔积以获得大量数字。

    WITH RANGES AS (
    SELECT (rownum - 1) * 10 AS age_range
      FROM ( SELECT row_number() OVER() as rownum
               FROM pg_tables
           ) n
          ,( SELECT ceil( max(age) / 10 )  range_end
               FROM thing
           ) m
      WHERE  n. rownum <= range_end
    )
    SELECT r.age_range, COUNT(t.age) AS count
      FROM ranges r
      LEFT JOIN thing t ON r.age_range = FLOOR(t.age / 10) * 10
      GROUP BY r.age_range
      ORDER BY r.age_range;
    

    编辑: mu is too short 有一个更优雅的答案,但如果你在 db 上没有 generate_series 函数,... :)

    【讨论】:

      猜你喜欢
      • 2011-09-06
      • 1970-01-01
      • 2016-09-03
      • 2021-04-09
      • 1970-01-01
      • 1970-01-01
      • 2018-10-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多