Die PostgreSQL 10-Identitätsspalte erhält beim Einfügen mehrerer Zeilen mit dem Standardschlüsselwort den Wert „Null“

7

Ich habe kürzlich ein Upgrade von PostgreSQL 9.5 auf PostgreSQL 10 durchgeführt. Eine der raffinierten Funktionen in PostgreSQL 10 ist der neue Identitätsspaltentyp , eine Alternative zum seriellen Pseudotyp vom PostgreSQL -Typ. Die offizielle Dokumentation zur Identitätsspalte finden Sie auf der CREATE TABLESeite.

Wenn Sie jedoch mehrere Zeilen in eine Tabelle mit einer GENERATED BY DEFAULT AS IDENTITYSpalte einfügen und das Schlüsselwort verwenden DEFAULT, um den nächsten ID-Wert abzurufen, wird der Standardwert als zurückgegeben null.

Nehmen wir zum Beispiel an, ich habe einen Tisch

CREATE TABLE test (
  id int GENERATED BY DEFAULT AS IDENTITY,
  t text
);
CREATE TABLE

Das Einfügen einer einzelnen Zeile mit dem DEFAULTSchlüsselwort scheint gut zu funktionieren.

INSERT INTO test (id, t) VALUES (DEFAULT, 'a');
INSERT 0 1

Das Einfügen mehrerer Zeilen funktioniert nicht.

INSERT INTO test (id, t) VALUES (DEFAULT, 'b'), (DEFAULT, 'c');
ERROR:  null value in column "id" violates not-null constraint
DETAIL:  Failing row contains (null, b).

Das Einfügen mehrerer Zeilen mit einem impliziten Standard funktioniert ebenfalls.

INSERT INTO test (t) VALUES ('d'), ('e');
INSERT 0 2

Das oben angegebene Problem scheint bei Verwendung des SERIALSpaltenpseudotyps nicht vorhanden zu sein .

CREATE TABLE test2 (
  id SERIAL,
  t text
);
CREATE TABLE

INSERT INTO test2 (id, t) VALUES (DEFAULT, 'a'), (DEFAULT, 'b');
INSERT 0 2

Meine Frage ist also: Vermisse ich etwas? Wird DEFAULTnicht erwartet, dass das Schlüsselwort mit der neuen Identitätsspalte funktioniert ? Oder ist das ein Fehler?

danwoz
quelle

Antworten:

4

Dies ist in der Tat ein Fehler. Ich habe es überprüft. Ich ging, um zu sehen, ob es eingereicht wurde und es scheint, dass es bereits ist. Es ist nicht nur abgelegt, das Commit ist da.

Sie können ihren Test genau wie Ihren sehen

+-- VALUES RTEs
+INSERT INTO itest3 VALUES (DEFAULT, 'a');
+INSERT INTO itest3 VALUES (DEFAULT, 'b'), (DEFAULT, 'c');
+SELECT * FROM itest3;

Warten Sie also, es ist für PostgreSQL 10.2 da.

Mögliche Umgehung für PostgreSQL <10.2

Wenn Sie dies unbedingt haben müssen und die Verwendung der impliziten Spalte nicht akzeptabel ist. Eine einfache Lösung wäre, die Sequenz mit der Kataloginfofunktion abzurufen

pg_get_serial_sequence(table_name, column_name) 

Was meiner Meinung nach funktionieren sollte , und das als Standard festzulegen.

ALTER TABLE ONLY test
  ALTER COLUMN id
  DEFAULT nextval('seqname');
Evan Carroll
quelle
2
Vielen Dank, dass Sie überprüft haben, ob es bereits abgelegt (und festgeschrieben) wurde. Mein Plan ist es, Sequenzen bei Bedarf bis zum 10.2.
Danwoz
4

Es ist ein Fehler, wie @Evan herausgefunden hat. Bis er behoben ist, müssen wir ihn umgehen.

Wie Sie wahrscheinlich wissen, ist es einfach, den Fall für mehrere Spalteneinfügungen zu umgehen, bei denen nicht alle Werte vorhanden sind. defaultLassen Sie einfach die Spalte (n) weg, in die der defaultWert vollständig eingefügt werden soll. Dies defaultist die Standardeinstellung :

INSERT INTO test (t) VALUES ('b'), ('c');
SELECT * FROM test;
id | t
-: | : -
 1 | b
 2 | c

dbfiddle hier

Wenn mehrere Spalten vorhanden sind und alle standardmäßig sein sollen, können Sie weiterhin einen CTE verwenden :

WITH w AS (INSERT INTO test (t) VALUES (DEFAULT) RETURNING t )
INSERT INTO test (t) SELECT w.t FROM w CROSS JOIN generate_series(1, 1);
SELECT * FROM test;
id | t  
-: | : -
 1 | foo
 2 | foo

dbfiddle hier

Leider gibt es keine einfache Problemumgehung, wenn alle Spalten gefüllt werden müssen mit default:

Für den Fall, dass es nur eine Spalte gibt und es sich um eine serielle handelt, sehe ich keine Möglichkeit, die Standardeinstellung zu verwenden.

Jack sagt, versuchen Sie es mit topanswers.xyz
quelle