投稿された回答のより簡単な代替。はるかに優れたパフォーマンスを発揮するはずです。
この関数は、指定されたテーブル ( 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);
SQL フィドル。
入力パラメーターの型として使用regclass
するため、最初は有効なテーブル名のみを使用でき、SQL インジェクションは除外されます。また、不正なテーブル名を指定する必要がある場合、関数はより早くより適切に失敗します。
OUT
パラメーター ( ) を使用してpk_new
、構文を簡素化します。
主キーの次の値を手動で把握する必要はありません。自動的に挿入され、事後に返されます。これにより、よりシンプルで高速になるだけでなく、無駄なシーケンス番号や順不同のシーケンス番号も回避できます。
format()
動的クエリ文字列の組み立てを簡素化し、エラーを起こしにくくするために使用します。識別子と文字列にそれぞれ位置パラメータを使用する方法に注意してください。
許可されたテーブルには、列のデフォルトを持つ整数型の単一の主キー列があるという暗黙の仮定に基づいています。通常は列。serial
関数の重要な要素は finalINSERT
です:
#=
副選択で演算子を使用してオーバーライド値を既存の行とマージし、結果の行をすぐに分解します。
- 次に、メインで関連する列のみを選択できます
SELECT
。
- Postgres に PK のデフォルト値を割り当てさせ、
RETURNING
句でそれを取得します。
- 戻り値を
OUT
パラメーターに直接書き込みます。
- すべてが単一の SQL コマンドで実行されるため、一般的に最速です。