3

これは、 Postgres での効率的な時系列クエリに対する @Erwin の回答からの質問です。

簡単にするために、その質問と同じテーブル構造を使用します

id | widget_id | for_date | score |

元の質問は、範囲内のすべての日付の各ウィジェットのスコアを取得することでした。日付にウィジェットのエントリがなかった場合は、そのウィジェットの前のエントリのスコアを表示します。クロス結合とウィンドウ関数を使用したソリューションは、クエリ対象の範囲にすべてのデータが含まれている場合にうまく機能しました。私の問題は、対象の日付範囲外であっても、以前のスコアが必要なことです。

サンプルデータ:

INSERT INTO score (id, widget_id, for_date, score) values
(1, 1337, '2012-04-07', 52),
(2, 2222, '2012-05-05', 99),
(3, 1337, '2012-05-07', 112),
(4, 2222, '2012-05-07', 101);

2012 年 5 月 5 日から 5 月 10 日までの範囲 (つまりgenerate_series('2012-05-05'::date, '2012-05-10'::date, '1d')) をクエリすると、次のようになります。

DAY          WIDGET_ID  SCORE
May, 05 2012    1337    52
May, 05 2012    2222    99
May, 06 2012    1337    52
May, 06 2012    2222    99
May, 07 2012    1337    112
May, 07 2012    2222    101
May, 08 2012    1337    112
May, 08 2012    2222    101
May, 09 2012    1337    112
May, 09 2012    2222    101
May, 10 2012    1337    112
May, 10 2012    2222    101

これまでの最良の解決策(これも@Erwinによる)は次のとおりです。

SELECT a.day, a.widget_id, s.score
FROM  (
   SELECT d.day, w.widget_id
         ,max(s.for_date) OVER (PARTITION BY w.widget_id ORDER BY d.day) AS effective_date
   FROM  (SELECT generate_series('2012-05-05'::date, '2012-05-10'::date, '1d')::date AS day) d
   CROSS  JOIN (SELECT DISTINCT widget_id FROM score) AS w
   LEFT   JOIN score s ON s.for_date = d.day AND s.widget_id = w.widget_id
   ) a
LEFT JOIN  score s ON s.for_date = a.effective_date AND s.widget_id = a.widget_id
ORDER BY a.day, a.widget_id;

しかし、このSQL Fiddleでわかるように、最初の 2 日間はウィジェット 1337 のスコアが null になります。その代わりに、行 1 の以前のスコア 52 を表示したいと思います。

これを効率的な方法で行うことは可能ですか?

4

3 に答える 3

1

@Romanが述べたように、これDISTINCT ONを解決できます。この関連する回答の詳細:

ただし、サブクエリは通常、CTE よりも少し高速です。

SELECT DISTINCT ON (d.day, w.widget_id)
       d.day, w.widget_id, s.score
FROM   generate_series('2012-05-05'::date, '2012-05-10'::date, '1d') d(day)
CROSS  JOIN (SELECT DISTINCT widget_id FROM score) AS w
LEFT   JOIN score s ON s.widget_id = w.widget_id AND s.for_date <= d.day
ORDER  BY d.day, w.widget_id, s.for_date DESC;

リスト内のテーブルのようなセットを返す関数を使用できますFROM

SQL フィドル

パフォーマンスの鍵となるのは、1 つの複数列インデックスです。

CREATE INDEX score_multi_idx ON score (widget_id, for_date, score)

3 番目の列は、Postgres 9.2 以降でカバリング インデックスにscoreするためにのみ含まれています。以前のバージョンには含めません。

もちろん、多数のウィジェットがあり、日付の範囲が広い場合、CROSS JOINは値札を持つ多数の行を生成します。実際に必要なウィジェットと日のみを選択してください。

于 2013-10-18T15:00:35.053 に答える
1

あなたが書いたように、一致するスコアを見つける必要がありますが、ギャップがある場合は、最も近い以前のスコアで埋めてください。SQL では次のようになります。

SELECT d.day, w.widget_id, 
  coalesce(s.score, (select s2.score from score s2
    where s2.for_date<d.day and s2.widget_id=w.widget_id order by s2.for_date desc limit 1)) as score
from (select distinct widget_id FROM score) AS w
cross join (SELECT generate_series('2012-05-05'::date, '2012-05-10'::date, '1d')::date AS day) d
left join score s ON (s.for_date = d.day AND s.widget_id = w.widget_id)
order by d.day, w.widget_id;

この場合の合体とは「隙間があれば」という意味です。

于 2013-10-18T06:25:21.017 に答える