Autoinkrementacja PostgreSQL

578

Przechodzę z MySQL na PostgreSQL i zastanawiałem się, jak mogę wykonać automatyczne zwiększenie wartości. W dokumentach PostgreSQL widziałem typ danych „seryjny”, ale podczas korzystania z niego występują błędy składniowe (w wersji 8.0).

Ian
źródło
9
jeśli podasz zapytanie i otrzymany błąd - być może ktoś powie ci, co jest nie tak z zapytaniem.
2
Moje pierwsze uderzenie też Micha, a ponieważ jest to pytanie, które ma wystarczająco dużo poglądów, aby być trafnym, dlaczego nie zagłosować. PS. Nie jest to trywialne, jeśli nie wiesz, jak to zrobić.
baash05
1
SERIAL jest preferowanym wyborem, jeśli sterownik klienta to Npgsql. Dostawca wewnętrznie wybiera nowe wartości po INSERT za pomocą SELECT currval (pg_get_serial_sequence („tabela”, „kolumna”)). Nie powiedzie się, jeśli podstawowa kolumna nie jest typu szeregowego (na przykład typ numeryczny + wyraźna sekwencja)
Olivier MATROT
Dla ciekawości ... Dlaczego ktoś musi migrować z MySQL, który jest bardzo dobry, do PostgreSql?
villamejia
17
... co jest jeszcze lepsze.
Rohmer,

Odpowiedzi:

701

Tak, SERIAL jest równoważną funkcją.

CREATE TABLE foo (
id SERIAL,
bar varchar);

INSERT INTO foo (bar) values ('blah');
INSERT INTO foo (bar) values ('blah');

SELECT * FROM foo;

1,blah
2,blah

SERIAL to po prostu makro czasu tabeli wokół sekwencji. Nie możesz zmienić SERIAL na istniejącą kolumnę.

Trey
źródło
19
zacytowanie nazwy tabeli to naprawdę zła praktyka
Evan Carroll
71
Cytowanie nazw tabel jest nawykiem, ponieważ odziedziczyłem DB, który miał mieszane nazwy liter, a cytowanie nazw tabel jest wymogiem użycia.
Trey
26
@Evan Carroll - Dlaczego to zły nawyk (po prostu pytam)?
Christian
27
bo chyba, że ​​masz stolik, "Table"a "table"następnie po prostu zostaw go nie cytowany i kanonizuj go table. Konwencja po prostu nigdy nie używa cudzysłowów w Pg. Możesz, jeśli chcesz, używać różnych nazw liter do wyglądu, po prostu nie wymagaj tego: CREATE TABLE fooBar ( .. ); SELECT * FROM fooBar;będzie działać, jak będzie SELECT * FROM foobar.
Evan Carroll
26
Zgodnie z postgres doc, konsekwentnie
cytuj
226

Możesz użyć dowolnego innego typu danych całkowitych , takiego jak smallint.

Przykład:

CREATE SEQUENCE user_id_seq;
CREATE TABLE user (
    user_id smallint NOT NULL DEFAULT nextval('user_id_seq')
);
ALTER SEQUENCE user_id_seq OWNED BY user.user_id;

Lepiej jest używać własnego typu danych niż szeregowego typu danych użytkownika .

Ahmad
źródło
11
Powiedziałbym, że jest to w rzeczywistości lepsza odpowiedź, ponieważ pozwoliła mi zmodyfikować tabelę, którą właśnie utworzyłem w PostgreSQL, ustawiając domyślne kolumny (po przeczytaniu na CREATE SEQUENCE postgresql.org/docs/8.1/interactive/sql-createsequence.html ) . JEDNAK nie jestem pewien, dlaczego zmieniłeś właściciela.
JayC
12
@JayC: Z dokumentacji : Na koniec sekwencja jest oznaczona jako „własność” kolumny, więc zostanie usunięta, jeśli kolumna lub tabela zostaną upuszczone.
user272735
8
dlaczego społeczność Postgres nie wymyśla na nowo słowa kluczowego autoinkrement?
Dr Deo
2
@Dr Deo: używają słowa kluczowego zamiast autoakrementacji, nie wiem dlaczego :)
Ahmad
4
Istnieje również smallserial, jeśli chcesz tylko mniejszego typu danych.
beldaz
110

Jeśli chcesz dodać sekwencję do identyfikatora w tabeli, która już istnieje, możesz użyć:

