SQL Server - повернення значення після INSERT


318

Я намагаюсь повернути ключ-значення після INSERT-оператора. Приклад: у мене є таблиця з ім'ям атрибутів та ідентифікатором. id - згенероване значення.

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

Тепер я хочу повернути ідентифікатор на тому ж кроці. Як це робиться?

Ми використовуємо Microsoft SQL Server 2008.


Я знайшов відповідь тут корисну: [PreparedStatement-з-заяву повернення згенерованих-ключі] [1] [1]: stackoverflow.com/questions/4224228 / ...
Ларс Ладегаард

Відповіді:


477

Немає необхідності в окремому SELECT ...

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

Це працює і для стовпців без ідентичності (таких як 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. Це ненадійно
gbn

188

Використовуйте SCOPE_IDENTITY()для отримання нового значення ідентифікатора

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

SELECT SCOPE_IDENTITY()

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


7
припускаючи, що idце особистість
Ilia 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 на таблицях з тригерами. Це робить це досить ненадійним, оскільки навіть якщо у вашій таблиці на даний момент немає жодних тригерів - хтось, додавши його вниз, порушить вашу програму. Часова бомба, поведінка.

Більш детальне пояснення див. У статті 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

це не варіант для нашого спадкового середовища @gbn
hajikelist

@hajikelist У всіх нас є спадщина, але ризик, коли тригери зіпсують ВИХІД низький, все, що потрібно, не встановлюється. Якщо хтось додає тригер, то він повинен знати, як його кодувати (мається на увазі, що ви керуєте головним чином) або вам потрібно навчити розробників. У якийсь момент ви змушені будете переходити, коли цієї версії SQL більше не буде. підтримується тощо, тому тригери не спричинятимуть набір результатів. Безвідносно, це не найкраща відповідь , тому що якщо у вас є ЗАМІСТЬ тригерів від SCOPE_IDENTITY може не працювати ( stackoverflow.com/questions/908257 / ... )
ГБН

@gbn - Мені просто подобається уникати таких дурних речей. Я не збираюся сказати всім своїм розробникам: "Не забудьте додати" не порушувати мою заяву "у кожному запуску." - ти можеш зберегти. Сценарій "натомість" - це набагато кращий варіант imo.
хаджикеліст

Більш безпечною відповіддю може бути лише те, що програма запустить інший запит, як тільки він повернеться з цього. Поки це робиться з заднього боку, покарання за ефективність повинно коштувати простоти управління розвитком у групах людей, і це ближче відповідно до стандартів, ніж якась божевільна особливість із крайніми корпусами. Я вважаю за краще, щоб крайові корпуси були в моєму коді, і уникати їх на платформах. просто моя думка не вигадує :)
Dan Chase

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записи, оскільки можлива й інша логіка на стороні БД, яка впливає на неї, наприклад, DEFAULTу деяких стовпцях, тригерах на таблиці тощо. EF оновлює оновлення сутність (об'єкт), яку він використовував для вставки, тому сторона клієнта отримує об’єкт замовника з ідентифікатором та всім іншим, що представляє поточний стан рядка.
Іларіон

Ще одна причина не використовувати EF.
cskwg

11

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


4
Потрібно не радити коли-небудь використовувати @@ IDENTITY - це не точне (занадто широке) набагато менш безпечне для потоків - дивіться відповідь @ Curt про SCOPE_IDENTITY ().
zanlok

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];  

@@ ІДЕНТИЧНІСТЬ : Він повертає останню ідентичність з того ж сеансу.

SELECT @@IDENTITY AS [@@IDENTITY];

1
@RezaJenabi Червень, кажучи , дуже добре працював, краще, ніж знайти багато ідентифікаторів у таблиці. Я використовував out putдля bulk insertі вставки на select statement. дякую вашій пропозиції
Amirhossein

5

Найкраще і найвірогідніше рішення - це використання SCOPE_IDENTITY() .

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

ident_currentі @@identityможе бути , вони працюють , але вони не є безпечною сферою. Ви можете мати проблеми у великій програмі

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

Більш детально тут документи Microsoft


