4

INSERT INTO t1 SELECT * FROM...列名が一致しない場合に失敗するような方法はありますか?

Postgresql9.xを使用しています。列名は事前にわかりません。

動機:(かなり標準的な)PL/pgSQLプロシージャによってマテリアライズドビューを定期的に更新しています。

CREATE OR REPLACE FUNCTION matview_refresh(name) RETURNS void AS 
$BODY$
DECLARE 
    matview ALIAS FOR $1;
    entry matviews%ROWTYPE;
BEGIN
    SELECT * INTO entry FROM matviews WHERE mv_name = matview;
    IF NOT FOUND THEN
        RAISE EXCEPTION 'Materialized view % does not exist.', matview;
    END IF;

    EXECUTE 'TRUNCATE TABLE ' || matview;
    EXECUTE 'INSERT INTO ' || matview  || ' SELECT * FROM ' || entry.v_name;

    UPDATE matviews SET last_refresh=CURRENT_TIMESTAMP WHERE mv_name=matview;
    RETURN;
END

私は、DROP / CREATETRUNCATEの代わりに、より軽量で同時実行に適しているように見えたので、その後に続くことを好みました。SELECT * INTO誰かがビューから列を追加/削除すると失敗しますが(その後、DROP / CREATEを実行します)、問題ではありません。その場合、更新は完了せず、すぐに問題を検出します。重要なのは、今日起こったことです。誰かが(同じタイプの)ビューの2つの列の順序を変更し、更新によって偽のデータが挿入されました。

4

2 に答える 2

1

information_schema.columns をクエリして、列を正しい順序で取得できます。

SELECT INTO cols array_to_string(array_agg(column_name::text), ',') 
FROM (
    SELECT column_name 
    FROM information_schema.columns 
    WHERE table_name = 'matview' 
    ORDER BY ordinal_position
) AS x;
EXECUTE 'INSERT INTO ' || matview  || ' SELECT ' || cols || ' FROM ' || entry.v_name;

pg_attribute から列リストを直接取得できます。内部SELECTinformation_schema.columns次のように置き換えるだけです。

SELECT attname AS column_name
FROM pg_attribute
WHERE attrelid = 'matview'::regclass AND attisdropped = false
ORDER BY attnum;
于 2012-11-17T00:25:54.897 に答える
1

これを plpgsql 関数に組み込み、ビューとテーブルが同じ列名を同じ順序で正確に共有していることを確認します。

IF EXISTS (
    SELECT 1
    FROM (
       SELECT *
       FROM   pg_attribute
       WHERE  attrelid = matview::regclass
       AND    attisdropped = FALSE
       AND    attnum > 0
       ) t
    FULL OUTER JOIN (
       SELECT *
       FROM   pg_attribute
       WHERE  attrelid = entry.v_name::regclass
       AND    attisdropped = FALSE
       AND    attnum > 0
       ) v USING (attnum, attname) -- atttypid to check for type, too
    WHERE t.attname IS NULL
    OR    v.attname IS NULL
   ) THEN 
   RAISE EXCEPTION 'Mismatch between table and view!';
END IF;

は、列名のリスト間の不一致の値を含むFULL OUTER JOIN行を追加します。NULLしたがって、EXISTS行が見つかった場合は、何かがオフになっています。

また、テーブルまたはビューのいずれかが存在しない場合 (またはスコープ外であり、スキーマ修飾されていない場合)にキャストする::regclassと、すぐに例外が発生します。search_path

列のデータ型も確認したい場合は、句に追加atttypidするだけです。USING

余談ですpg_catalogが、通常、テーブルのクエリは、肥大化したビュー int のクエリよりも桁違いに高速です。information_schema はinformation_schema、SQL 標準への準拠とコードの移植性にのみ適しています。100% Postgres 固有のコードを書いているので、どちらもここでは関係ありません。

于 2012-11-17T00:38:50.400 に答える