Найкращий спосіб отримати останню ідентифікацію, вставлену в таблицю


35

Який із них найкращий варіант, щоб отримати значення ідентичності, яку я щойно створив за допомогою вставки? Який вплив цих тверджень з точки зору продуктивності?

  1. SCOPE_IDENTITY()
  2. Функція сукупності MAX()
  3. SELECT TOP 1IdentityColumn від ТаблORDER BY IdentityColumn DESC

1
Використовуйте postgreSQL, і ви отримаєте його з полиці postgresql.org/docs/9.1/static/sql-insert.html
Євген Афанасьєв,

Опція Leftfield - якщо у таблиці є стовпець Guid і ви можете генерувати новий Guid і вставляти його в новий стовпчик під час вставки - ви можете вибрати рядок із цим Guid, щоб витягнути генерований ідентифікатор int.
niico

Відповіді:


56

Використовуйте,SCOPE_IDENTITY() якщо ви вставляєте один рядок і хочете отримати створений ідентифікатор.

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

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

SELECT SCOPE_IDENTITY();

Результат:

----
1

Використовуйте OUTPUTпункт, якщо ви вставляєте кілька рядків і вам потрібно отримати набір створених ідентифікаторів.

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

Результат:

----
2
3

і чому це найкращий швидший варіант?

Окрім продуктивності, це єдині, для яких гарантовано правильність рівня ізоляції за замовчуванням та / або для кількох користувачів. Навіть якщо ви ігноруєте аспект правильності, SQL Server зберігає вставлене значенняSCOPE_IDENTITY() пам’яті, тому, природно, це буде швидше, ніж збирання та запуск власного ізольованого запиту проти таблиці або проти системних таблиць.

Ігнорувати аспект коректності - це як сказати поштовому машинологу, що він добре виконав доставку сьогоднішньої пошти - він закінчив свій маршрут на 10 хвилин швидше, ніж середній час, проблема полягає в тому, що жодна пошта не була доставлена ​​до потрібного будинку.

Не використовуйте будь-що з наступного:

  • @@IDENTITY - оскільки це не можна використовувати у всіх сценаріях, наприклад, коли у таблиці зі стовпцем ідентичності є тригер, який також вставляється в іншу таблицю зі своїм стовпцем ідентичності - ви отримаєте неправильне значення назад.
  • IDENT_CURRENT()- Я тут детально розбираюсь, і коментарі також корисні для читання, але по суті, за умови одночасності, ви часто отримаєте неправильну відповідь.
  • MAX()або TOP 1- вам доведеться захищати ці два твердження серіалізаційною ізоляцією, щоб гарантувати, що отримане MAX()вами не є чужим. Це набагато дорожче, ніж просто використання SCOPE_IDENTITY().

Ці функції також виходять з ладу щоразу, коли ви вставляєте два чи більше рядків, і потрібні всі генеровані значення ідентичності - єдиним варіантом є OUTPUTпункт


Ще одне питання викликало мою пам’ять. Коли коли-небудь нам потрібно отримати останню ідентифікацію, сформовану в певній таблиці будь-яким сеансом або користувачем, єдиним правильним і найкращим способом є MAX () цього стовпця?
AA.SC

як щодо того, коли я вставляю кілька рядків у таблицю, SCOPE_IDENTITY () завжди повертає останню сформовану ідентичність? Що робити, якщо стовпець є первинним ключем, але не стовпець Identity?
AA.SC

@ AA.SC так, він поверне останній. Якщо це не стовпець ідентичності, ні, жодна з цих функцій не працюватиме. Звідки береться значення PK у такому випадку?
Аарон Бертран

Я бачив це для стовпця в нашій програмі, де стовпець типу INT, а розробники використовують MAX (ім'я стовпця) +1, коли їм потрібно вставити новий запис
AA.SC

Тоді вони вже знають, яке значення вони щойно вставили. SQL Server не має ніякого способу сказати вам про це (ви не можете розраховувати на те, щоб витягнути MAX знову після цього факту, якщо ви повністю не виділили всю свою транзакцію, що не буде корисним для продуктивності чи одночасності).
Аарон Бертран

7

Крім продуктивності, всі вони мають досить різні значення.

SCOPE_IDENTITY()дасть вам останнє значення ідентичності, вставлене у будь-яку таблицю безпосередньо в поточному діапазоні (область = пакет, збережена процедура тощо, але не в межах, скажімо, тригера, який був запущений поточним діапазоном).

IDENT_CURRENT()дасть вам останнє значення ідентичності, вставлене у конкретну таблицю з будь-якої області, будь-яким користувачем.

@@IDENTITYдає останнє значення ідентичності, згенерованого останнім оператором INSERT для поточного з'єднання, незалежно від таблиці чи області застосування. (Бічна примітка: Access використовує цю функцію, і тому виникають деякі проблеми з тригерами, які вставляють значення в таблиці з стовпцями ідентичності.)

Використання MAX()або TOP 1може дати вам абсолютно неправильні результати, якщо в таблиці є крок негативної ідентичності, або у неї були вставлені рядки з SET IDENTITY_INSERTпрограванням. Ось сценарій, що демонструє все це:

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

Резюме: палиця SCOPE_IDENTITY(), IDENT_CURRENT()або @@IDENTITY, і переконайтеся , що ви використовуєте той , який повертає то , що ви на самому справі потрібно.


1
Чому ви заохочуєте використання IDENT_CURRENT()та @@IDENTITYколи ваш власний сценарій демонструє, що вони дають неправильні результати?
Аарон Бертран

1
@AaronBertrand Я не впевнений, що я слідкую за цим. Останнє згенероване значення ідентичності було 8998 (зауважте, крок -1), і ось що IDENT_CURRENT()повертається. MAX () ніколи не повертає потрібне значення за межами першого рядка, оскільки id рахується назад, а з IDENTITY_INSERTon, 9005 не є генерованим значенням ідентичності, таким чином, не відображається IDENT_CURRENT(). Але це може призвести до "неправильних" результатів, якщо ви дійсно після того, що SCOPE_IDENTITY()повертається. Виберіть правильний інструмент для роботи.
db2

ОП, здається, відповідає ідентифікаційному значенню, яке вони вставили - у цьому випадку 8998 невірно. Крайові випадки, про які ви згадуєте ( наріст назад і IDENTITY_INSERT увімкнено), ще більше заперечують проти використання IDENT_CURRENT, на мою думку, і @@ IDENTITY ніколи не слід реально використовувати через небезпеку запуску (зараз або додано пізніше). Я все ще намагаюся зрозуміти, чому саме IDENT_CURRENT буде тим, який хотів би використовувати ОП (особливо за умови одночасності), або чому @@ IDENTITY коли-небудь буде використаний будь-коли, коли існують набагато більш надійні методи.
Аарон Бертран

@AaronBertrand З питання не на 100% зрозуміло, що бажаний результат - це остання вставка з поточної області (варіант 1 відрізняється від 2 і 3 в цьому відношенні), тому я подумав, що було б хорошою ідеєю описати і те, і як вони відрізняються. Але я погоджуюся, що @@IDENTITYмайже ніколи не є ідеальним способом отримання генерованих цінностей ідентичності. Головне в тому , що MAX()або TOP 1, як менш надійний варіант IDENT_CURRENT(), який є прекрасно функцією використання , якщо ви розумієте , що це робить. Може бути корисним для робіт з обслуговування чи чогось іншого.
db2
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.