W PostgreSQL, jak mogę wstawić ostatni identyfikator do tabeli?
W MS SQL jest SCOPE_IDENTITY ().
Proszę nie doradzać mi używania czegoś takiego:
select max(id) from table
W PostgreSQL, jak mogę wstawić ostatni identyfikator do tabeli?
W MS SQL jest SCOPE_IDENTITY ().
Proszę nie doradzać mi używania czegoś takiego:
select max(id) from table
Odpowiedzi:
( tl;dr
: goto opcja 3: WSTAWIANIE Z POWROTEM)
Przypomnijmy, że w postgresql nie ma pojęcia „id” dla tabel, tylko sekwencje (które są zwykle, ale niekoniecznie używane jako wartości domyślne dla zastępczych kluczy podstawowych, z pseudo-typem SERIAL ).
Jeśli chcesz uzyskać identyfikator nowo wstawionego wiersza, istnieje kilka sposobów:
Wariant 1: CURRVAL(<sequence name>);
.
Na przykład:
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
SELECT currval('persons_id_seq');
Nazwa sekwencji musi być znana, to naprawdę dowolne; w tym przykładzie zakładamy, że tabela persons
ma id
kolumnę utworzoną za pomocą SERIAL
pseudo-typu. Aby uniknąć polegania na tym i poczuć się czystszym, możesz zamiast tego użyć pg_get_serial_sequence
:
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
SELECT currval(pg_get_serial_sequence('persons','id'));
Zastrzeżenie: currval()
działa tylko po INSERT
(który wykonał nextval()
), w tej samej sesji .
Opcja 2: LASTVAL();
Jest to podobne do poprzedniego, tyle że nie musisz określać nazwy sekwencji: szuka najnowszej zmodyfikowanej sekwencji (zawsze w sesji, z tym samym zastrzeżeniem jak powyżej).
Zarówno CURRVAL
i LASTVAL
są całkowicie zbieżne bezpieczne. Zachowanie sekwencji w PG jest zaprojektowane tak, aby inna sesja nie przeszkadzała, więc nie ma ryzyka warunków wyścigu (jeśli inna sesja wstawi kolejny wiersz między moim INSERT a moim SELECT, nadal otrzymuję swoją prawidłową wartość).
Jednak oni mają subtelny potencjalny problem. Jeśli baza danych ma TRIGGER (lub ZASADĘ), która po wstawieniu do persons
tabeli powoduje dodatkowe wstawienia w innych tabelach LASTVAL
, prawdopodobnie poda nam niewłaściwą wartość. Problem może się nawet zdarzyć CURRVAL
, jeśli dodatkowe wstawienia zostaną wykonane w tej samej persons
tabeli (jest to znacznie mniej normalne, ale ryzyko nadal istnieje).
Opcja 3: INSERT
zRETURNING
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;
Jest to najbardziej czysty, wydajny i bezpieczny sposób na uzyskanie identyfikatora. Nie ma żadnego ryzyka z poprzedniego.
Wady Prawie żaden: może być konieczna modyfikacja sposobu wywoływania instrukcji INSERT (w najgorszym przypadku warstwa API lub DB nie spodziewa się, że INSERT zwróci wartość); to nie jest standardowy SQL (kogo to obchodzi); jest dostępny od Postgresql 8.2 (grudzień 2006 ...)
Wniosek: jeśli możesz, wybierz opcję 3. W innym miejscu, wybierz 1.
Uwaga: wszystkie te metody są bezużyteczne, jeśli zamierzasz uzyskać ostatni wstawiony identyfikator globalnie (niekoniecznie przez twoją sesję). W tym celu należy skorzystać SELECT max(id) FROM table
(oczywiście nie odczyta to nieprzypisanych wstawek z innych transakcji).
I odwrotnie, nigdy nie powinieneś używać SELECT max(id) FROM table
jednej z 3 powyższych opcji, aby uzyskać identyfikator wygenerowany właśnie przez twoją INSERT
instrukcję, ponieważ (oprócz wydajności) nie jest to jednocześnie bezpieczne: między twoją INSERT
a SELECT
inną sesją mogło wstawić inny rekord.
SELECT max(id)
niestety nie wykonuje zadania, gdy tylko zaczniesz usuwać wiersze.
1,2,3,4,5
i usuwasz wiersze 4 i 5, ostatni wstawiony identyfikator to wciąż 5, ale max()
zwraca 3.
RETURNING id;
aby wstawić to do innej tabeli!
RETURNING id
z skryptu SQL dostarczanego do psql
narzędzia wiersza poleceń?
Zobacz klauzulę RETURNING instrukcji INSERT . Zasadniczo INSERT pełni również funkcję zapytania i zwraca wartość, która została wstawiona.
insert into names (firstname, lastname) values ('john', 'smith') returning id;
to po prostu wyświetla id tak, jakbyś działał select id from names where id=$lastid
bezpośrednio. Jeśli chcesz zapisać zwrot w zmiennej , to insert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;
jeśli instrukcja zawierająca zwracaną wartość jest ostatnią instrukcją w funkcji , wówczas identyfikator, który returning
jest zwracany, jest zwracany przez funkcję jako całość.
możesz użyć klauzuli RETURNING w instrukcji INSERT, podobnie jak poniżej
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
Odpowiedź Leonbloya jest dość kompletna. Dodałbym tylko specjalny przypadek, w którym trzeba uzyskać ostatnią wstawioną wartość z funkcji PL / pgSQL, gdzie OPCJA 3 nie pasuje dokładnie.
Na przykład, jeśli mamy następujące tabele:
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
);
Jeśli musimy wstawić rekord klienta, musimy odwołać się do rekordu osoby. Ale powiedzmy, że chcemy opracować funkcję PL / pgSQL, która wstawia nowy rekord do klienta, ale także dba o wstawienie rekordu nowej osoby. W tym celu musimy użyć niewielkiej odmiany OPCJI 3 firmy leonbloy:
INSERT INTO person(lastname, firstname)
VALUES (lastn, firstn)
RETURNING id INTO [new_variable];
Zauważ, że istnieją dwie klauzule INTO. Dlatego funkcję PL / pgSQL można zdefiniować następująco:
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;
Teraz możemy wstawić nowe dane za pomocą:
SELECT new_client('Smith', 'John');
lub
SELECT * FROM new_client('Smith', 'John');
I otrzymujemy nowo utworzony identyfikator.
new_client
integer
----------
1
Zobacz poniższy przykład
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);
Następnie, aby uzyskać ostatni wstawiony identyfikator, użyj tego dla tabeli „user” seq nazwa kolumny „id”
SELECT currval(pg_get_serial_sequence('users', 'id'));
SELECT CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))
Musisz oczywiście podać nazwę tabeli i nazwę kolumny.
Dotyczy to bieżącej sesji / połączenia http://www.postgresql.org/docs/8.3/static/functions-sequence.html
Spróbuj tego:
select nextval('my_seq_name'); // Returns next value
Jeśli to zwróci 1 (lub cokolwiek to jest wartość_początkowa dla sekwencji), następnie zresetuj sekwencję z powrotem do pierwotnej wartości, przekazując flagę false:
select setval('my_seq_name', 1, false);
Inaczej,
select setval('my_seq_name', nextValue - 1, true);
Spowoduje to przywrócenie wartości sekwencji do pierwotnego stanu, a „setval” powróci z poszukiwaną wartością sekwencji.
Postgres ma wbudowany mechanizm dla tego samego, który w tym samym zapytaniu zwraca id lub cokolwiek, co chcesz zwrócić zapytanie. Oto przykład. Załóżmy, że masz utworzoną tabelę z 2 kolumnami kolumna 1 i kolumna 2 i chcesz, aby kolumna 1 była zwracana po każdej wstawce.
# 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)
Miałem ten problem z Javą i Postgres. Naprawiłem to, aktualizując nową wersję Connector-J.
postgresql-9.2-1002.jdbc4.jar
https://jdbc.postgresql.org/download.html : Wersja 42.2.12