PostgreSQL 11
パーティションテーブルのID列のデフォルト値を生成する最良の方法は何ですか。
例えば
CREATE TABLE data.log
(
id BIGINT GENERATED ALWAYS AS IDENTITY
(
INCREMENT BY 1
MINVALUE -9223372036854775808
MAXVALUE 9223372036854775807
START WITH -9223372036854775808
RESTART WITH -9223372036854775808
CYCLE
),
Epoch_millis BIGINT NOT NULL,
message TEXT NOT NULL
) PARTITION BY RANGE (Epoch_millis);
CREATE TABLE data.foo_log
PARTITION OF data.log
(
PRIMARY KEY (id)
)
FOR VALUES FROM (0) TO (9999999999);
私が行った場合:
INSERT INTO data.foo_log (Epoch_millis, message)
VALUES (1000000, 'hello');
私は得ます:
エラー:列「id」のnull値はnot-null制約に違反しています
詳細:失敗した行には(null、1000000、hello)が含まれます。
SQL状態:23502
デフォルトで生成された値はパーティションに適用されないため、次のようにルートテーブルに挿入します。
INSERT INTO data.log (Epoch_millis, message)
VALUES (1000000, 'hello');
ただし、パフォーマンス上の理由(一括COPYの実行など)のために、特定のパーティションに直接挿入したい場合もあります。
これを機能させる唯一の方法は、次のようにID列に対して暗黙的に作成されたシーケンスを認識しながらパーティションを作成することです。
CREATE TABLE data.foo_log
PARTITION OF data.log
(
id DEFAULT nextval('data.log_id_seq'),
PRIMARY KEY (id)
)
FOR VALUES FROM (0) TO (9999999999);
これを行うためのより良い方法はありますか?
一般的には、より良い解決策を知りません。ただし、いくつかのマイナーなこと:
pg_get_serial_sequence()
親の暗黙のシーケンスの名前がわからない場合は、 pg_get_serial_sequence()
を使用します。
SELECT pg_get_serial_sequence('data.log', 'id');
CREATE TABLE
スクリプトで式を直接使用することもできますが、デフォルトの実際の名前を計算するために非常にわずかな追加コストがかかります(トランザクションごとに1回と思います)。これはパフォーマンスの最適化に関するものなので...
COPY
はGENERATED ALWAYS
をオーバーライドしますが、トリガーはしませんid
列をGENERATED ALWAYS AS IDENTITY
として定義すると、次のようなオーバーライド句を使用している場合でも、id
ステートメントで列INSERT
にユーザー値を提供することはできません。
INSERT INTO data.log (Epoch_millis, message) OVERRIDING USER VALUE
VALUES (1000000, 'hello');
これが機能するためにはGENERATED BY DEFAULT
であるか、またはid
からomitINSERT
を完全に使用する必要があります。 マニュアル:
OVERRIDING USER VALUE
この句が指定されている場合、
GENERATED BY DEFAULT
として定義されているID列に指定された値はすべて無視され、デフォルトのシーケンス生成値が適用されます。この句は、テーブル間で値をコピーする場合などに役立ちます。
INSERT INTO tbl2 OVERRIDING USER VALUE SELECT * FROM tbl1
を書き込むと、tbl1
のID列ではないすべての列がtbl2
からコピーされますが、tbl2
のID列の値は、tbl2
に関連付けられたシーケンスによって生成されます。
COPY
いずれの場合も上書きされます。 マニュアル:
ID列の場合、
COPY FROM
コマンドは、INSERT
オプションOVERRIDING SYSTEM VALUE
のように、常に入力データで提供される列の値を書き込みます。
しかし、パーティションに直接書き込むとき、ソリューションではINSERT
もオーバーライドされるため、id
列にユーザー値を直接指定しないようにするためにあなたの責任になります。代替策は、パーティションでトリガーを使用することですデフォルト値の代わりに:
CREATE OR REPLACE FUNCTION trg_log_default_id()
RETURNS trigger AS
$func$
BEGIN
NEW.id := nextval('data.log_id_seq')
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER insbef_default_id
BEFORE INSERT ON data.foo_log -- the partition
FOR EACH ROW
EXECUTE PROCEDURE trg_log_default_id();
これにより、シーケンスから番号が割り当てられます。親のGENERATED ALWAYS
の動作をより厳密にエミュレートします。さらに厳密に、COPY
がルールに違反するのを防ぎます。 マニュアル:
COPY FROM
は、トリガーを呼び出し、宛先テーブルの制約をチェックします。
ただし、トリガーは単純なデフォルト値よりも少し高価です。また、親テーブルを介した通常の挿入では、行ごとに追加のシリアル番号が書き込まれます。 (トリガーではケースを区別できるはずですが、今は試みませんでした。)