@@IDENTITY: примеры (SQL)

Работа с функцией @@IDENTITY в SQL Server на примерах
Раздел: Функции управления системой
@@IDENTITY: numeric(38,0)

Функция @@IDENTITY в MS SQL Server

Функция @@IDENTITY является системной и возвращает последнее значение идентификатора (IDENTITY), сгенерированное в рамках текущего сеанса соединения с сервером. Она применяется после выполнения инструкций INSERT, SELECT INTO или bulk copy, которые добавляют данные в таблицу со столбцом IDENTITY.

Функция не принимает аргументов. Возвращаемое значение имеет тип данных numeric(38,0). Если в течение сеанса не было сгенерировано ни одного значения IDENTITY, функция возвращает NULL. Ключевая особенность — функция возвращает последнее значение, созданное для любой таблицы в текущем сеансе, даже если оно было создано триггером, что иногда приводит к непредвиденным результатам.

Простые примеры использования

Пример вставки строки и получения сгенерированного идентификатора:

CREATE TABLE TestTable (ID INT IDENTITY(1,1), Name NVARCHAR(50));
INSERT INTO TestTable (Name) VALUES ('Пример 1');
SELECT @@IDENTITY AS 'Последний ID';
Последний ID
------------
1

Пример, демонстрирующий получение значения после нескольких вставок:

INSERT INTO TestTable (Name) VALUES ('Пример 2'), ('Пример 3');
SELECT @@IDENTITY AS 'Последний ID';
Последний ID
------------
3

Если вставка не затрагивает столбцы IDENTITY, функция возвращает NULL:

CREATE TABLE TableNoIdentity (Data NVARCHAR(50));
INSERT INTO TableNoIdentity (Data) VALUES ('Тест');
SELECT @@IDENTITY AS 'Последний ID';
Последний ID
------------
NULL

Альтернативные функции в MS SQL

В MS SQL Server существуют другие функции для работы со сгенерированными идентификаторами:

  • SCOPE_IDENTITY(): возвращает последнее значение IDENTITY, созданное в той же области видимости (scope). Область видимости — это текущий модуль (хранимая процедура, триггер, функция, пакет). Это самый надежный выбор для большинства сценариев, так как не зависит от действий триггеров.
  • IDENT_CURRENT('имя_таблицы'): возвращает последнее значение IDENTITY, сгенерированное для конкретной таблицы в любом сеансе и области видимости. Полезно, когда требуется узнать последнее значение, вставленное в таблицу другими процессами.

Предпочтительнее использовать SCOPE_IDENTITY(), чтобы избежать непреднамеренного получения значения из триггера. Функцию @@IDENTITY применяют в простых случаях, когда уверены в отсутствии триггеров или когда именно такое поведение требуется.

Аналоги в других СУБД

В других системах управления базами данных существуют схожие механизмы:

MySQL: функция LAST_INSERT_ID(). Возвращает первое автоматически сгенерированное значение, если инструкция INSERT вставляла несколько строк.

INSERT INTO users (username) VALUES ('user1');
SELECT LAST_INSERT_ID();
LAST_INSERT_ID()
----------------
101

PostgreSQL: используется предложение RETURNING в запросе INSERT или функция currval() для последовательности.

INSERT INTO products (name) VALUES ('Товар') RETURNING id;
 id
----
 55

Oracle: последовательности (SEQUENCE). Для получения последнего значения применяют sequence_name.CURRVAL в пределах текущего сеанса, но только после вызова NEXTVAL.

INSERT INTO employees (id, name) VALUES (emp_seq.NEXTVAL, 'Иван');
SELECT emp_seq.CURRVAL FROM dual;
CURRVAL
-------
   1234

SQLite: функция last_insert_rowid(). Возвращает ROWID последней вставленной строки.

INSERT INTO logs (message) VALUES ('Событие');
SELECT last_insert_rowid();
last_insert_rowid()
-------------------
5

Sybase ASE: как и в MS SQL, доступна функция @@identity с похожей семантикой.

Распространенные ошибки

Основная ошибка — игнорирование влияния триггеров. Если после вставки в целевую таблицу срабатывает триггер, который вставляет данные в другую таблицу с IDENTITY, @@IDENTITY вернет значение из второй таблицы.

