Postgresql组月份缺少值

时间:2014-06-11 06:41:20

标签: sql postgresql group-by

首先是我的一个例子:

id_object;time;value;status
1;2014-05-22 09:30:00;1234;1
1;2014-05-22 09:31:00;2341;2
1;2014-05-22 09:32:00;1234;1
...
1;2014-06-01 00:00:00;4321;1
...

现在我需要计算所有行,其中status = 1,id_object = 1月份。这是我的疑问:

SELECT COUNT(*)
FROM my_table
WHERE id_object=1
  AND status=1
  AND extract(YEAR FROM time)=2014
GROUP BY extract(MONTH FROM time)

此示例的结果是:

2
1

2表示可能,1表示6月,但我需要一个所有12个月的输出,也是没有数据的月份。对于这个例子,我需要这个输出:

0 0 0 0 2 1 0 0 0 0 0 0

寻求帮助。

3 个答案:

答案 0 :(得分:5)

您可以使用generate_series()这样的功能:

select
    g.month,
    count(m)
from generate_series(1, 12) as g(month)
    left outer join my_table as m on
        m.id_object = 1 and
        m.status = 1 and
        extract(year from m.time) = 2014 and
        extract(month from m.time) = g.month
group by g.month
order by g.month

<强> sql fiddle demo

答案 1 :(得分:0)

您不想与提取的值进行比较,而是希望使用范围表。看起来像这样:

month  startOfMonth  nextMonth
1      '2014-01-01'  '2014-02-01'
2      '2014-02-01'  '2014-03-01'
......
12     '2014-12-01'  '2015-01-01'

正如@ Roman的回答一样,我们将以generate_series()开头,这次使用它来生成范围表:

WITH Month_Range AS (SELECT EXTRACT(MONTH FROM month) AS month, 
                            month AS startOfMonth,
                            month + INTERVAL '1 MONTH' AS nextMonth
                     FROM generate_series(CAST('2014-01-01' AS DATE),
                                          CAST('2014-12-01' AS DATE),
                                          INTERVAL '1 month') AS mr(month))
SELECT Month_Range.month, COUNT(My_Table) 
FROM Month_Range
LEFT JOIN My_Table
       ON My_Table.time >= Month_Range.startOfMonth
          AND My_Table.time < Month_Range.nextMonth
          AND my_table.id_object = 1
          AND my_table.status = 1
GROUP BY Month_Range.month
ORDER BY Month_Range.month

(作为旁注,我现在对PostgreSQL处理间隔的方式感到恼火)

<强> SQL Fiddle Demo

使用范围将允许使用包含My_Table.time的任何索引(尽管如果索引是在EXTRACT ed列上构建的,则不会。

编辑:

修改后的查询以利用generate_series(...)也将处理日期/时间序列的事实。

答案 2 :(得分:0)

generate_series可以生成时间戳系列

select
    g.month,
    count(t)
from
    generate_series(
        (select date_trunc('year', min(t.time)) from t),
        (select date_trunc('year', max(t.time)) + interval '11 months' from t),
        interval '1 month'
    ) as g(month)
    left outer join
    t on
        t.id_object = 1 and
        t.status = 1 and
        date_trunc('month', t.time) = g.month
where date_trunc('year', g.month) = '2014-01-01'::date
group by g.month
order by g.month