PostgreSQL Autoincrement

578

Ich wechsle von MySQL zu PostgreSQL und habe mich gefragt, wie ich Werte automatisch erhöhen kann. Ich habe in den PostgreSQL-Dokumenten einen Datentyp "serial" gesehen, aber bei der Verwendung (in Version 8.0) treten Syntaxfehler auf.

Ian
quelle
9
Wenn Sie die Abfrage und den Fehler angeben, die Sie erhalten, kann Ihnen möglicherweise jemand sagen, was mit der Abfrage nicht stimmt.
2
Mein erster Treffer auch Mich 'und da es eine Frage ist, die genug Ansichten bekommt, um relevant zu sein, warum nicht abstimmen. PS: Es ist nicht trivial, wenn Sie nicht wissen, wie es geht.
Baash05
1
SERIAL ist die bevorzugte Wahl, wenn Ihr Client-Treiber Npgsql ist. Der Anbieter wählt intern neue Werte nach einem INSERT mit SELECT currval (pg_get_serial_sequence ('table', 'column')) aus. Dies schlägt fehl, wenn die zugrunde liegende Spalte nicht vom Typ seriell ist (z. B. numerischer Typ + explizite Sequenz)
Olivier MATROT
Nur aus Neugier ... Warum muss jemand von MySQL, das sehr gut ist, auf PostgreSql migrieren?
Villamejia
17
... was noch besser ist.
Rohmer

Antworten:

701

Ja, SERIAL ist die entsprechende Funktion.

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 ist nur ein Makro zum Erstellen einer Tabellenzeit um Sequenzen. Sie können SERIAL nicht in eine vorhandene Spalte ändern.

Trey
quelle
19
Das Zitieren des Tabellennamens ist eine wirklich schlechte Praxis
Evan Carroll
71
Das Zitieren der Tabellennamen ist eine Gewohnheit, da ich eine Datenbank mit gemischten Fallnamen geerbt habe und das Zitieren von Tabellennamen eine Voraussetzung für die Verwendung ist.
Trey
26
@Evan Carroll - Warum ist es eine schlechte Angewohnheit (nur fragen)?
Christian
27
denn es sei denn, Sie haben eine Tabelle "Table"und "table"lassen sie dann einfach nicht zitiert und kanonisieren sie table. Die Konvention ist einfach, niemals Anführungszeichen in Seite zu verwenden. Sie können, wenn Sie möchten, gemischte Fallnamen für das Erscheinungsbild verwenden, benötigen sie jedoch nicht: Funktioniert CREATE TABLE fooBar ( .. ); SELECT * FROM fooBar;wie auch SELECT * FROM foobar.
Evan Carroll
26
Per postgres doc entweder konsistent zitieren oder nicht zitieren: postgresql.org/docs/current/interactive/…
Καrτhικ
226

Sie können jeden anderen ganzzahligen Datentyp verwenden , z smallint.

Beispiel:

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;

Verwenden Sie lieber Ihren eigenen Datentyp als den seriellen Datentyp des Benutzers .

Ahmad
quelle
11
Ich würde sagen, dies ist tatsächlich die bessere Antwort, da ich damit eine Tabelle ändern konnte, die ich gerade in PostgreSQL erstellt hatte, indem ich die Spalten als Standard CREATE SEQUENCE festlegte (nach dem Lesen von postgresql.org/docs/8.1/interactive/sql-createsequence.html ). . Ich bin mir jedoch nicht ganz sicher, warum Sie den Besitzer gewechselt haben.
JayC
12
@JayC: Aus der Dokumentation : Zuletzt wird die Sequenz als "Eigentum" der Spalte markiert, sodass sie gelöscht wird, wenn die Spalte oder Tabelle gelöscht wird.
user272735
8
Warum erfindet die Postgres-Community das Schlüsselwort für die automatische Inkrementierung nicht neu?
Dr. Deo
2
@ Dr. Deo: Sie verwenden serielle statt Autoincrement-Schlüsselwort, ich weiß nicht warum :)
Ahmad
4
Es gibt auch Smallserial, wenn Sie nur einen kleineren Datentyp möchten.
Beldaz
110

Wenn Sie der bereits vorhandenen Tabelle eine Sequenz zur ID hinzufügen möchten, können Sie Folgendes verwenden:

CREATE SEQUENCE user_id_seq;
ALTER TABLE user ALTER user_id SET DEFAULT NEXTVAL('user_id_seq');
Sereja
quelle
Was ist Sequenz? Wo ist AUTO_INCREMENT?
Grün
23
@Green: AUTO_INCREMENT ist nicht Teil des SQL-Standards, sondern spezifisch für MySQL. Sequenzen sind etwas, das in PostgreSQL einen ähnlichen Job macht.
Beldaz
5
Wenn Sie 'id SERIAL' verwenden, wird automatisch eine Sequenz in PostgreSQL erstellt. Der Name dieser Sequenz ist <Tabellenname> _ <Spaltenname> _seq
Jude Niroshan
Müssen Sie nicht verwenden ALTER COLUMN user_id?
Alec
Ich habe diese Methode ausprobiert, erhalte jedoch eine Fehlermeldung: ERROR: syntax error at or near "DEFAULT"Irgendwelche Vorschläge?
Ely Fialkoff
44

Während es sieht aus wie Sequenzen das sind äquivalent zu MySQL auto_increment, gibt es einige feine , aber wichtige Unterschiede:

1. Fehlgeschlagene Abfragen erhöhen die Sequenz / Seriennummer

