10

PG db に次のようなテーブルがあります。

id | widget_id | for_date | score |

参照される各ウィジェットには、これらのアイテムが多数あります。ウィジェットごとに常に 1 日 1 つですが、ギャップがあります。

私が取得したいのは、X 以降の各日付のすべてのウィジェットを含む結果です。日付は、生成シリーズを介して取り込まれます。

 SELECT date.date::date
   FROM generate_series('2012-01-01'::timestamp with time zone,'now'::text::date::timestamp with time zone, '1 day') date(date)
 ORDER BY date.date DESC;

指定された widget_id の日付のエントリがない場合は、前のものを使用したいと考えています。したがって、ウィジェット 1337 には 2012-05-10 のエントリがなく、2012-05-08 にはエントリがない場合、結果セットに 2012-05-10 の 2012-05-08 エントリも表示する必要があります。

Actual data:
widget_id | for_date   | score
1312      | 2012-05-07 | 20
1337      | 2012-05-07 | 12
1337      | 2012-05-08 | 41
1337      | 2012-05-11 | 500

Desired output based on generate series:
widget_id | for_date   | score
1336      | 2012-05-07 | 20
1337      | 2012-05-07 | 12
1336      | 2012-05-08 | 20
1337      | 2012-05-08 | 41
1336      | 2012-05-09 | 20
1337      | 2012-05-09 | 41
1336      | 2012-05-10 | 20
1337      | 2012-05-10 | 41
1336      | 2012-05-11 | 20
1337      | 2012-05-11 | 500

最終的には、これをビューに要約して、簡単にクエリできる 1 日あたりの一貫したデータ セットを取得したいと考えています。

編集:サンプルデータと予想される結果セットをより明確にしました

4

4 に答える 4

8

SQL フィドル

select
    widget_id,
    for_date,
    case
        when score is not null then score
        else first_value(score) over (partition by widget_id, c order by for_date)
        end score
from (
    select
        a.widget_id,
        a.for_date,
        s.score,
        count(score) over(partition by a.widget_id order by a.for_date) c
    from (
        select widget_id, g.d::date for_date
        from (
            select distinct widget_id
            from score
            ) s
            cross join
            generate_series(
                (select min(for_date) from score),
                (select max(for_date) from score),
                '1 day'
            ) g(d)
        ) a
        left join
        score s on a.widget_id = s.widget_id and a.for_date = s.for_date
) s
order by widget_id, for_date
于 2013-02-14T13:34:08.603 に答える
2

テーブル構造を使用して、MIN(For_Date)で始まり、MAX(For_Date)に達するまで増分する次の再帰CTEを作成しました。より効率的な方法があるかどうかはわかりませんが、これはうまく機能しているようです。

WITH RECURSIVE nodes_cte(widgetid, for_date, score) AS (
-- First Widget Using Min Date
 SELECT 
    w.widgetId, 
    w.for_date, 
    w.score
 FROM widgets w 
  INNER JOIN ( 
      SELECT widgetId, Min(for_date) min_for_date
      FROM widgets
      GROUP BY widgetId
   ) minW ON w.widgetId = minW.widgetid 
        AND w.for_date = minW.min_for_date
UNION ALL
 SELECT 
    n.widgetId,
    n.for_date + 1 for_date,
    coalesce(w.score,n.score) score
 FROM nodes_cte n
  INNER JOIN (
      SELECT widgetId, Max(for_date) max_for_date
      FROM widgets 
      GROUP BY widgetId
   ) maxW ON n.widgetId = maxW.widgetId
  LEFT JOIN widgets w ON n.widgetid = w.widgetid 
    AND n.for_date + 1 = w.for_date
  WHERE n.for_date + 1 <= maxW.max_for_date
)
SELECT * 
FROM nodes_cte 
ORDER BY for_date

これがSQLフィドルです。

そして返される結果(あなたが望むように日付をフォーマットする):

WIDGETID   FOR_DATE                     SCORE
1337       May, 07 2012 00:00:00+0000   12
1337       May, 08 2012 00:00:00+0000   41
1337       May, 09 2012 00:00:00+0000   41
1337       May, 10 2012 00:00:00+0000   41
1337       May, 11 2012 00:00:00+0000   500

これは、For_Dateフィールドが日付であると想定していることに注意してください(時間が含まれている場合)。代わりに、上記のクエリで間隔「1日」を使用する必要がある場合があります。

お役に立てれば。

于 2013-02-14T13:23:11.067 に答える
0

データ:

DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;

CREATE TABLE widget
        ( widget_id INTEGER NOT NULL
        , for_date DATE NOT NULL
        , score INTEGER
         , PRIMARY KEY (widget_id,for_date)
        );
INSERT INTO widget(widget_id , for_date , score) VALUES
 (1312, '2012-05-07', 20)
, (1337, '2012-05-07', 12)
, (1337, '2012-05-08', 41)
, (1337, '2012-05-11', 500)
        ;

クエリ:

SELECT w.widget_id AS widget_id
        , cal::date AS for_date
        -- , w.for_date AS org_date
        , w.score AS score
FROM generate_series( '2012-05-07'::timestamp , '2012-05-11'::timestamp
                 , '1day'::interval) AS cal
        -- "half cartesian" Join;
        -- will be restricted by the NOT EXISTS() below
LEFT JOIN widget w ON w.for_date <= cal
WHERE NOT EXISTS (
        SELECT * FROM widget nx
        WHERE nx.widget_id = w.widget_id
        AND nx.for_date <= cal
        AND nx.for_date > w.for_date
        )
ORDER BY cal, w.widget_id
        ;

結果:

 widget_id |  for_date  | score 
-----------+------------+-------
      1312 | 2012-05-07 |    20
      1337 | 2012-05-07 |    12
      1312 | 2012-05-08 |    20
      1337 | 2012-05-08 |    41
      1312 | 2012-05-09 |    20
      1337 | 2012-05-09 |    41
      1312 | 2012-05-10 |    20
      1337 | 2012-05-10 |    41
      1312 | 2012-05-11 |    20
      1337 | 2012-05-11 |   500
(10 rows)
于 2013-02-14T19:42:44.317 に答える