Najlepszy sposób na wstawienie ostatniej tożsamości do tabeli

35

Która z nich jest najlepszą opcją uzyskania wartości tożsamości, którą właśnie wygenerowałem przez wstawkę? Jaki jest wpływ tych stwierdzeń na wydajność?

  1. SCOPE_IDENTITY()
  2. Funkcja agregująca MAX()
  3. WYBIERZ TOP 1IdentityColumn FROM TableNameORDER BY IdentityColumn DESC
AA.SC
źródło
1
Użyj postgreSQL, a otrzymasz go z półki postgresql.org/docs/9.1/static/sql-insert.html
Jewgienij Afanasyjew
Opcja Leftfield - jeśli masz kolumnę Guid w tabeli i możesz wygenerować nowego Guid i wstawić go do nowej kolumny podczas wstawiania - możesz następnie wybrać wiersz z tym Guid, aby wyciągnąć wygenerowaną tożsamość int.
niico

Odpowiedzi:

56

Użyj,SCOPE_IDENTITY() jeśli wstawiasz pojedynczy wiersz i chcesz odzyskać wygenerowany identyfikator.

CREATE TABLE #a(identity_column INT IDENTITY(1,1), x CHAR(1));

INSERT #a(x) VALUES('a');

SELECT SCOPE_IDENTITY();

Wynik:

----
1

Użyj OUTPUTklauzuli, jeśli wstawiasz wiele wierszy i potrzebujesz pobrać zestaw wygenerowanych identyfikatorów.

INSERT #a(x) 
  OUTPUT inserted.identity_column 
  VALUES('b'),('c');

Wynik:

----
2
3

i dlaczego jest to najlepsza szybsza opcja?

Pomijając wydajność, są to jedyne, które z pewnością są poprawne na domyślnym poziomie izolacji i / lub z wieloma użytkownikami. Nawet jeśli zignorujesz aspekt poprawności, SQL Server przechowuje wstawioną wartość w SCOPE_IDENTITY()pamięci, więc naturalnie będzie to szybsze niż uruchomienie i uruchomienie własnego izolowanego zapytania względem tabeli lub tabel systemowych.

Ignorowanie aspektu poprawności jest jak mówienie listonoszowi, że wykonał dobrą robotę dostarczając dzisiejszą pocztę - zakończył swoją trasę o 10 minut szybciej niż przeciętny czas, problem polega na tym, że żadna poczta nie została dostarczona do właściwego domu.

Nie używaj żadnego z poniższych:

  • @@IDENTITY - ponieważ nie można tego użyć we wszystkich scenariuszach, na przykład gdy tabela z kolumną tożsamości ma wyzwalacz, który wstawia również do innej tabeli z własną kolumną tożsamości - odzyskasz niewłaściwą wartość.
  • IDENT_CURRENT()- Wchodzę tutaj szczegółowo w to , a komentarze są również przydatne do czytania, ale zasadniczo, w przypadku współbieżności, często otrzymasz błędną odpowiedź.
  • MAX()lub TOP 1- musisz zabezpieczyć te dwa oświadczenia za pomocą szeregowalnej izolacji, aby mieć pewność, że MAX()nie dostaniesz się od kogoś innego. Jest to o wiele droższe niż zwykłe używanie SCOPE_IDENTITY().

Te funkcje również zawodzą, gdy wstawisz dwa lub więcej wierszy i potrzebujesz wygenerować wszystkie wartości tożsamości - twoją jedyną opcją jest OUTPUTklauzula.

Aaron Bertrand
źródło
Jeszcze jedno pytanie wywołało w mojej pamięci. Kiedy kiedykolwiek musimy uzyskać ostatnią tożsamość wygenerowaną w określonej tabeli przez dowolną sesję lub użytkownika, jedynym poprawnym i najlepszym sposobem jest MAX () w tej kolumnie?
AA.SC,
co powiesz na to, że kiedy wstawię wiele wierszy w tabeli, SCOPE_IDENTITY () zawsze zwróci ostatnią wygenerowaną tożsamość? Co jeśli kolumna jest kluczem podstawowym, ale nie kolumna tożsamości?
AA.SC
@ AA.SC tak, zwróci ostatni. Jeśli nie jest to kolumna tożsamości, nie, żadna z tych funkcji nie będzie działać. Skąd w tym przypadku bierze się wartość PK?
Aaron Bertrand
Widziałem to dla kolumny w naszej aplikacji, w której kolumna jest typu INT, a programiści używają MAX (nazwa kolumny) +1, gdy tylko chcą wstawić nowy rekord
AA.SC
Wtedy już wiedzą, jaką wartość właśnie wstawili. SQL Server nie ma żadnego sposobu, aby ci to powiedzieć (nie możesz polegać na ponownym ściągnięciu MAX po tym fakcie, chyba że całkowicie wyodrębniłeś całą transakcję, co nie będzie dobre dla wydajności lub współbieżności).
Aaron Bertrand
7