Die serielle Spalte wird bei fehlgeschlagenen Abfragen erhöht. Dies führt zu einer Fragmentierung von fehlgeschlagenen Abfragen, nicht nur zum Löschen von Zeilen. Führen Sie beispielsweise die folgenden Abfragen in Ihrer PostgreSQL-Datenbank aus:

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;

Sie sollten die folgende Ausgabe erhalten:

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

Beachten Sie, wie die UID von 1 auf 3 anstatt von 1 auf 2 wechselt.

Dies tritt immer noch auf, wenn Sie Ihre eigene Sequenz manuell erstellen mit:

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;

Wenn Sie testen möchten, wie sich MySQL unterscheidet, führen Sie Folgendes in einer MySQL-Datenbank aus:

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

Sie sollten ohne Fraging Folgendes erhalten :

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

2. Das manuelle Festlegen des Werts für die serielle Spalte kann dazu führen, dass zukünftige Abfragen fehlschlagen.

Dies wurde von @trev in einer früheren Antwort hervorgehoben.

Um dies manuell zu simulieren, setzen Sie die UID auf 4, was später "zusammenstößt".

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

Tabellendaten:

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

Führen Sie eine weitere Einfügung aus:

INSERT INTO table1 (col_b) VALUES(6);

Tabellendaten:

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

Wenn Sie nun eine andere Einfügung ausführen:

INSERT INTO table1 (col_b) VALUES(7);

Es wird mit der folgenden Fehlermeldung fehlschlagen:

FEHLER: Doppelter Schlüsselwert verletzt eindeutige Einschränkung "table1_pkey" DETAIL: Schlüssel (uid) = (5) existiert bereits.

Im Gegensatz dazu wird MySQL dies wie unten gezeigt ordnungsgemäß handhaben:

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

Fügen Sie nun eine weitere Zeile ein, ohne uid einzustellen

INSERT INTO table1 (col_b) VALUES(3);

Die Abfrage schlägt nicht fehl, uid springt einfach zu 5:

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

Die Tests wurden unter MySQL 5.6.33 für Linux (x86_64) und PostgreSQL 9.4.9 durchgeführt

Programmierer
quelle
10
Sie geben einen Vergleich, aber ich sehe hier keine Lösung! Ist es eine Antwort?
Anwar
4
@Anwar erweitert einfach die verschiedenen Antworten, die besagen, dass die Antwort eine Seriennummer / Sequenz verwenden soll. Dies bietet einen wichtigen Kontext, der berücksichtigt werden muss.
Programster
38

Ab Postgres 10 werden auch Identitätsspalten gemäß dem SQL-Standard unterstützt:

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

Erstellt eine Identitätsspalte, die nur überschrieben werden kann, wenn dies ausdrücklich angefordert wird. Das folgende Einfügen schlägt mit einer Spalte fehl, die wie folgt definiert ist generated always:

insert into foo (id) 
values (1);

Dies kann jedoch außer Kraft gesetzt werden:

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

Bei Verwendung der Option entspricht generated by defaultdies im Wesentlichen dem Verhalten der vorhandenen serialImplementierung:

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

Wenn ein Wert manuell angegeben wird, muss die zugrunde liegende Sequenz ebenfalls manuell angepasst werden - genau wie bei einer serialSpalte.


Eine Identitätsspalte ist standardmäßig kein Primärschlüssel (genau wie eine serialSpalte). Sollte dies der Fall sein, muss eine Primärschlüsseleinschränkung manuell definiert werden.

ein Pferd ohne Name
quelle
26

Es tut uns leid, eine alte Frage erneut aufzubereiten, aber dies war die erste Frage / Antwort zum Stapelüberlauf, die bei Google aufgetaucht ist.

In diesem Beitrag (der zuerst bei Google veröffentlicht wurde) wird die Verwendung der aktualisierten Syntax für PostgreSQL 10 beschrieben: https://blog.2ndquadrant.com/postgresql-10-identity-columns/

was zufällig ist:

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

Ich hoffe, das hilft :)

Zhao Li
quelle
1
Dies ist in der Tat der richtige Weg in PostgreSQL 10 und entspricht der Syntax anderer Datenbanksoftware wie DB2 oder Oracle.
Adria
1
@adriaan Eigentlich sind die GENERATED … AS IDENTITYBefehle Standard-SQL. Zuerst in SQL: 2003 hinzugefügt , dann in SQL: 2008 geklärt . Siehe Funktionen # T174 & F386 & T178.
Basil Bourque
16

Sie müssen darauf achten, nicht direkt in Ihr SERIAL- oder Sequenzfeld einzufügen, da sonst das Schreiben fehlschlägt, wenn die Sequenz den eingefügten Wert erreicht:

-- 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
quelle
15

Im Kontext der gestellten Frage und als Antwort auf den Kommentar von @ sereja1c werden beim SERIALimpliziten Erstellen Sequenzen erstellt.

CREATE TABLE foo (id SERIAL,bar varchar);

CREATE TABLEwürde implizit eine Sequenz foo_id_seqfür die serielle Spalte erstellen foo.id. Daher ist SERIAL[4 Bytes] für die Benutzerfreundlichkeit gut, es sei denn, Sie benötigen einen bestimmten Datentyp für Ihre ID.

Prinz
quelle
3

Dieser Weg wird sicher funktionieren, ich hoffe es hilft:

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

Sie können dies anhand der Details im nächsten Link überprüfen: http://www.postgresqltutorial.com/postgresql-serial/

Webtechnelson
quelle
3

Seit PostgreSQL 10

CREATE TABLE test_new (
    id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    payload text
);
Sergey Vishnevetskiy
quelle