SQL Server - возвращаемое значение после INSERT


318

Я пытаюсь вернуть значение ключа после оператора INSERT. Пример: у меня есть таблица с атрибутами name и id. id является сгенерированным значением.

    INSERT INTO table (name) VALUES('bob');

Теперь я хочу вернуть идентификатор на том же этапе. Как это сделать?

Мы используем Microsoft SQL Server 2008.


Я нашел здесь полезный ответ: [подготовленное состояние с оператором-возвращением-сгенерированными ключами] [1] [1]: stackoverflow.com/questions/4224228/…
Ларс Ладегаард,

Ответы:


477

Нет необходимости в отдельном SELECT ...

INSERT INTO table (name)
OUTPUT Inserted.ID
VALUES('bob');

Это работает и для столбцов не-IDENTITY (например, GUID)


29
не могли бы вы уточнить немного? Куда идет вывод в этом примере? Документация показывает только примеры для таблиц (используя вывод ... в). В идеале я хотел бы просто иметь возможность передать его в переменную
JonnyRaa

2
@JonnyLeeds: вы не можете сделать это с переменной (кроме табличной переменной). ВЫХОД идет к клиенту или таблице
gbn

7
К сожалению, вы не можете полагаться на это, так как добавление триггера к таблице нарушит ваши утверждения! Re: blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/…
hajikelist

1
@hajikelist: это довольно сложный случай, обычно помогает SET NCOOUNT ON в триггере. См stackoverflow.com/questions/1483732/set-nocount-on-usage
ГБН

5
Никогда не используйте @@ IDENTITY. SCOPE_IDENTITY, да, но никогда @@ IDENTITY. Ненадежно
17

188

Используйте, SCOPE_IDENTITY()чтобы получить новое значение идентификатора

INSERT INTO table (name) VALUES('bob');

SELECT SCOPE_IDENTITY()

http://msdn.microsoft.com/en-us/library/ms190315.aspx


7
при условии, что idэто личность
Илья G

12
@ liho1eye - ОП ссылается на имя столбца идентификации как id, так что да.
Курт

3
На большей системе, что, если много sql работают одновременно? Будет ли он возвращать последний вставленный идентификатор для каждого запроса?
Шив

2
@Shiv «SCOPE_IDENTITY возвращает значения, вставленные только в текущую область»
goodies4uall

45
INSERT INTO files (title) VALUES ('whatever'); 
SELECT * FROM files WHERE id = SCOPE_IDENTITY();

Это самая безопасная ставка, поскольку существует известная проблема с конфликтом предложения OUTPUT для таблиц с триггерами. Делает это довольно ненадежным, даже если в вашей таблице в настоящий момент нет триггеров - кто-то, добавивший один из них, сломает ваше приложение Time Bomb своего рода поведение.

См. Статью MSDN для более глубокого объяснения:

http://blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/update-with-output-clause-triggers-and-sqlmoreresults.aspx


Только если вы не добавляете SET NOCOUNT ON в триггеры. Также см docs.microsoft.com/en-us/sql/database-engine/configure-windows/...
ГБН

это не вариант для наших устаревших сред @ gbn
hajikelist

@hajikelist У всех нас есть наследство, но риск того, что триггеры испортят OUTPUT, низок, все, что нужно, это включить nocount. Если кто-то добавляет триггер, он должен знать, как его кодировать (подразумевается, что вы в основном управляете им), или вам нужно обучать своих разработчиков. В какой-то момент вы будете вынуждены мигрировать, когда эта версия SQL больше не будет поддерживается и т.д., поэтому триггеры не будут вызывать набор результатов. Как бы то ни было, это не лучший ответ, потому что если у вас есть триггеры INSTEAD OF, SCOPE_IDENTITY может не сработать ( stackoverflow.com/questions/908257/… )
gbn

@gbn - Мне просто нравится избегать таких глупостей. Я не собираюсь говорить всем своим разработчикам: «Не забывайте добавлять« не нарушайте мой оператор приложения »в каждом триггере». - ты можешь оставить это. Сценарий «вместо» - это гораздо более сложный случай.
hajikelist

