22

数年間(2003年から2008年)、データ(日付)が不均一に分散しています。PostgreSQL 8.3( http://www.postgresql.org/docs)でサポートされている間隔(日、週、月、四半期、年)のいずれかでデータをグループ化して、特定の開始日と終了日のセットのデータをクエリしたい/8.3/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC)。

問題は、次のように、一部のクエリで必要な期間にわたって継続的な結果が得られることです。

select to_char(date_trunc('month',date), 'YYYY-MM-DD'),count(distinct post_id) 
from some_table where category_id=1 and entity_id = 77  and entity2_id = 115 
and date <= '2008-12-06' and date >= '2007-12-01' group by 
date_trunc('month',date) order by date_trunc('month',date);
          to_char   | count 
        ------------+-------
         2007-12-01 |    64
         2008-01-01 |    31
         2008-02-01 |    14
         2008-03-01 |    21
         2008-04-01 |    28
         2008-05-01 |    44
         2008-06-01 |   100
         2008-07-01 |    72
         2008-08-01 |    91
         2008-09-01 |    92
         2008-10-01 |    79
         2008-11-01 |    65
        (12 rows)

ただし、次のように、データが存在しないため、一部の間隔が欠落しています。

select to_char(date_trunc('month',date), 'YYYY-MM-DD'),count(distinct post_id) 
from some_table where category_id=1 and entity_id = 75  and entity2_id = 115 
and date <= '2008-12-06' and date >= '2007-12-01' group by 
date_trunc('month',date) order by date_trunc('month',date);

        to_char   | count 
    ------------+-------

     2007-12-01 |     2
     2008-01-01 |     2
     2008-03-01 |     1
     2008-04-01 |     2
     2008-06-01 |     1
     2008-08-01 |     3
     2008-10-01 |     2
    (7 rows)

必要な結果セットは次のとおりです。

  to_char   | count 
------------+-------
 2007-12-01 |     2
 2008-01-01 |     2
 2008-02-01 |     0
 2008-03-01 |     1
 2008-04-01 |     2
 2008-05-01 |     0
 2008-06-01 |     1
 2008-07-01 |     0
 2008-08-01 |     3
 2008-09-01 |     0
 2008-10-01 |     2
 2008-11-01 |     0
(12 rows)

欠落しているエントリのカウントは0です。

Stack Overflowに関する以前の議論を見たことがありますが、私のグループ化期間は(日、週、月、四半期、年)のいずれかであり、アプリケーションによって実行時間を決定したため、問題は解決しないようです。したがって、カレンダーテーブルまたはシーケンステーブルとの左結合のようなアプローチは、私が推測するのに役立ちません。

これに対する私の現在の解決策は、カレンダーモジュールを使用してPython(Turbogearsアプリ内)でこれらのギャップを埋めることです。

これを行うためのより良い方法はありますか?

4

3 に答える 3

33

この質問は古いです。しかし、仲間のユーザーがそれを新しい複製のマスターとして選んだので、適切な回答を追加しています.

適切な解決策

SELECT *
FROM  (
   SELECT day::date
   FROM   generate_series(timestamp '2007-12-01'
                        , timestamp '2008-12-01'
                        , interval  '1 month') day
   ) d
LEFT   JOIN (
   SELECT date_trunc('month', date_col)::date AS day
        , count(*) AS some_count
   FROM   tbl
   WHERE  date_col >= date '2007-12-01'
   AND    date_col <= date '2008-12-06'
-- AND    ... more conditions
   GROUP  BY 1
   ) t USING (day)
ORDER  BY day;
  • LEFT JOINもちろん、使用します。

  • generate_series()タイムスタンプのテーブルをオンザフライで非常に高速に生成できます。

  • 一般に、参加する前に集計する方が高速です。私は最近、この関連する回答で sqlfiddle.com のテスト ケースを提供しました。

  • 基本フォーマットの( ) にキャストしtimestampます。より多くの使用のために。date::dateto_char()

  • GROUP BY 1最初の出力列を参照する構文の省略形です。それGROUP BY dayも可能ですが、同じ名前の既存の列と競合する可能性があります。またはGROUP BY date_trunc('month', date_col)::dateしかし、それは私の好みには長すぎます。

  • の利用可能な間隔引数で動作しdate_trunc()ます。

  • count()NULL(0行がない場合)は生成LEFT JOINされませんが、生成されます。外側の代わりに
    戻るには、 を使用します。マニュアル。0NULLSELECTCOALESCE(some_count, 0) AS some_count

  • より一般的な解決策または任意の時間間隔については、次の密接に関連する回答を検討してください。

于 2013-03-31T18:44:22.283 に答える
21

昨年のすべての最初の日のリストを作成できます(たとえば)

select distinct date_trunc('month', (current_date - offs)) as date 
from generate_series(0,365,28) as offs;
          date
------------------------
 2007-12-01 00:00:00+01
 2008-01-01 00:00:00+01
 2008-02-01 00:00:00+01
 2008-03-01 00:00:00+01
 2008-04-01 00:00:00+02
 2008-05-01 00:00:00+02
 2008-06-01 00:00:00+02
 2008-07-01 00:00:00+02
 2008-08-01 00:00:00+02
 2008-09-01 00:00:00+02
 2008-10-01 00:00:00+02
 2008-11-01 00:00:00+01
 2008-12-01 00:00:00+01

その後、そのシリーズに参加できます。

于 2008-12-06T11:30:40.420 に答える
0

実行時に一時テーブルを作成し、そのテーブルに結合したままにすることができます。それが最も理にかなっているようです。

于 2008-12-06T10:54:50.897 に答える