Postgres'teki mevcut sütuna 'seri' ekleme


100

Postgres 9.0 veritabanımda şu anda 1'den başlayan benzersiz sıralı tamsayılar içeren, ancak 'serial' anahtar sözcüğü kullanılarak oluşturulmamış bir tamsayı kimlik alanına (birincil anahtar) sahip küçük bir tablo (~ 30 satır) var.

Bu tabloyu bundan böyle bu tabloya yapılan eklemeler bu alanın bir tür olarak 'serial' ile oluşturulmuş gibi davranmasına neden olacak şekilde nasıl değiştirebilirim?


5
Bilginize, SERIALsözde tipi artık eski yeni supplanted, GENERATED … AS IDENTITYözelliği tanımlı içinde 2003: SQL , Postgres 10 ve sonrası. Açıklamaya bakın .
Basil Bourque

Modern Postgres sürümü (> = 10) için şu soruya bakın: stackoverflow.com/questions/2944499
a_horse_with_no_name

Yanıtlar:


137

Aşağıdaki komutlara bakın (özellikle yorumlanmış bloğa).

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;

OP'nizde birincil anahtarlardan bahsettiğiniz için, bunu da isteyebilirsiniz ALTER TABLE foo ADD PRIMARY KEY (a).
Skippy le Grand Gourou

SERİ, sözdizimsel şekerdir ve DB meta verilerinde depolanmaz, bu nedenle yukarıdaki kod% 100 eşdeğer olacaktır.
DKroot

Hedef tablonun farklı bir kullanıcı tarafından oluşturulma şansı varsa, önce yapmanız gerekir ALTER TABLE foo OWNER TO current_user;.
DKroot

2
MAX(a)+1Setval ayarlamanız gerekmiyor mu? SELECT MAX(a)+1 FROM foo; SELECT setval('foo_a_seq', 6);
SunnyPro

52

START WITHEuler'in cevabında olduğu gibi setval aynı şeyi başarsa da, belirli bir noktadan bir diziye başlamak için de kullanabilirsiniz .

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');

32

TL; DR

Burada, bir değeri okuyup kendi başına yazmak için bir insana ihtiyacınız olmayan bir sürüm var.

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'); 

Diğer bir seçenek Function, bu cevabın sonunda paylaşılan yeniden kullanılabilirliği kullanmak olacaktır .


Etkileşimli olmayan bir çözüm

Diğer iki yanıta ekleyerek, bunların etkileşimli olmayanSequence bir komut dosyasıyla oluşturulmasına ihtiyaç duyanlarımız içinÖrnek olarak live-ish DB'ye yama uygularken, bunların .

Yani, SELECTdeğeri manuel olarak istemediğinizde ve sonraki CREATEifadeye kendiniz yazın .

Kısacası, olabilir değil yapın:

CREATE SEQUENCE foo_a_seq
    START WITH ( SELECT max(a) + 1 FROM foo );

... içindeki START [WITH]cümle bir alt sorgu değil, CREATE SEQUENCEbir değer beklediğinden .

Not: (olmayan tüm CRUD için de geçerlidir Pratik bir kural olarak, yani : dışında bir INSERT, SELECT, UPDATE, DELETEifadelerden) pgSQL afaik.

Ancak setval()öyle! Bu nedenle, aşağıdakiler kesinlikle iyidir:

SELECT setval('foo_a_seq', max(a)) FROM foo;

Veri yoksa ve bunu bilmek istemiyorsanız (istemiyorsanız) coalesce(), varsayılan değeri ayarlamak için kullanın :

SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo;
--                         ^      ^         ^
--                       defaults to:       0

Bununla birlikte, mevcut sıra değerinin olarak ayarlanması 0, yasadışı değilse sakar.
Üç parametreli biçimini kullanmak setvaldaha uygun olur:

--                                             vvv
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
--                                                  ^   ^
--                                                is_called

İsteğe bağlı üçüncü bir parametre ayarlanması setvaliçin falseaşağıdaki engeller nextvalve böylece bir değer dönmeden önce sekansı ilerlemesini ve:

sonraki nextvaltam olarak belirtilen değeri döndürecektir ve sıra ilerlemesi aşağıdakilerle başlar nextval.

- belgelerdeki bu girişten

İlişkili olmayan bir notta, Sequencedoğrudan ile sahip olduğu sütunu da belirtebilirsiniz, CREATEdaha sonra değiştirmek zorunda kalmazsınız:

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;

Özetle:

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'); 

Bir Function

Alternatif olarak, bunu birden çok sütun için yapmayı planlıyorsanız, bir gerçek kullanmayı tercih edebilirsiniz 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;

Bunu şu şekilde kullanın:

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

Harika cevap, ancak coalesce(max(a), 0))kimliklerin genellikle 1'den başlaması nedeniyle çoğu zaman işe yaramayacağını unutmayın . Daha doğru bir yol olabilircoalesce(max(a), 1))
Amiko

1
Yorum için teşekkürler @ Amiko! setvalFonksiyon aslında sadece sekans için geçerli "son kullanılan değer" ayarlar. Bir sonraki mevcut değer (gerçekte kullanılacak ilk değer) bir tane daha olacaktır! setval(..., coalesce(max(a), 1))Boş bir sütunda kullanmak , dokümantasyonda2 gösterildiği gibi (bir sonraki mevcut değer) ile "başlamak" için ayarlayacaktır .
ccjmne

1
@Amiko Kodumda bir sorun olduğunu söylediğinizde haklısınız , gerçek veri kümesine yansıtılmasa bile currvalasla olmamalı 0. Üç parametre formu setvaldaha uygun olacaktır: setval(..., coalesce(max(a), 0) + 1, false). Cevap buna göre güncellendi!
ccjmne

1
Katılıyorum, bunu tamamen kaçırdım. Cevap için teşekkürler zamanımı kurtardım.
Amiko
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.