Более безопасный ответ может заключаться в том, чтобы приложение запускало другой запрос после того, как оно вернется из этого. Пока это делается на бэкэнде, снижение производительности должно стоить простоты управления разработкой в ​​группах людей, и оно ближе по стандартам, чем какая-то сумасшедшая особенность с крайними случаями. Я бы предпочел, чтобы крайние случаи были в моем коде, и избегать их на платформах. только мое мнение не волнуйтесь :)
Дэн Чейз

33

Entity Framework выполняет что-то похожее на ответ gbn:

DECLARE @generated_keys table([Id] uniqueidentifier)

INSERT INTO Customers(FirstName)
OUTPUT inserted.CustomerID INTO @generated_keys
VALUES('bob');

SELECT t.[CustomerID]
FROM @generated_keys AS g 
   JOIN dbo.Customers AS t 
   ON g.Id = t.CustomerID
WHERE @@ROWCOUNT > 0

Результаты вывода сохраняются во временной табличной переменной, а затем отбираются обратно клиенту. Нужно знать о Гоче:

вставки могут генерировать более одной строки, поэтому переменная может содержать более одной строки, поэтому вам может быть возвращено более одной ID

Я понятия не имею, почему EF внутренне присоединяет эфемерную таблицу обратно к реальной таблице (при каких обстоятельствах эти две пары не совпадают).

Но это то, что делает EF.

SQL Server 2008 или новее только. Если это 2005, то вам не повезло.


2
Причина, по которой EF делает это, заключается в том, чтобы он также мог «видеть» все другие изменения во вставленном Customer записи, поскольку на нее могут влиять другие логики на стороне DB, например, DEFAULTв некоторых столбцах, триггеры в таблице и т. Д. EF обновляет Сущность (объект) используется для вставки, поэтому клиентская сторона получает объект customer с идентификатором, а все остальное представляет текущее состояние строки.
Иларион

Еще одна причина не использовать EF.
cskwg

11

@@IDENTITY Системная функция, которая возвращает последнее вставленное значение идентификатора.


4
Нужно советовать никогда не использовать @@ IDENTITY - оно не точное (слишком широкое) и менее поточно-ориентированное - см. Ответ @ Curt о SCOPE_IDENTITY ().
Занлок

10

Есть много способов выйти после вставки

Когда вы вставляете данные в таблицу, вы можете использовать предложение OUTPUT, чтобы вернуть копию данных, которые были вставлены в таблицу. Предложение OUTPUT принимает две основные формы: OUTPUT и OUTPUT INTO. Используйте форму OUTPUT, если вы хотите вернуть данные вызывающему приложению. Используйте форму OUTPUT INTO, если вы хотите вернуть данные в таблицу или табличную переменную.

DECLARE @MyTableVar TABLE (id INT,NAME NVARCHAR(50));

INSERT INTO tableName
(
  NAME,....
)OUTPUT INSERTED.id,INSERTED.Name INTO @MyTableVar
VALUES
(
   'test',...
)

IDENT_CURRENT : возвращает последний идентификатор, созданный для конкретной таблицы или представления в любом сеансе.

SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]

SCOPE_IDENTITY : возвращает последний идентификатор из того же сеанса и той же области. Область действия - это хранимая процедура / триггер и т. Д.

SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];  

@@ IDENTITY : возвращает последний идентификатор из того же сеанса.

SELECT @@IDENTITY AS [@@IDENTITY];

1
@RezaJenabi Jun, выход очень хорошо сработал, лучше, чем найти много идентификаторов в таблице. Я использовал out putдля bulk insertи вставить select statement. спасибо за ваше предложение
Амирхосейн

5

Лучшее и самое верное решение - использовать SCOPE_IDENTITY().

Просто вы должны получить идентификатор области после каждой вставки и сохранить его в переменной, потому что вы можете вызвать две вставки в одной и той же области.

ident_currentи, @@identityвозможно, они работают, но они не являются безопасным объемом Вы можете иметь проблемы в большом приложении

  declare @duplicataId int
  select @duplicataId =   (SELECT SCOPE_IDENTITY())

