PostgreSQL-Funktion für die zuletzt eingefügte ID

321

Wie kann ich in PostgreSQL die letzte ID in eine Tabelle einfügen?

In MS SQL gibt es SCOPE_IDENTITY ().

Bitte raten Sie mir nicht, so etwas zu verwenden:

select max(id) from table
Anton
quelle
Warum hasst du die Max-Funktion? Ich finde es sehr einfach. Gibt es ein Problem wie Sicherheit?
jeongmin.cha
9
@ jeongmin.cha gibt es ein Problem, wenn dazwischen andere Operationen liegen und mehr Einfügungen (gleichzeitige Operationen) bedeuten, dass sich die maximale ID geändert hat, es sei denn und bis Sie eine Sperre explizit nehmen und sie nicht freigeben
rohanagarwal
Wenn der beabsichtigte Anwendungsfall darin besteht, die zuletzt eingefügte ID als Teil des Werts einer nachfolgenden Einfügung zu verwenden, siehe diese Frage .
Flux

Antworten:

604

( tl;dr: gehe zu Option 3: EINFÜGEN mit RÜCKGABE)

Denken Sie daran, dass es in postgresql kein "id" -Konzept für Tabellen gibt, sondern nur Sequenzen (die normalerweise, aber nicht unbedingt als Standardwerte für Ersatzprimärschlüssel mit dem SERIAL- Pseudotyp verwendet werden).

Wenn Sie die ID einer neu eingefügten Zeile abrufen möchten, gibt es verschiedene Möglichkeiten:


Option 1 : CURRVAL(<sequence name>);.

Zum Beispiel:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval('persons_id_seq');

Der Name der Sequenz muss bekannt sein, er ist wirklich willkürlich; In diesem Beispiel wird davon ausgegangen, dass für die Tabelle personseine idSpalte mit dem SERIALPseudotyp erstellt wurde. Um sich nicht darauf zu verlassen und sich sauberer zu fühlen, können Sie stattdessen Folgendes verwenden pg_get_serial_sequence:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval(pg_get_serial_sequence('persons','id'));

Vorsichtsmaßnahme: Funktioniert currval()nur nach einer INSERT(ausgeführten nextval()) Sitzung in derselben Sitzung .


Option 2: LASTVAL();

Dies ähnelt dem vorherigen, nur dass Sie den Sequenznamen nicht angeben müssen: Es wird nach der zuletzt geänderten Sequenz gesucht (immer innerhalb Ihrer Sitzung, dieselbe Einschränkung wie oben).


Beide CURRVALundLASTVAL sind absolut gleichzeitig sicher. Das Verhalten der Sequenz in PG ist so konzipiert, dass andere Sitzungen nicht stören, sodass kein Risiko für Rennbedingungen besteht (wenn eine andere Sitzung eine weitere Zeile zwischen meinem INSERT und meinem SELECT einfügt, erhalte ich immer noch meinen korrekten Wert).

Sie haben jedoch ein subtiles potenzielles Problem. Wenn die Datenbank einen TRIGGER (oder eine REGEL) enthält, der beim Einfügen in eine personsTabelle einige zusätzliche Einfügungen in andere Tabellen vornimmt LASTVAL, erhalten wir wahrscheinlich den falschen Wert. Das Problem kann sogar auftreten CURRVAL, wenn die zusätzlichen Einfügungen in dieselbe personsTabelle vorgenommen werden (dies ist viel weniger üblich, aber das Risiko besteht weiterhin).


Option 3: INSERTmitRETURNING

INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;

Dies ist der sauberste, effizienteste und sicherste Weg, um die ID zu erhalten. Es hat keine der Risiken der vorherigen.

Nachteile? Fast keine: Möglicherweise müssen Sie die Art und Weise ändern, in der Sie Ihre INSERT-Anweisung aufrufen (im schlimmsten Fall erwartet Ihre API- oder DB-Schicht möglicherweise nicht, dass ein INSERT einen Wert zurückgibt). Es ist kein Standard-SQL (wen interessiert das?). es ist verfügbar seit Postgresql 8.2 (Dez 2006 ...)


