Postgres 9.0データベースに小さなテーブル(〜30行)があり、整数IDフィールド(主キー)が現在1から始まる一意の連続した整数が含まれていますが、 'serial'キーワードを使用して作成されていません。
今後、このテーブルに挿入すると、このフィールドがタイプとして「シリアル」を使用して作成されたかのように動作するように、このテーブルを変更するにはどうすればよいですか?
Postgres 9.0データベースに小さなテーブル(〜30行)があり、整数IDフィールド(主キー)が現在1から始まる一意の連続した整数が含まれていますが、 'serial'キーワードを使用して作成されていません。
今後、このテーブルに挿入すると、このフィールドがタイプとして「シリアル」を使用して作成されたかのように動作するように、このテーブルを変更するにはどうすればよいですか?
回答:
次のコマンド(特にコメントブロック)を確認してください。
DROP TABLE foo;
DROP TABLE bar;
CREATE TABLE foo (a int, b text);
CREATE TABLE bar (a serial, b text);
INSERT INTO foo (a, b) SELECT i, 'foo ' || i::text FROM generate_series(1, 5) i;
INSERT INTO bar (b) SELECT 'bar ' || i::text FROM generate_series(1, 5) i;
-- blocks of commands to turn foo into bar
CREATE SEQUENCE foo_a_seq;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
ALTER TABLE foo ALTER COLUMN a SET NOT NULL;
ALTER SEQUENCE foo_a_seq OWNED BY foo.a; -- 8.2 or later
SELECT MAX(a) FROM foo;
SELECT setval('foo_a_seq', 5); -- replace 5 by SELECT MAX result
INSERT INTO foo (b) VALUES('teste');
INSERT INTO bar (b) VALUES('teste');
SELECT * FROM foo;
SELECT * FROM bar;
ALTER TABLE foo ADD PRIMARY KEY (a)
。
ALTER TABLE foo OWNER TO current_user;
最初に行う必要があります。
MAX(a)+1
setvalを設定するべきではありませんか?SELECT MAX(a)+1 FROM foo; SELECT setval('foo_a_seq', 6);
これは、値を読み取って自分で入力するのに人間を必要としないバージョンです。
CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
別のオプションはFunction
、この回答の終わりに再利用可能な共有を採用することです。
他の2つの答えに追加するだけで、たとえばSequence
、非対話型スクリプトによってこれらのを作成し、ライブ風のDBにパッチを適用する必要がある場合などです。
つまりSELECT
、値を手動で入力したくない場合、次のCREATE
ステートメントに自分で入力します。
つまり、次のことはできません。
CREATE SEQUENCE foo_a_seq
START WITH ( SELECT max(a) + 1 FROM foo );
...のSTART [WITH]
句は、サブクエリではなく、値をCREATE SEQUENCE
期待するためです。
注:(すべての非CRUDに適用される経験則として、すなわち:以外
INSERT
、SELECT
、UPDATE
、DELETE
内の文)pgSQLに私の知る限り。
しかし、setval()
そうです!したがって、以下は絶対に問題ありません。
SELECT setval('foo_a_seq', max(a)) FROM foo;
データがなく、それを知らない(知りたい)場合は、を使用coalesce()
してデフォルト値を設定します。
SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo;
-- ^ ^ ^
-- defaults to: 0
ただし、現在のシーケンス値をに設定すること0
は、違法ではないにしても不格好です。
3パラメータ形式のを使用setval
するのがより適切です。
-- vvv
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
-- ^ ^
-- is_called
オプションの3番目のパラメーターをsetval
toに設定するfalse
と、next nextval
が値を返す前にシーケンスを進めることができなくなり、次のようになります。
次
nextval
は正確に指定された値を返し、シーケンスの前進は以下から始まりますnextval
。
— ドキュメントのこのエントリから
関連のないメモでは、をSequence
直接所有する列を指定することもできます。CREATE
後で変更する必要はありません。
CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
要約すれば:
CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
Function
または、複数の列に対してこれを行う予定の場合は、実際のを使用することを選択できますFunction
。
CREATE OR REPLACE FUNCTION make_into_serial(table_name TEXT, column_name TEXT) RETURNS INTEGER AS $$
DECLARE
start_with INTEGER;
sequence_name TEXT;
BEGIN
sequence_name := table_name || '_' || column_name || '_seq';
EXECUTE 'SELECT coalesce(max(' || column_name || '), 0) + 1 FROM ' || table_name
INTO start_with;
EXECUTE 'CREATE SEQUENCE ' || sequence_name ||
' START WITH ' || start_with ||
' OWNED BY ' || table_name || '.' || column_name;
EXECUTE 'ALTER TABLE ' || table_name || ' ALTER COLUMN ' || column_name ||
' SET DEFAULT nextVal(''' || sequence_name || ''')';
RETURN start_with;
END;
$$ LANGUAGE plpgsql VOLATILE;
次のように使用します。
INSERT INTO foo (data) VALUES ('asdf');
-- ERROR: null value in column "a" violates not-null constraint
SELECT make_into_serial('foo', 'a');
INSERT INTO foo (data) VALUES ('asdf');
-- OK: 1 row(s) affected
coalesce(max(a), 0))
、Idsは通常1から始まるため、ほとんどの場合は機能しません。より正確な方法はcoalesce(max(a), 1))
currval
してはいけません0
、それは実際のデータセットには反映されない場合でも、。の3つのパラメータ形式を使用setval
するのがより適切ですsetval(..., coalesce(max(a), 0) + 1, false)
。それに応じて更新された回答!
SERIAL
疑似型がレガシーになりました。説明を参照してください。GENERATED … AS IDENTITY