Более подробно здесь Microsoft документы


1
Можно упростить это доselect @duplicataId = SCOPE_IDENTITY()
pcnate

1
OUTPUTэто лучшее, более чистое решение :)
Дейл К

ВЫХОД В очень медленный.
cskwg

4

Ты можешь использовать scope_identity() чтобы выбрать идентификатор строки, которую вы только что вставили в переменную, а затем просто выбрать любые столбцы из этой таблицы, где id = идентификатор, который вы получилиscope_identity()

См. Здесь для получения информации MSDN http://msdn.microsoft.com/en-us/library/ms190315.aspx


1

Есть несколько способов получить последний вставленный идентификатор после команды вставки.

  1. @@IDENTITY Возвращает последнее значение идентификатора, сгенерированное для соединения в текущем сеансе, независимо от таблицы и области действия оператора, создавшего значение.
  2. SCOPE_IDENTITY()Возвращает последнее значение идентификатора, созданное оператором вставки в текущей области в текущем соединении, независимо от таблицы.
  3. IDENT_CURRENT(‘TABLENAME’)Возвращает последнее значение идентификатора, созданное в указанной таблице, независимо от соединения, сеанса или области действия. IDENT_CURRENT не ограничен областью действия и сессией; он ограничен указанной таблицей.

Теперь кажется, что труднее решить, какой из них будет точно соответствовать моему требованию.

Я в основном предпочитаю SCOPE_IDENTITY ().

Если вы используете select SCOPE_IDENTITY () вместе с TableName в инструкции вставки, вы получите точный результат в соответствии с вашими ожиданиями.

Источник: CodoBee


0

Вот как я использую OUTPUT INSERTED при вставке в таблицу, которая использует ID в качестве столбца идентификаторов в SQL Server:

'myConn is the ADO connection, RS a recordset and ID an integer
Set RS=myConn.Execute("INSERT INTO M2_VOTELIST(PRODUCER_ID,TITLE,TIMEU) OUTPUT INSERTED.ID VALUES ('Gator','Test',GETDATE())")
ID=RS(0)

0

Вы можете добавить оператор select к вашему оператору вставки. Integer myInt = Вставить в таблицу1 (FName) значения («Фред»); Выберите Scope_Identity (); Это вернет значение идентичности при выполнении скалера.



-4

* Порядок параметров в строке подключения иногда важен. * Расположение параметра Provider может нарушать курсор набора записей после добавления строки. Мы видели такое поведение с поставщиком SQLOLEDB.

После добавления строки поля строки недоступны, ЕСЛИ НЕ указан поставщик в качестве первого параметра в строке подключения. Когда поставщик находится где-либо в строке подключения, кроме как в качестве первого параметра, вновь вставленные поля строки недоступны. Когда мы переместили провайдера к первому параметру, поля строки волшебным образом появились.


1
Не могли бы вы рассказать нам, как этот комментарий отвечает / имеет отношение к заданному вопросу? Я не чувствую, что это заслуживает заглавных букв / жирным шрифтом. Если ваш ответ будет сочтен полезным, пользователи проголосуют за него.
n__o

Многие пользователи, вероятно, пришли на эту страницу, потому что у них не было допустимых полей для идентификации только что добавленной строки. Мы обнаружили, что такое поведение (простое изменение порядка параметров в строке подключения позволяет сразу получить доступ к вновь добавленной строке) настолько странно, что я подумал, что оно заслуживает упоминания в шапках, тем более что оно, скорее всего, устранит причину, по которой люди хотят нового идентификатор строки и другие поля этой строки. Если в качестве первого параметра указать поставщика, проблема исчезнет.
Дэвид Гвидос

Вам нужно отредактировать и улучшить свой ответ. Это в настоящее время шумно и не кажется достойным ответом или даже попыткой
Джеймс

Что именно вы подразумеваете под "шумным"? Вы должны объяснить свою жалобу. Это настолько просто, насколько это возможно. Если вы измените порядок параметров в строке подключения, это может повлиять на доступность данных строки после вставки.
Дэвид Гвидос
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.