CREATE SEQUENCE user_id_seq;
ALTER TABLE user ALTER user_id SET DEFAULT NEXTVAL('user_id_seq');
Sereja
źródło
Co to jest sekwencja? Gdzie jest AUTO_INCREMENT?
Zielony
23
@Green: AUTO_INCREMENT nie jest częścią standardu SQL, jest specyficzny dla MySQL. Sekwencje są czymś, co wykonuje podobną pracę w PostgreSQL.
beldaz
5
jeśli użyjesz „id SERIAL”, automatycznie utworzy sekwencję w PostgreSQL. Nazwą tej sekwencji będzie <nazwa tabeli> _ <nazwa kolumny> _seq
Jude Niroshan
Nie musisz używać ALTER COLUMN user_id?
Alec
Próbowałem tej metody, ale pojawia się błąd: ERROR: syntax error at or near "DEFAULT"jakieś sugestie?
Ely Fialkoff,
44

Choć wygląda na to, że sekwencje są odpowiednikiem auto_increment MySQL, istnieją pewne subtelne, ale ważne różnice:

1. Zwiększenie liczby nieudanych zapytań Sekwencja / numer seryjny

Kolumna szeregowa jest zwiększana w przypadku nieudanych zapytań. Prowadzi to do fragmentacji z powodu nieudanych zapytań, a nie tylko usuwania wierszy. Na przykład uruchom następujące zapytania w bazie danych PostgreSQL:

CREATE TABLE table1 (
  uid serial NOT NULL PRIMARY KEY,
  col_b integer NOT NULL,
  CHECK (col_b>=0)
);

INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);

SELECT * FROM table1;

Powinieneś otrzymać następujące dane wyjściowe:

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
(2 rows)

Zauważ, jak uid zmienia się z 1 na 3 zamiast 1 na 2.

Dzieje się tak nadal, jeśli ręcznie utworzysz własną sekwencję za pomocą:

CREATE SEQUENCE table1_seq;
CREATE TABLE table1 (
    col_a smallint NOT NULL DEFAULT nextval('table1_seq'),
    col_b integer NOT NULL,
    CHECK (col_b>=0)
);
ALTER SEQUENCE table1_seq OWNED BY table1.col_a;

Jeśli chcesz przetestować, jak różni się MySQL, uruchom następujące polecenie w bazie danych MySQL:

CREATE TABLE table1 (
  uid int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
  col_b int unsigned NOT NULL
);

INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);

Powinieneś otrzymać następujące bez frustracji :

+-----+-------+
| uid | col_b |
+-----+-------+
|   1 |     1 |
|   2 |     2 |
+-----+-------+
2 rows in set (0.00 sec)

2. Ręczne ustawienie wartości kolumny szeregowej może spowodować, że przyszłe zapytania nie będą działać.

Zwrócił na to uwagę @trev w poprzedniej odpowiedzi.

Aby to zasymulować ręcznie, ustaw UID na 4, który później „zderzy się”.

INSERT INTO table1 (uid, col_b) VALUES(5, 5);

Dane tabeli:

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
   5 |     5
(3 rows)

Uruchom kolejną wkładkę:

INSERT INTO table1 (col_b) VALUES(6);

Dane tabeli:

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
   5 |     5
   4 |     6

Teraz, jeśli uruchomisz inną wstawkę:

INSERT INTO table1 (col_b) VALUES(7);

Nie powiedzie się z następującym komunikatem o błędzie:

BŁĄD: zduplikowana wartość klucza narusza unikalne ograniczenie „table1_pkey” SZCZEGÓŁY: Klucz (UID) = (5) już istnieje.

W przeciwieństwie do tego MySQL poradzi sobie z tym z wdziękiem, jak pokazano poniżej:

INSERT INTO table1 (uid, col_b) VALUES(4, 4);

Teraz wstaw kolejny wiersz bez ustawiania UID

INSERT INTO table1 (col_b) VALUES(3);

Zapytanie nie kończy się niepowodzeniem, UID po prostu przeskakuje do 5:

+-----+-------+
| uid | col_b |
+-----+-------+
|   1 |     1 |
|   2 |     2 |
|   4 |     4 |
|   5 |     3 |
+-----+-------+

Testy przeprowadzono na MySQL 5.6.33, dla Linuksa (x86_64) i PostgreSQL 9.4.9

Programista
źródło
10
Dajesz porównanie, ale nie widzę tutaj żadnego rozwiązania! Czy to odpowiedź?
Anwar
4
@Anwar po prostu rozszerza różne odpowiedzi, które stwierdzają, że odpowiedzią jest użycie numeru seryjnego / sekwencji. Daje to pewien ważny kontekst do wzięcia pod uwagę.
Programster
38