Fazit: Wenn Sie können, wählen Sie Option 3. An anderer Stelle bevorzugen Sie 1.

Hinweis: Alle diese Methoden sind nutzlos, wenn Sie die zuletzt eingefügte ID global abrufen möchten (nicht unbedingt von Ihrer Sitzung). Dazu müssen Sie darauf zurückgreifen SELECT max(id) FROM table(dies liest natürlich keine nicht festgeschriebenen Einfügungen aus anderen Transaktionen).

Umgekehrt sollten Sie nie benutzen , SELECT max(id) FROM tableanstatt eine der drei oben genannten Optionen, die ID nur von Euren zu bekommen INSERTAussage, weil sie (abgesehen von Leistung) nicht gleichzeitig sicher ist: zwischen Ihrer INSERTund Ihrer SELECTanderen Sitzung möglicherweise einem anderen Datensatz eingefügt haben.

leonbloy
quelle
25
LASTVAL () kann sehr böse sein, wenn Sie einen Trigger / eine Regel hinzufügen, die Zeilen in eine andere Tabelle einfügen.
Kouber Saparev
3
SELECT max(id)erledigt den Job leider auch nicht, sobald Sie anfangen, Zeilen zu löschen.
Simon A. Eugster
2
@leonbloy Sofern ich nichts verpasst habe, wenn Sie Zeilen mit IDs haben 1,2,3,4,5und die Zeilen 4 und 5 löschen, ist die zuletzt eingefügte ID immer noch 5, gibt aber max()3 zurück.
Simon A. Eugster
9
Ein Beispiel, wie RETURNING id;man dies in eine andere Tabelle einfügt, wäre willkommen!
Olivier Pons
8
Wie kann ich RETURNING idein SQL-Skript verwenden, das dem psqlBefehlszeilentool zugeführt wird ?
Amoe
82

Siehe die RETURNING-Klausel der INSERT- Anweisung. Grundsätzlich dient INSERT auch als Abfrage und gibt Ihnen den eingefügten Wert zurück.

Kwatford
quelle
4
Funktioniert ab Version 8.2 und ist die beste und schnellste Lösung.
Frank Heikens
9
Vielleicht eine kurze Erklärung, wie man die zurückgegebene ID verwendet?
Andrew
@ Andrew Ich bin nicht sicher, ob ich die Frage verstehe. Wissen Sie nicht, wie Sie Ergebnisse aus einer Abfrage abrufen können? Das ist sprach- / bibliotheksabhängig und sollte gleich funktionieren, unabhängig davon, ob Sie eine Auswahl oder eine Rückgabe einfügen. Die einzige andere Interpretation, die ich finden kann, ist, dass Sie die ID erfolgreich aus dem Anruf abgerufen haben und nicht wissen, wofür sie bestimmt ist. In welchem ​​Fall haben Sie sie abgerufen?
Kwatford
8
@ Andrew, Wenn Sie über die psql-Befehlszeile Folgendes ausführen : insert into names (firstname, lastname) values ('john', 'smith') returning id;, gibt es einfach id aus, als ob Sie select id from names where id=$lastiddirekt ausgeführt hätten. Wenn Sie die Rückgabe in einer Variablen speichern möchten , ist insert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;die Anweisung, die die Rückgabe enthält, die letzte Anweisung in einer Funktion , und die ID, die bearbeitet wird returning, wird von der gesamten Funktion zurückgegeben.
Alexander Bird
32

Sie können die RETURNING-Klausel in der INSERT-Anweisung wie folgt verwenden

wgzhao=# create table foo(id int,name text);
CREATE TABLE
wgzhao=# insert into foo values(1,'wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into foo values(3,'wgzhao') returning id;
 id 
----
  3
(1 row)

INSERT 0 1

wgzhao=# create table bar(id serial,name text);
CREATE TABLE
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  2
(1 row)

INSERT 0 
wgzhao
quelle
28

Leonbloys Antwort ist ziemlich vollständig. Ich würde nur den Sonderfall hinzufügen, in dem der zuletzt eingefügte Wert aus einer PL / pgSQL-Funktion abgerufen werden muss, in der OPTION 3 nicht genau passt.