Oprócz wydajności wszystkie mają raczej różne znaczenia.

SCOPE_IDENTITY()poda ostatnią wartość tożsamości wstawioną do dowolnej tabeli bezpośrednio w bieżącym zakresie (zakres = partia, procedura składowana itp., ale nie w ramach, powiedzmy, wyzwalacza uruchomionego przez bieżący zakres).

IDENT_CURRENT()poda ostatnią wartość tożsamości wstawioną do określonej tabeli z dowolnego zakresu przez dowolnego użytkownika.

@@IDENTITYdaje ostatnią wartość tożsamości wygenerowaną przez najnowszą instrukcję INSERT dla bieżącego połączenia, niezależnie od tabeli lub zakresu. (Uwaga dodatkowa: Access korzysta z tej funkcji, a zatem ma pewne problemy z wyzwalaczami, które wstawiają wartości do tabel z kolumnami tożsamości).

Użycie MAX()lub TOP 1może dać całkowicie błędne wyniki, jeśli tabela ma negatywny krok identyczności lub w wierszu wstawiono wiersze SET IDENTITY_INSERT. Oto skrypt demonstrujący wszystkie te:

CREATE TABLE ReverseIdent (
    id int IDENTITY(9000,-1) NOT NULL PRIMARY KEY CLUSTERED,
    data char(4)
)

INSERT INTO ReverseIdent (data)
VALUES ('a'), ('b'), ('c')

SELECT * FROM ReverseIdent

SELECT IDENT_CURRENT('ReverseIdent') --8998
SELECT MAX(id) FROM ReverseIdent --9000

SET IDENTITY_INSERT ReverseIdent ON

INSERT INTO ReverseIdent (id, data)
VALUES (9005, 'd')

SET IDENTITY_INSERT ReverseIdent OFF

SELECT IDENT_CURRENT('ReverseIdent') --8998
SELECT MAX(id) FROM ReverseIdent --9005

Podsumowanie: kij z SCOPE_IDENTITY(), IDENT_CURRENT()lub @@IDENTITY, i upewnij się, że używasz ten, który powraca co rzeczywiście potrzebne.

db2
źródło
1
Dlaczego zachęcasz do korzystania z własnego skryptu IDENT_CURRENT()i @@IDENTITYkiedy pokazuje on, że wyniki są niepoprawne?
Aaron Bertrand
1
@AaronBertrand Nie jestem pewien, czy śledzę. Ostatnia wygenerowana wartość tożsamości to 8998 (zauważ, że krok to -1) i to właśnie IDENT_CURRENT()zwraca. MAX () nigdy nie zwraca poprawnej wartości poza pierwszy wiersz, ponieważ id odlicza wstecz, a przy IDENTITY_INSERTon, 9005 nie jest wygenerowaną wartością tożsamości, dlatego nie jest odzwierciedlona przez IDENT_CURRENT(). Ale może zwracać „niepoprawne” wyniki, jeśli naprawdę chcesz uzyskać SCOPE_IDENTITY()zwrot. Wybierz odpowiednie narzędzie do pracy.
db2
Wygląda na to, że OP jest po wartości, którą wstawili - w tym przypadku 8998 jest niepoprawny. Przypadki, o których wspominasz (przyrost wstecz i IDENTITY_INSERT włączony) jeszcze bardziej przemawiają przeciwko używaniu IDENT_CURRENT w mojej opinii, a @@ IDENTITY nigdy nie powinno być nigdy używane z powodu niebezpieczeństwa wyzwalaczy (teraz lub dodanych później). Wciąż staram się zrozumieć, dlaczego IDENT_CURRENT byłby tym, którego OP chciałby używać (zwłaszcza w ramach współbieżności) lub dlaczego @@ IDENTITY miałby być kiedykolwiek używany przez kogokolwiek, skoro istnieją znacznie bardziej niezawodne metody.
Aaron Bertrand
@AaronBertrand Nie jest w 100% jasne, że pożądanym rezultatem jest ostatnia wstawka z obecnego zakresu (opcja 1 różni się od 2 i 3 pod tym względem), więc pomyślałem, że dobrym pomysłem byłoby opisanie obu i tego, w jaki sposób różnić się. Ale zgadzam się, że @@IDENTITYprawie nigdy nie jest to idealny sposób na uzyskanie wygenerowanych wartości tożsamości. Głównym punktem jest to, że MAX()albo TOP 1są jakby mniej wiarygodne wersji IDENT_CURRENT(), która jest perfekcyjnie funkcja do użycia jeśli rozumiesz, co robi. Może być przydatny do prac konserwacyjnych lub czegoś takiego.
db2