Począwszy od Postgres 10, obsługiwane są również kolumny tożsamości zdefiniowane w standardzie SQL:

create table foo 
(
  id integer generated always as identity
);

tworzy kolumnę tożsamości, której nie można zastąpić, chyba że zostanie o to wyraźnie poproszony. Następująca wstawka zakończy się niepowodzeniem z kolumną zdefiniowaną jako generated always:

insert into foo (id) 
values (1);

Można to jednak zmienić:

insert into foo (id) overriding system value 
values (1);

Podczas korzystania z opcji generated by defaultjest to zasadniczo to samo zachowanie, co istniejąca serialimplementacja:

create table foo 
(
  id integer generated by default as identity
);

Gdy wartość jest podawana ręcznie, sekwencję podstawową należy również dostosować ręcznie - tak samo jak w przypadku serialkolumny.


Kolumna tożsamości nie jest domyślnie kluczem podstawowym (podobnie jak serialkolumna). Jeśli ma to być jeden, ograniczenie klucza podstawowego należy zdefiniować ręcznie.

koń bez imienia
źródło
26

Przykro mi, aby powtórzyć stare pytanie, ale było to pierwsze pytanie / odpowiedź Przepełnienie stosu, które pojawiło się w Google.

Ten post (który pojawił się jako pierwszy w Google) mówi o używaniu bardziej zaktualizowanej składni PostgreSQL 10: https://blog.2ndquadrant.com/postgresql-10-identity-columns/

co się dzieje:

CREATE TABLE test_new (
    id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
);

Mam nadzieję, że to pomoże :)

Zhao Li
źródło
1
Tak jest w rzeczywistości w PostgreSQL 10 i ma tę samą składnię, co inne oprogramowanie baz danych, takie jak DB2 lub Oracle.
adriaan
1
@ adriaan Właściwie GENERATED … AS IDENTITYpolecenia są standardowym SQL. Najpierw dodano w SQL: 2003 , a następnie wyjaśniono w SQL: 2008 . Zobacz funkcje # T174 i F386 i T178.
Basil Bourque,
16

Musisz uważać, aby nie wstawić bezpośrednio do pola SERIAL lub sekwencji, w przeciwnym razie zapis nie powiedzie się, gdy sekwencja osiągnie wstawioną wartość:

-- Table: "test"

-- DROP TABLE test;

CREATE TABLE test
(
  "ID" SERIAL,
  "Rank" integer NOT NULL,
  "GermanHeadword" "text" [] NOT NULL,
  "PartOfSpeech" "text" NOT NULL,
  "ExampleSentence" "text" NOT NULL,
  "EnglishGloss" "text"[] NOT NULL,
  CONSTRAINT "PKey" PRIMARY KEY ("ID", "Rank")
)
WITH (
  OIDS=FALSE
);
-- ALTER TABLE test OWNER TO postgres;
 INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (1, '{"der", "die", "das", "den", "dem", "des"}', 'art', 'Der Mann küsst die Frau und das Kind schaut zu', '{"the", "of the" }');


 INSERT INTO test("ID", "Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (2, 1, '{"der", "die", "das"}', 'pron', 'Das ist mein Fahrrad', '{"that", "those"}');

 INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (1, '{"der", "die", "das"}', 'pron', 'Die Frau, die nebenen wohnt, heißt Renate', '{"that", "who"}');

SELECT * from test; 
trev
źródło
15

W kontekście zadanego pytania i odpowiedzi na komentarz @ sereja1c, tworzenie SERIALniejawnie tworzy sekwencje, więc w powyższym przykładzie-

CREATE TABLE foo (id SERIAL,bar varchar);

CREATE TABLEtworzyłoby domyślnie sekwencję foo_id_seqdla kolumny szeregowej foo.id. Dlatego SERIAL[4 bajty] jest dobry ze względu na łatwość użycia, chyba że potrzebujesz określonego typu danych dla swojego identyfikatora.

Książę
źródło
3

Ten sposób na pewno zadziała, mam nadzieję, że pomoże:

CREATE TABLE fruits(
   id SERIAL PRIMARY KEY,
   name VARCHAR NOT NULL
);

INSERT INTO fruits(id,name) VALUES(DEFAULT,'apple');

or

INSERT INTO fruits VALUES(DEFAULT,'apple');

Możesz to sprawdzić szczegóły w następnym linku: http://www.postgresqltutorial.com/postgresql-serial/

webtechnelson
źródło
3

Od PostgreSQL 10

CREATE TABLE test_new (
    id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    payload text
);
Siergiej Wiszniewski
źródło