CREATE TABLE MainTable (ID INT IDENTITY(1,1), Data NVARCHAR(50));
CREATE TABLE LogTable (LogID INT IDENTITY(100,1), Action NVARCHAR(50));
GO
CREATE TRIGGER MainTable_Ins ON MainTable AFTER INSERT AS
    INSERT INTO LogTable (Action) VALUES ('Insert fired');
GO
INSERT INTO MainTable (Data) VALUES ('Тест');
SELECT @@IDENTITY AS 'Что вернулось?';
Что вернулось?
--------------
100

В этом примере возвращается 100 (из LogTable), а не 1 (из MainTable). Также ошибкой является предположение, что функция возвращает значение для конкретной таблицы. Она глобальна для сеанса. Другая проблема — проверка значения без учета NULL. Если в сеансе не было операций, генерирующих IDENTITY, функция вернет NULL, что может вызвать ошибки в логике приложения.

Изменения в последних версиях

Поведение функции @@IDENTITY в современных версиях MS SQL Server (начиная с 2008 и вплоть до 2022) остается стабильным и неизменным. Основные изменения связаны не с самой функцией, а с рекомендациями по ее использованию. В официальной документации Microsoft продолжает советовать применять SCOPE_IDENTITY() или выходной параметр OUTPUT предложения INSERT для более предсказуемого и безопасного получения сгенерированных значений. Сама функция не устарела, но ее применение без понимания контекста считается потенциально рискованным.

Расширенные примеры применения

Пример использования в хранимой процедуре с несколькими вставками. Функция возвращает идентификатор последней операции, независимо от таблицы.

Пример sql
CREATE PROC InsertData @Name NVARCHAR(50) AS
BEGIN
    INSERT INTO TableA (Name) VALUES (@Name); -- IDENTITY столбец
    INSERT INTO TableB (Description) VALUES ('Лог'); -- Без IDENTITY
    DECLARE @LastID NUMERIC(38,0);
    SET @LastID = @@IDENTITY;
    SELECT @LastID AS 'Последний ID в сеансе';
END;
GO
EXEC InsertData 'Новая запись';
Последний ID в сеансе
---------------------
1

Сравнение поведения @@IDENTITY и SCOPE_IDENTITY() в одном пакете с триггером.

Пример sql
CREATE TABLE Parent (PID INT IDENTITY(10,1), PName NVARCHAR(50));
CREATE TABLE Child (CID INT IDENTITY(100,1), PID INT, CName NVARCHAR(50));
GO
CREATE TRIGGER Parent_Ins ON Parent AFTER INSERT AS
    INSERT INTO Child (PID, CName) SELECT PID, 'Child for ' + PName FROM inserted;
GO
INSERT INTO Parent (PName) VALUES ('Родитель 1');
SELECT @@IDENTITY AS '@@IDENTITY', SCOPE_IDENTITY() AS 'SCOPE_IDENTITY';
@@IDENTITY SCOPE_IDENTITY
---------- ---------------
100        10

Использование в транзакции с откатом. Функция возвращает значение, сгенерированное до отката, так как оно уже было создано в сеансе.

Пример sql
BEGIN TRANSACTION;
INSERT INTO TestTable (Name) VALUES ('В транзакции');
SELECT @@IDENTITY AS 'ID до Rollback';
ROLLBACK TRANSACTION;
SELECT @@IDENTITY AS 'ID после Rollback';
ID до Rollback
--------------
4

ID после Rollback
-----------------
4

Вставка через SELECT INTO. Функция также работает с этой инструкцией.

Пример sql
SELECT * INTO NewTable FROM TestTable WHERE 1=0; -- Создается таблица без данных, но со схемой (без IDENTITY, если не указано иначе).
-- Создадим таблицу с IDENTITY через SELECT INTO явно:
SELECT IDENTITY(INT,1,1) AS NewID, Name INTO NewTable2 FROM TestTable;
SELECT @@IDENTITY AS 'После SELECT INTO'; -- Вернет последний ID, сгенерированный при вставке, если она была.

MS SQL @@IDENTITY function comments

En
@@IDENTITY Returns the last-inserted identity value