Czy funkcja LAST_INSERT_ID () w MySql jest poprawna?

36

Kiedy robię pojedynczy wiersz INSERTdo tabeli, która ma AUTO_INCREMENTkolumnę, chciałbym użyć LAST_INSERT_ID()funkcji, aby zwrócić nową AUTO_INCREMENTwartość ed zapisaną dla tego wiersza.

Ponieważ wielu programistów i administratorów Microsoft SQL Server jest bez wątpienia świadomych równoważnej funkcjonalności SQL Server ( SCOPE_IDENTITYi @@IDENTITY) nie obyło się bez problemów .

Wiem, że dokumentacja MySQL:

Wygenerowany identyfikator jest przechowywany na serwerze na podstawie połączenia . Oznacza to, że wartość zwrócona przez funkcję do danego klienta jest pierwszą AUTO_INCREMENTwartością wygenerowaną dla najnowszej instrukcji wpływającej na AUTO_INCREMENTkolumnę przez tego klienta . Na tę wartość nie mogą wpływać inni klienci, nawet jeśli generują AUTO_INCREMENTwłasne wartości. Takie zachowanie zapewnia, że ​​każdy klient może odzyskać swój własny identyfikator bez obawy o aktywność innych klientów i bez potrzeby blokowania lub transakcji.

(źródło)

a nawet posunąć się do stwierdzenia:

Jednoczesne używanie LAST_INSERT_ID()i AUTO_INCREMENTkolumn wielu klientów jest całkowicie poprawne.

(źródło)

Czy są znane zagrożenia lub scenariusze, które mogą spowodować, że LAST_INSERT_ID()nie zwrócą prawidłowej wartości?

Używam MySQL 5.5 na CentOS 5.5 x64 i Fedora 16 x64 oraz silnik InnoDB.

Kev
źródło

Odpowiedzi:

35

Kilka zastrzeżeń, na które chciałbym zwrócić uwagę podczas używania LAST_INSERT_ID:

  1. Wiem, że wspomniałeś o wkładkach jednorzędowych. Ale podczas wstawiania wielu wierszy LAST_INSERT_ID()zwraca wartość pierwszego wstawionego wiersza (nie ostatniego).

  2. Jeśli wstawianie nie powiedzie się, LAST_INSERT_ID()będzie niezdefiniowane. To samo dotyczy automatycznego wycofywania transakcji (z powodu błędów).

  3. Jeśli wstawisz transakcję, która się powiedzie, a nadal wystawisz ROLLBACK, LAST_INSERT_ID()pozostanie tak jak przed wycofaniem.

  4. Istnieje kilka zastrzeżeń dotyczących używania AUTO_INCREMENTi LAST_INSERT_IDreplikacji opartej na instrukcjach. Pierwszym z nich jest użycie w wyzwalaczu lub funkcji. Drugi to rzadziej spotykany scenariusz, w którym kolumna auto_increment jest częścią złożonego klucza podstawowego i nie jest pierwszą kolumną w kluczu.

Derek Downey
źródło
7

Aby rozwinąć punkt 2 w odpowiedzi udzielonej przez DTest:

Na wersji MySQL, które użyłem, to jest dobry pomysł, aby jawnie zresetować wartość LAST_INSERT_ID przed każdym bloku kodu, w którym planuje się wykonać wkładkę.

Można to zrobić w następujący sposób:

-- initialize the LAST_INSERT_ID to some flag value:
SELECT LAST_INSERT_ID( some_flag_init_value_of_your_choice );
-- perform the insert  
INSERT INTO ttt (ccc) VALUES (vvv);
-- retrieve the id of the inserted row:  
SELECT LAST_INSERT_ID();

Po wykonaniu powyższej serii instrukcji będziesz wiedział, czy wstawka miała jakikolwiek wpływ, sprawdzając, czy LAST_INSERT_ID nadal było ustawione na „some_flag_init_value_of_your_choice” na końcu wykonania.

W przeciwnym razie możesz zakończyć się następującą problematyczną sytuacją:

INSERT INTO ttt ( ccc ) VALUES ( 'a' );    -- assume this succeeds.
SELECT LAST_INSERT_ID();                   -- this will return the unique id of the new row with value 'a'.
INSERT INTO ttt ( ccc ) VALUES ( 'b' );    -- assume this FAILS.
SELECT LAST_INSERT_ID();                   -- this will STILL RETURN the unique id of the row with 'a'.

Ponieważ druga wstawka nie powiodła się , być może spodziewałeś się, że drugie wywołanie LAST_INSERT_ID zwróci NULL lub wygeneruje pusty zestaw wyników (zero wierszy). Fakt, że nadal zwraca poprawny identyfikator liczby całkowitej, może wprowadzić cię w błąd, że pomyślałeś, że druga wstawka SUKCEEDOWAŁA, gdy tego nie zrobiła.

Rzeczy stają się NAWET WEIRDER, gdy weźmiesz pod uwagę, że LAST_INSERT_ID będzie nadal zachowywał i powtarzał ostatni udany unikatowy identyfikator, nawet jeśli kolejne błędne instrukcje wstawiania są kierowane na inne tabele niż tabela, która wygenerowała ostatni udany unikalny identyfikator. Innymi słowy, wstawiasz do tabeli TA i dostajesz id 5, następnie wstawiasz do TB (ale to się nie udaje), ale nadal widzisz 5. Na tej podstawie myślisz , że właśnie utworzyłeś nowy wiersz w TA o id 5 i nowym wierszu w TB o id 5, podczas gdy w rzeczywistości albo nie ma żadnego wiersza w TB o id 5, albo istnieje taki wiersz, ale tak naprawdę nie ma on nic wspólnego z żadnym kodem, który właśnie biegł.

pestofagiczny
źródło
2
Po pierwsze, nie powinieneś używać istnienia, last_insert_id()aby ocenić, czy zapytanie zakończyło się powodzeniem. W końcu jest to ostatni wstawiony identyfikator, zawierający wartości, których potrzebujesz, gdy już wiesz, że ci się udało.
Pacerier