web-dev-qa-db-ja.com

日ごとにカウント/グループ化し、データなしで日を表示するPostgreSQLクエリ

を返すPostgreSQLクエリを作成する必要があります

  • 一日
  • その日に見つかったオブジェクトの数

その日にオブジェクトが見つからなかった場合でも、結果に1日ごとにが表示されることが重要です。 (これについては前に説明しましたが、特定のケースで物事を機能させることができませんでした。)

最初に、 日の範囲を生成するSQLクエリ を見つけました。これに参加できます。

SELECT to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD')
AS date 
FROM generate_series(0, 365, 1) 
AS offs

結果:

    date    
------------
 2013-03-28
 2013-03-27
 2013-03-26
 2013-03-25
 ...
 2012-03-28
(366 rows)

今、私はそれを「created」列を持つ「sharer_emailshare」という名前のテーブルに結合しようとしています:

Table 'public.sharer_emailshare'
column    |   type  
-------------------
id        | integer
created   | timestamp with time zone
message   | text
to        | character varying(75)

ここに私が今まで持っている最高のGROUP BYクエリがあります:

SELECT d.date, count(se.id) FROM (
    select to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD')
    AS date 
    FROM generate_series(0, 365, 1) 
    AS offs
    ) d 
JOIN sharer_emailshare se 
ON (d.date=to_char(date_trunc('day', se.created), 'YYYY-MM-DD'))  
GROUP BY d.date;

結果:

    date    | count 
------------+-------
 2013-03-27 |    11
 2013-03-24 |     2
 2013-02-14 |     2
(3 rows)

望ましい結果:

    date    | count 
------------+-------
 2013-03-28 |     0
 2013-03-27 |    11
 2013-03-26 |     0
 2013-03-25 |     0
 2013-03-24 |     2
 2013-03-23 |     0
 ...
 2012-03-28 |     0
(366 rows)

正しく理解している場合、これはプレーン(暗黙のINNERJOINを使用しているためであり、これは postgresのドキュメントで説明されている のように予期される動作です。

私は何十ものStackOverflowソリューションを調べてきましたが、クエリが機能するものはすべてMySQL/Oracle/MSSQLに固有のものであり、PostgreSQLに翻訳するのに苦労しています。

この質問 を求める男は、Postgresで答えを見つけましたが、しばらく前に期限切れになったPastebinリンクにそれを置きました。

LEFT OUTER JOINRIGHT JOINRIGHT OUTER JOINCROSS JOINに切り替えようとしましたが、CASEステートメントを使用して、null、COALESCEデフォルト値などを提供しますが、必要なものを取得する方法でそれらを使用することができませんでした。

どんな援助も大歓迎です!そして、私はすぐにその巨大なPostgreSQLの本を読み始めることを約束します;)

53
Marcel Chastain

必要なのはleft outer join内部結合の代わりに:

SELECT d.date, count(se.id)
FROM (SELECT to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD') AS date 
      FROM generate_series(0, 365, 1) AS offs
     ) d LEFT OUTER JOIN
     sharer_emailshare se 
     ON d.date = to_char(date_trunc('day', se.created), 'YYYY-MM-DD'))  
GROUP BY d.date;
47
Gordon Linoff

Gordon Linoffの有益な答えを拡張して、次のようないくつかの改善を提案します。

  • date_trunc('day', ...)の代わりに::dateを使用します
  • 文字型ではなく日付型に参加します(よりクリーンです)。
  • 後で変更しやすいように、特定の日付範囲を使用します。この場合、テーブルの最新のエントリの1年前を選択します。これは、他のクエリでは簡単に実行できなかったことです。
  • 任意のサブクエリの合計を計算します(CTEを使用)。目的の列を日付型にキャストし、date_columnと呼ぶだけです。
  • 累積合計の列を含めます。 (何故なの?)

私のクエリは次のとおりです。

WITH dates_table AS (
    SELECT created::date AS date_column FROM sharer_emailshare WHERE showroom_id=5
)
SELECT series_table.date, COUNT(dates_table.date_column), SUM(COUNT(dates_table.date_column)) OVER (ORDER BY series_table.date) FROM (
    SELECT (last_date - b.offs) AS date
        FROM (
            SELECT GENERATE_SERIES(0, last_date - first_date, 1) AS offs, last_date from (
                 SELECT MAX(date_column) AS last_date, (MAX(date_column) - '1 year'::interval)::date AS first_date FROM dates_table
            ) AS a
        ) AS b
) AS series_table
LEFT OUTER JOIN dates_table
    ON (series_table.date = dates_table.date_column)
GROUP BY series_table.date
ORDER BY series_table.date

クエリをテストし、同じ結果と累積合計の列を生成しました。

26
Travis

ゴードン・リノフの答えに基づいて、私は別の問題が元の質問で言及しなかったWHERE句があることであることに気付きました。

裸のWHEREの代わりに、サブクエリを作成しました。

SELECT d.date, count(se.id) FROM (
    select to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD')
    AS date 
    FROM generate_series(0, 365, 1) 
    AS offs
    ) d 
LEFT OUTER JOIN (
    SELECT * FROM sharer_emailshare 
    WHERE showroom_id=5
) se
ON (d.date=to_char(date_trunc('day', se.created), 'YYYY-MM-DD')) 
GROUP BY d.date;
7
Marcel Chastain