Saya memiliki tabel kecil (~ 30 baris) di database Postgres 9.0 saya dengan bidang ID integer (kunci utama) yang saat ini berisi bilangan bulat berurutan unik mulai dari 1, tetapi tidak dibuat menggunakan kata kunci 'serial'.
Bagaimana cara mengubah tabel ini sehingga mulai sekarang, penyisipan ke tabel ini akan menyebabkan bidang ini berperilaku seolah-olah telah dibuat dengan 'serial' sebagai jenis?
postgresql
nicolaskruchten.dll
sumber
sumber
SERIAL
pseudo-type sekarang sudah lama , digantikan olehGENERATED … AS IDENTITY
fitur baru yang didefinisikan di SQL: 2003 , di Postgres 10 dan yang lebih baru. Lihat penjelasannya .Jawaban:
Lihat perintah berikut (terutama blok yang diberi komentar).
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;
sumber
ALTER TABLE foo ADD PRIMARY KEY (a)
.ALTER TABLE foo OWNER TO current_user;
terlebih dahulu.MAX(a)+1
di setval?SELECT MAX(a)+1 FROM foo; SELECT setval('foo_a_seq', 6);
Anda juga dapat menggunakan
START WITH
untuk memulai urutan dari titik tertentu, meskipun setval menyelesaikan hal yang sama, seperti dalam jawaban Euler, misalnya,SELECT MAX(a) + 1 FROM foo; CREATE SEQUENCE foo_a_seq START WITH 12345; -- replace 12345 with max above ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
sumber
TL; DR
Ini adalah versi di mana Anda tidak memerlukan manusia untuk membaca nilai dan mengetiknya sendiri.
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');
Pilihan lain adalah menggunakan yang dapat digunakan kembali
Function
bersama di akhir jawaban ini.Solusi non-interaktif
Hanya menambahkan ke dua jawaban lainnya, bagi kita yang membutuhkan ini
Sequence
dibuat oleh skrip non-interaktif , sambil menambal DB live-ish misalnya.Artinya, saat Anda tidak menginginkan
SELECT
nilainya secara manual dan mengetiknya sendiri ke dalamCREATE
pernyataan berikutnya .Singkatnya, Anda tidak dapat melakukan:
CREATE SEQUENCE foo_a_seq START WITH ( SELECT max(a) + 1 FROM foo );
... karena
START [WITH]
klausa dalamCREATE SEQUENCE
mengharapkan nilai , bukan subkueri.Namun,
setval()
lakukan! Jadi, berikut ini baik-baik saja:SELECT setval('foo_a_seq', max(a)) FROM foo;
Jika tidak ada data dan Anda tidak (ingin) mengetahuinya, gunakan
coalesce()
untuk menyetel nilai default:SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo; -- ^ ^ ^ -- defaults to: 0
Namun, menyetel nilai urutan saat ini ke
0
adalah kikuk, jika tidak ilegal.Menggunakan bentuk tiga parameter dari
setval
akan lebih tepat:-- vvv SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo; -- ^ ^ -- is_called
Menyetel parameter opsional ketiga dari
setval
menjadifalse
akan mencegah yang berikutnyanextval
memajukan urutan sebelum mengembalikan nilai, dan dengan demikian:- dari entri ini di dokumentasi
Pada catatan yang tidak terkait, Anda juga dapat menentukan kolom yang memiliki
Sequence
langsung denganCREATE
, Anda tidak perlu mengubahnya nanti:CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
Singkatnya:
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');
Menggunakan sebuah
Function
Alternatifnya, jika Anda berencana melakukan ini untuk beberapa kolom, Anda dapat memilih untuk menggunakan aktual
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;
Gunakan seperti ini:
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
sumber
coalesce(max(a), 0))
tidak akan berfungsi sebagian besar waktu, karena Id biasanya mulai dari 1. Cara yang lebih tepat adalahcoalesce(max(a), 1))
setval
Fungsi sebenarnya hanya menetapkan saat "Nilai bekas terbaru" untuk urutan. Nilai berikutnya yang tersedia (yang pertama benar-benar digunakan) adalah satu lagi! Menggunakansetval(..., coalesce(max(a), 1))
pada kolom kosong akan menyiapkannya untuk "dimulai" dengan2
(nilai berikutnya yang tersedia), seperti yang diilustrasikan dalam dokumentasi .currval
seharusnya tidak pernah0
, bahkan jika itu tidak akan tercermin dalam dataset yang sebenarnya. Menggunakan bentuk tiga parametersetval
akan lebih tepat:setval(..., coalesce(max(a), 0) + 1, false)
. Jawaban diperbarui sesuai!