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

318

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

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

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

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

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

Ответы:

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

hajikelist
источник
Только если вы не добавляете 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 Системная функция, которая возвращает последнее вставленное значение идентификатора.

AngelaG
источник
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

Purplegoldfish
источник
1

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

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

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

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

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

Источник: CodoBee

Ishrar
источник
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

После вставки в таблицу со столбцом идентификаторов вы можете обратиться к @@ IDENTITY, чтобы получить значение: http://msdn.microsoft.com/en-us/library/aa933167%28v=sql.80%29.aspx

FanOfTamago
источник
24
Никогда не используйте @@ IDENTITY: это небезопасно: триггеры и т. Д. Влияют на него.
ГБН
-4

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

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

Дэвид Гвидос
источник
1
Не могли бы вы рассказать нам, как этот комментарий отвечает / имеет отношение к заданному вопросу? Я не чувствую, что это заслуживает заглавных букв / жирным шрифтом. Если ваш ответ будет сочтен полезным, пользователи проголосуют за него.
n__o
Многие пользователи, вероятно, пришли на эту страницу, потому что у них не было допустимых полей для идентификации только что добавленной строки. Мы обнаружили, что такое поведение (простое изменение порядка параметров в строке подключения позволяет сразу получить доступ к вновь добавленной строке) настолько странно, что я подумал, что оно заслуживает упоминания в шапках, тем более что оно, скорее всего, устранит причину, по которой люди хотят нового идентификатор строки и другие поля этой строки. Если в качестве первого параметра указать поставщика, проблема исчезнет.
Дэвид Гвидос
Вам нужно отредактировать и улучшить свой ответ. Это в настоящее время шумно и не кажется достойным ответом или даже попыткой
Джеймс
Что именно вы подразумеваете под "шумным"? Вы должны объяснить свою жалобу. Это настолько просто, насколько это возможно. Если вы измените порядок параметров в строке подключения, это может повлиять на доступность данных строки после вставки.
Дэвид Гвидос