13

を使用して1か月あたりのIDをカウントしたいgenerate_series()。このクエリはPostgreSQL9.1で機能します。

SELECT (to_char(serie,'yyyy-mm')) AS year, sum(amount)::int AS eintraege FROM (
    SELECT  
       COUNT(mytable.id) as amount,   
       generate_series::date as serie   
       FROM mytable  
    
    RIGHT JOIN generate_series(     
       (SELECT min(date_from) FROM mytable)::date,   
       (SELECT max(date_from) FROM mytable)::date,  
       interval '1 day') ON generate_series = date(date_from)  
       WHERE version = 1   
       GROUP BY generate_series       
       ) AS foo
GROUP BY Year   
ORDER BY Year ASC;  

これは私の出力です:

"2006-12" | 4  
"2007-02" | 1  
"2007-03" | 1  

しかし、私が取得したいのは、この出力(1月の「0」値)です。

"2006-12" | 4  
"2007-01" | 0  
"2007-02" | 1  
"2007-03" | 1  

それにもかかわらず、ない月idはリストされるべきです。
これを解決する方法はありますか?

サンプルデータ:

drop table if exists mytable;
create table mytable(id bigint, version smallint, date_from timestamp);
insert into mytable(id, version, date_from) values
(4084036, 1, '2006-12-22 22:46:35'),
(4084938, 1, '2006-12-23 16:19:13'),
(4084938, 2, '2006-12-23 16:20:23'),
(4084939, 1, '2006-12-23 16:29:14'),
(4084954, 1, '2006-12-23 16:28:28'),
(4250653, 1, '2007-02-12 21:58:53'),
(4250657, 1, '2007-03-12 21:58:53')
;
4

1 に答える 1

30

もつれを解き、単純化し、修正すると、次のようになります。

SELECT to_char(s.tag,'yyyy-mm') AS monat
     , count(t.id) AS eintraege
FROM  (
   SELECT generate_series(min(date_from)::date
                        , max(date_from)::date
                        , interval '1 day'
          )::date AS tag
   FROM   mytable t
   ) s
LEFT   JOIN mytable t ON t.date_from::date = s.tag AND t.version = 1   
GROUP  BY 1
ORDER  BY 1;

ここでdb<>フィドル

すべてのノイズ、誤解を招く識別子、および型破りな形式の中で、実際の問題はここに隠されていました。

WHERE version = 1

を正しく使用しRIGHT [OUTER] JOINました。WHEREただし、から既存の行を必要とする句を追加すると、が効果的にmytable変換されます。RIGHT [OUTER] JOIN[INNER] JOIN

そのフィルターをJOIN条件に移動して、機能させます。

私はそれにいる間、他のいくつかのことを単純化しました。

より良い、まだ

SELECT to_char(mon, 'yyyy-mm') AS monat
     , COALESCE(t.ct, 0) AS eintraege
FROM  (
   SELECT date_trunc('month', date_from)::date AS mon
        , count(*) AS ct
   FROM   mytable
   WHERE  version = 1     
   GROUP  BY 1
   ) t
RIGHT JOIN (
   SELECT generate_series(date_trunc('month', min(date_from))
                        , max(date_from)
                        , interval '1 mon')::date
   FROM   mytable
   ) m(mon) USING (mon)
ORDER  BY mon;

ここでdb<>フィドル

最初に集計して後で参加する方がはるかに安価です。1日に1行ではなく、1か月に1行に参加します。

レンダリングされたの代わりに、値にGROUP BY基づいORDER BYて作成する方が安価です。datetext

count(*)このクエリcount(id)では同等ですが、はより少し高速です。

generate_series()timestampの代わりに基づいた場合、は少し速く安全ですdate。見る:

于 2012-11-16T10:31:23.363 に答える