投稿された回答のより簡単な代替手段。パフォーマンスが大幅に向上するはずです。
この関数は、指定されたテーブル( in_table_name
)から行を取得します )および主キー値( in_row_pk
)、それを新しい行として同じテーブルに挿入し、一部の値を置き換えます( in_override_values
)。デフォルトの新しい主キー値が返されます( pk_new
。
CREATE OR REPLACE FUNCTION f_clone_row(in_table_name regclass
, in_row_pk int
, in_override_values hstore
, OUT pk_new int) AS
$func$
DECLARE
_pk text; -- name of PK column
_cols text; -- list of names of other columns
BEGIN
-- Get name of PK column
SELECT INTO _pk a.attname
FROM pg_catalog.pg_index i
JOIN pg_catalog.pg_attribute a ON a.attrelid = i.indrelid
AND a.attnum = i.indkey[0] -- 1 PK col!
WHERE i.indrelid = 't'::regclass
AND i.indisprimary;
-- Get list of columns excluding PK column
_cols := array_to_string(ARRAY(
SELECT quote_ident(attname)
FROM pg_catalog.pg_attribute
WHERE attrelid = in_table_name -- regclass used as OID
AND attnum > 0 -- exclude system columns
AND attisdropped = FALSE -- exclude dropped columns
AND attname <> _pk -- exclude PK column
), ',');
-- INSERT cloned row with override values, returning new PK
EXECUTE format('
INSERT INTO %1$I (%2$s)
SELECT %2$s
FROM (SELECT (t #= $1).* FROM %1$I t WHERE %3$I = $2) x
RETURNING %3$I'
, in_table_name, _cols, _pk)
USING in_override_values, in_row_pk -- use override values directly
INTO pk_new; -- return new pk directly
END
$func$ LANGUAGE plpgsql;
電話:
SELECT f_clone_row('t', 1, '"col1"=>"foo_new","col2"=>"bar_new"'::hstore);
-
regclass
を使用します 入力パラメータタイプとして、有効なテーブル名のみを最初に使用でき、SQLインジェクションは除外されます。不正なテーブル名を指定する必要がある場合も、関数はより早く、より適切に失敗します。 -
OUT
を使用する パラメータ(pk_new
)構文を単純化するため。 -
主キーの次の値を手動で把握する必要はありません。自動的に挿入され、事後に返されます。これは、単純で高速であるだけでなく、シーケンス番号の無駄や順序の乱れも回避できます。
-
format()を使用しますコード>
動的クエリ文字列のアセンブリを簡素化し、エラーが発生しにくくするため。識別子と文字列にそれぞれ位置パラメータを使用する方法に注意してください。 -
私はあなたの暗黙の仮定に基づいています 許可されたテーブルには、整数型の単一の主キー列とデフォルトの列があります 。通常、
シリアル
列。 -
関数の重要な要素は、最後の
INSERT
です。 :-
#=
オペレーター 副選択で、結果の行をすぐに分解します。 - 次に、メインの
SELECT
で関連する列のみを選択できます 。 - PostgresにPKのデフォルト値を割り当てさせ、
RETURNING
で元に戻します。 条項。 - 戻り値を
OUT
に書き込みます パラメータを直接。 - すべてが単一のSQLコマンドで実行され、通常は最速です。
-