1
Можна спростити цеselect @duplicataId = SCOPE_IDENTITY()
pcnate

1
OUTPUTпункт є кращим, більш чистим рішенням :)
Dale K

ВИХІД ВІД дуже повільний.
cskwg

4

Ви можете використовувати scope_identity()ідентифікатор рядка, який ви щойно вставили в змінну, а потім просто вибрати будь-які стовпці, які ви хочете, з тієї таблиці, де id = ідентичність, яку ви отрималиscope_identity()

Інформацію про MSDN див. Тут http://msdn.microsoft.com/en-us/library/ms190315.aspx


1

Існує кілька способів отримати останній вставлений ідентифікатор після команди insert.

  1. @@IDENTITY : Він повертає останнє значення Identity, сформоване в З'єднанні в поточному сеансі, незалежно від Таблиці та обсягу оператора, який створив значення
  2. SCOPE_IDENTITY(): Він повертає останнє значення ідентичності, згенерованого оператором вставки в поточному діапазоні в поточному з'єднанні незалежно від таблиці.
  3. IDENT_CURRENT(‘TABLENAME’): Він повертає останнє значення ідентичності, згенерованого у вказаній таблиці, незалежно від будь-якого з'єднання, сеансу чи обсягу. IDENT_CURRENT не обмежений обсягом та сеансом; він обмежений вказаною таблицею.

Зараз здається складніше вирішити, який з них буде точно відповідати моїй вимозі.

Я переважно віддаю перевагу SCOPE_IDENTITY ().

Якщо ви використовуєте виберіть SCOPE_IDENTITY () разом із таблицею NameName у виписці, ви отримаєте точний результат відповідно до ваших очікувань.

Джерело: CodoBee


0

Ось так я використовую OUTPUT INSERTED під час вставки в таблицю, яка використовує ідентифікатор як стовпець ідентичності в 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 = Вставити в table1 (FName) значення ('Fred'); Виберіть Scope_Identity (); Це поверне значення ідентичності при виконанні скалера.


-4

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


24
Ніколи не використовуйте @@ ІДЕНТИЧНІСТЬ: це не безпечно для сфери дії: тригери тощо впливають на нього.
gbn

-4

* Порядок параметрів у рядку з'єднання іноді важливий. * Параметр Провайдера може перебити курсор набору записів після додавання рядка. Ми бачили таку поведінку у постачальника SQLOLEDB.

Після додавання рядка поля рядків недоступні, ПІДКЛЮЧЕНО Постачальник вказаний як перший параметр у рядку з'єднання. Якщо постачальник знаходиться де-небудь у рядку з'єднання, окрім першого параметра, щойно вставлені поля рядків недоступні. Коли ми перемістили Провайдера до першого параметра, поля рядків магічно з'явилися.


1
Не могли б ви сказати нам, як цей коментар відповідає / має відношення до поставленого питання? Я не вважаю, що це заслуговує на шапки / жирне. Якщо ваша відповідь буде визнана корисною, користувачі проголосують за неї.
n__о

На цю сторінку, ймовірно, прийшло багато користувачів, оскільки вони не мали дійсних полів для ідентифікації щойно доданого рядка. Така поведінка, яку ми з’ясували (що просто зміна порядку параметрів у рядку з'єднання дозволяє негайно отримати доступ до щойно доданого рядка) настільки химерна, що я вважав, що заслуговує згадки у великих шапках, тим більше, що це, ймовірно, виправить причину того, що люди хочуть нового ідентифікатор рядка та інші поля цього рядка. Просто поставивши провайдера в якості першого параметра, проблема зникає.
Девід Гвідос

Вам потрібно відредагувати та вдосконалити свою відповідь. Наразі це шумно і не сприймається як гідна відповідь чи навіть спроба
Джеймс

Що саме ви маєте на увазі під "галасливим"? Вам потрібно пояснити свою скаргу. Це приблизно так просто, як це може бути. Якщо ви зміните порядок параметрів у рядку з'єднання, це може вплинути на те, чи є дані про рядки після вставки.
Девід Гвідос
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.