( tl;dr
:przejdź do opcji 3:INSERT with RETURNING )
Przypomnij sobie, ż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 głównych z pseudotypem SERIAL).
Jeśli jesteś zainteresowany uzyskaniem identyfikatora nowo wstawionego wiersza, istnieje kilka sposobów:
Opcja 1:CURRVAL(<sequence name>);
.
Na przykład:
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
SELECT currval('persons_id_seq');
Nazwa ciągu musi być znana, to naprawdę dowolna; w tym przykładzie zakładamy, że tabela persons
ma id
kolumna utworzona za pomocą SERIAL
pseudotyp. Aby uniknąć polegania na tym i poczuć się bardziej czysto, 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, z tą różnicą, że nie musisz określać nazwy sekwencji:szuka najnowszej zmodyfikowanej sekwencji (zawsze w Twojej sesji, to samo zastrzeżenie jak powyżej).
Oba CURRVAL
i LASTVAL
są całkowicie bezpieczne jednocześnie. Zachowanie sekwencji w PG jest zaprojektowane tak, aby różne sesje nie przeszkadzały, więc nie ma ryzyka warunków wyścigu (jeśli inna sesja wstawi kolejny wiersz między moim INSERT i moim SELECT, nadal otrzymuję prawidłową wartość).
Jednak mają subtelny potencjalny problem. Jeśli baza danych ma jakiś TRIGGER (lub REGUŁĘ), który po wstawieniu do persons
table, wprowadza dodatkowe wstawki w innych tabelach... potem LASTVAL
prawdopodobnie da nam złą wartość. Problem może nawet wystąpić w przypadku CURRVAL
, jeśli dodatkowe wstawienia są wykonywane w tych samych persons
tabeli (jest to znacznie mniej powszechne, ale ryzyko nadal istnieje).
Opcja 3:INSERT
z RETURNING
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;
To najczystszy, najskuteczniejszy i najbezpieczniejszy sposób na uzyskanie identyfikatora. Nie ma żadnego ryzyka poprzedniego.
Wady? Prawie żaden:być może będziesz musiał zmienić sposób wywoływania instrukcji INSERT (w najgorszym przypadku być może Twoja warstwa API lub DB nie oczekuje, ż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ć ostatnio wstawiony identyfikator globalnie (niekoniecznie przez twoją sesję). W tym celu musisz skorzystać z SELECT max(id) FROM table
(oczywiście nie spowoduje to odczytania niezatwierdzonych wstawek z innych transakcji).
I odwrotnie, nigdy użyj SELECT max(id) FROM table
zamiast jednej z 3 powyższych opcji, aby uzyskać identyfikator właśnie wygenerowany przez INSERT
oświadczenie, ponieważ (poza wydajnością) nie jest to jednocześnie bezpieczne:między twoim INSERT
i twój SELECT
inna sesja mogła wstawić inny rekord.