Zum Beispiel, wenn wir die folgenden Tabellen haben:

CREATE TABLE person(
   id serial,
   lastname character varying (50),
   firstname character varying (50),
   CONSTRAINT person_pk PRIMARY KEY (id)
);

CREATE TABLE client (
    id integer,
   CONSTRAINT client_pk PRIMARY KEY (id),
   CONSTRAINT fk_client_person FOREIGN KEY (id)
       REFERENCES person (id) MATCH SIMPLE
);

Wenn wir einen Kundendatensatz einfügen müssen, müssen wir uns auf einen Personendatensatz beziehen. Angenommen, wir möchten eine PL / pgSQL-Funktion entwickeln, die einen neuen Datensatz in den Client einfügt, sich aber auch um das Einfügen des neuen Personendatensatzes kümmert. Dafür müssen wir eine kleine Variation von Leonbloys OPTION 3 verwenden:

INSERT INTO person(lastname, firstname) 
VALUES (lastn, firstn) 
RETURNING id INTO [new_variable];

Beachten Sie, dass es zwei INTO-Klauseln gibt. Daher würde die PL / pgSQL-Funktion wie folgt definiert:

CREATE OR REPLACE FUNCTION new_client(lastn character varying, firstn character varying)
  RETURNS integer AS
$BODY$
DECLARE
   v_id integer;
BEGIN
   -- Inserts the new person record and retrieves the last inserted id
   INSERT INTO person(lastname, firstname)
   VALUES (lastn, firstn)
   RETURNING id INTO v_id;

   -- Inserts the new client and references the inserted person
   INSERT INTO client(id) VALUES (v_id);

   -- Return the new id so we can use it in a select clause or return the new id into the user application
    RETURN v_id;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

Jetzt können wir die neuen Daten einfügen mit:

SELECT new_client('Smith', 'John');

oder

SELECT * FROM new_client('Smith', 'John');

Und wir bekommen die neu erstellte ID.

new_client
integer
----------
         1
Krauss
quelle
9

Für diejenigen, die den gesamten Datensatz erhalten müssen, können Sie hinzufügen

returning *

bis zum Ende Ihrer Abfrage, um das Objekt all einschließlich der ID abzurufen.

emreoktem
quelle
8

Siehe das folgende Beispiel

CREATE TABLE users (
    -- make the "id" column a primary key; this also creates
    -- a UNIQUE constraint and a b+-tree index on the column
    id    SERIAL PRIMARY KEY,
    name  TEXT,
    age   INT4
);

INSERT INTO users (name, age) VALUES ('Mozart', 20);

Um die zuletzt eingefügte ID zu erhalten, verwenden Sie diese für die Tabelle "Benutzer".

SELECT currval(pg_get_serial_sequence('users', 'id'));
RINSON KE
quelle
1

Versuche dies:

select nextval('my_seq_name');  // Returns next value

Wenn dies 1 zurückgibt (oder was auch immer der Startwert für Ihre Sequenz ist), setzen Sie die Sequenz auf den ursprünglichen Wert zurück und übergeben Sie das falsche Flag:

select setval('my_seq_name', 1, false);

Andernfalls,

select setval('my_seq_name', nextValue - 1, true);

Dadurch wird der Sequenzwert auf den ursprünglichen Zustand zurückgesetzt und "setval" wird mit dem von Ihnen gesuchten Sequenzwert zurückgegeben.

Oozman
quelle
1

Postgres verfügt über einen integrierten Mechanismus für denselben, der in derselben Abfrage die ID oder was auch immer Sie zurückgeben möchten, zurückgibt. Hier ist ein Beispiel. Angenommen, Sie haben eine Tabelle mit 2 Spalten Spalte1 und Spalte2 erstellt und möchten, dass Spalte1 nach jeder Einfügung zurückgegeben wird.

# create table users_table(id serial not null primary key, name character varying);
CREATE TABLE
#insert into users_table(name) VALUES ('Jon Snow') RETURNING id;
 id 
----
  1
(1 row)

# insert into users_table(name) VALUES ('Arya Stark') RETURNING id;
 id 
----
  2
(1 row)
Sandip Debnath
quelle