4

2002年の毎日のデータを含むテーブルがありますが、日付が欠落しています。つまり、2002年のレコードは(365ではなく)354レコードです。私の計算では、テーブルにNull値を持つ欠落データが必要です

+-----+------------+------------+
| ID  |  rainfall  | date       |
+-----+------------+------------+
| 100 |  110.2     | 2002-05-06 |
| 101 |  56.6      | 2002-05-07 |
| 102 |  65.6      | 2002-05-09 |
| 103 |  75.9      | 2002-05-10 |
+-----+------------+------------+

2002-05-08が欠落していることがわかります。最終的なテーブルを次のようにします。

+-----+------------+------------+
| ID  |  rainfall  | date       |
+-----+------------+------------+
| 100 |  110.2     | 2002-05-06 |
| 101 |  56.6      | 2002-05-07 |
| 102 |            | 2002-05-08 |
| 103 |  65.6      | 2002-05-09 |
| 104 |  75.9      | 2002-05-10 |
+-----+------------+------------+

PostgreSQLでそれを行う方法はありますか?

結果がクエリ結果と同じであるかどうかは関係ありません(必ずしも更新されたテーブルである必要はありません)

4

4 に答える 4

9

date標準 SQLの予約語であり、PostgreSQL のデータ型の名前です。PostgreSQL ではこれを識別子として使用できますが、それは良い考えではありません。thedate代わりに列名として使用します。

サロゲート ID にギャップがないことに依存しないでください。それはほとんどの場合、悪い考えです。このような ID は、他の特定の属性を持っているように見える場合でも、意味のない一意の番号として扱っください

この特定のケースでは、@Clodoaldo がコメントしthedateように、完全な主キーのように見え、列idはただの粗悪品です-削除しました:

CREATE TEMP TABLE tbl (thedate date PRIMARY KEY, rainfall numeric);
INSERT INTO tbl(thedate, rainfall) VALUES
  ('2002-05-06', 110.2)
, ('2002-05-07', 56.6)
, ('2002-05-09', 65.6)
, ('2002-05-10', 75.9);

クエリ

クエリごとの完全なテーブル:

SELECT x.thedate, t.rainfall  -- rainfall automatically NULL for missing rows
FROM (
   SELECT generate_series(min(thedate), max(thedate), '1d')::date AS thedate
   FROM   tbl
   ) x
LEFT   JOIN tbl t USING (thedate)
ORDER  BY x.thedate

@a_horse_with_no_nameが投稿したものと似ていますが、簡略化して pruned を無視していidます。

テーブルで見つかった最初の日付と最後の日付の間のギャップを埋めます。先行/遅延ギャップが存在する可能性がある場合は、それに応じて拡張します。@Clodoaldodate_trunc()が示したように使用できますが、彼のクエリは構文エラーに悩まされており、より単純になる可能性があります。

欠落している行を挿入

それを行うための最も速くて読みやすい方法は、NOT EXISTSアンチセミジョインです。

INSERT INTO tbl (thedate, rainfall)
SELECT x.thedate, NULL
FROM (
   SELECT generate_series(min(thedate), max(thedate), '1d')::date AS thedate
   FROM   tbl
   ) x
WHERE NOT EXISTS (SELECT 1 FROM tbl t WHERE t.thedate = x.thedate)
于 2012-10-28T05:16:11.163 に答える
8

2002 年のすべての日付を返すクエリに対して外部結合を行うだけです。

with all_dates as (
  select date '2002-01-01' + i as date_col
  from generate_series(0, extract(doy from date '2002-12-31')::int - 1) as i
)
select row_number() over (order by ad.date_col) as id, 
       t.rainfall,
       ad.date_col as date
from all_dates ad
  left join your_table t on ad.date_col = t.date
order by ad.date_col;

これによりテーブルが変更されることはありません。必要な結果が得られるだけです。

生成された id 列には、テーブルの ID 列と同じ値が含まれないことに注意してください。これは、結果セット内の単なるカウンターであるためです。

row_number()関数を次のように置き換えることもできますextract(doy from ad.date_col)

于 2012-10-27T13:48:30.490 に答える
4

ギャップを埋めるために。これは ID を並べ替えません。

insert into t (rainfall, "date") values
select null, "date"
from (
    select d::date as "date"
    from (
        t
        right join
        generate_series(
            (select date_trunc('year', min("date")) from t)::timestamp,
            (select max("date") from t),
            '1 day'
        ) s(d) on t."date" = s.d::date
    where t."date" is null
    ) q
) s
于 2012-10-27T13:47:30.323 に答える
1

インデックスを変更する必要があるため、テーブルを完全に再作成する必要があります。

これを行うためのより良い方法は、好みの dbi 言語を使用し、ID を無視してループを作成し、新しいシリアル化された ID を持つ新しいテーブルに値を入れることです。

for day in (whole needed calendar)
    value = select rainfall from oldbrokentable where date = day
    insert into newcleanedtable date=day, rainfall=value, id=serialized

(これは実際のコードではありません! お好みのスクリプト言語に適応させるための概念的なものです)

于 2012-10-27T13:24:21.583 に答える