SQL Server-插入后返回值


318

我正在尝试在INSERT语句之后找回键值。示例:我有一个带有属性名称和ID的表。id是一个生成的值。

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

现在,我想在同一步骤中重新获得ID。怎么做?

我们正在使用Microsoft SQL Server 2008。


我发现了一个有用的答案在这里:PreparedStatement的-与语句返回生成的密钥] [1] [1]:stackoverflow.com/questions/4224228/...
拉斯Ladegaard

Answers:


477

无需单独的SELECT ...

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

这也适用于非IDENTITY列(例如GUID)


29
你能详细说明一下吗?在此示例中,输出到哪里去了? 该文档仅显示表的示例(使用output ... into)。理想情况下,我只想将其传递给变量
JonnyRaa 2014年

2
@JonnyLeeds:您不能对变量执行此操作(除非有表变量)。输出到客户端或一个表
gbn 2014年

7
不幸的是,您不能依靠它,因为向表添加触发器将破坏您的语句!回复:blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/...
hajikelist

1
@hajikelist:这是一个非常极端的情况,在触发器中将SET NCOOUNT ON设置通常会有所帮助。参见stackoverflow.com/questions/1483732/set-nocount-on-usage
gbn 2015年

5
请勿使用@@ IDENTITY。SCOPE_IDENTITY,是的,但永远不要@@ IDENTITY。这是不可靠的
gbn

188

使用SCOPE_IDENTITY()以获取新的ID值

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

SELECT SCOPE_IDENTITY()

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


7
假设id是身份
Ilia G

12
@ liho1eye-OP将标识列名称称为id,所以可以。
Curt

3
在较大的系统上,如果同时运行多个sql怎么办?它将为每个请求返回最后插入的ID吗?
Shiv

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我们都有传承,但是触发器搞乱OUTPUT的风险很低,只需要设置nocount就可以了。如果某人正在添加触发器,那么他们应该知道如何对其进行编码(这主要是意味着您具有控制权),或者您需要培训开发人员。在某些时候,当不再使用该版本的SQL时,您将被迫迁移。支持等,因此触发器不会导致结果集。无论如何,它是不是最好的答案,因为如果你有INSTEAD OF触发器的SCOPE_IDENTITY可能无法正常工作(stackoverflow.com/questions/908257/...
GBN

@gbn-我只是想避免像这样的愚蠢的事情。我不会告诉所有的开发人员,“不要忘记在每个触发器中添加'不要破坏我的应用程序声明'。” - 你可以留着。“替代”方案远不止是边缘情况。
hajikelist,

一个更安全的答案可能就是让应用程序从该查询返回后再运行另一个查询。只要在后端完成,性能损失就值得在一群人中管理开发的简单性,而且按照标准,它比带有边缘案例的某些疯狂功能更接近标准。我宁愿边缘情况出现在我的代码中,并避免在平台上使用它们。只是我的意见不怪:)
Dan Chase

33

实体框架执行类似于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更新了实体(对象)用于插入,因此客户端将获得具有ID的客户对象以及代表行当前状态的所有其他对象。
Hilarion

不使用EF的另一个原因。
cskwg

11

@@IDENTITY 是一个系统函数,它返回最后插入的标识值。


4
必须建议不要使用@@ IDENTITY-它不准确(太广泛),线程安全性要差得多-请参阅@Curt关于SCOPE_IDENTITY()的回答。
zanlok '18

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,输出效果很好,胜于在表中找到许多ID。我用out putbulk insert和插入的select statement。感谢您的建议
Amirhossein

5

最好,最确定的解决方案是使用SCOPE_IDENTITY()

只是您必须在每次插入后获得作用域标识并将其保存在变量中,因为您可以在同一作用域中调用两个插入。

ident_current并且@@identity可能是他们的工作,但他们都没有安全的范围。您可能在大型应用程序中遇到问题

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

更详细的信息在这里Microsoft docs


1
可以简化为select @duplicataId = SCOPE_IDENTITY()
pcnate

1
OUTPUT子句是一种更好,更纯净的解决方案:)
Dale K

输出进入非常慢。
cskwg


1

在插入命令之后,有多种方法来获取最后插入的ID。

  1. @@IDENTITY :它将返回当前会话中在Connection上生成的最后一个Identity值,而不管产生该值的Table和语句的范围如何
  2. SCOPE_IDENTITY():它返回由insert语句在当前连接的当前作用域中生成的最后一个标识值,而与表无关。
  3. IDENT_CURRENT(‘TABLENAME’):它返回在指定表上生成的最后一个标识值,而不管任何连接,会话或作用域如何。IDENT_CURRENT不受范围和会话的限制;它仅限于指定的表。

现在,似乎很难决定哪一个将完全符合我的要求。

我最喜欢SCOPE_IDENTITY()。

如果在插入语句中将select SCOPE_IDENTITY()与TableName一起使用,则将按照您的期望获得确切的结果。

资料来源:CodoBee


0

当插入到使用ID作为SQL Server中的标识列的表时,这就是我使用OUTPUT INSERTED的方式:

'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

您可以在您的插入语句后附加一个选择语句。整数myInt =插入表1(FName)中的值('Fred'); 选择Scope_Identity(); 执行缩放器时,这将返回标识的值。



-4

*连接字符串中的参数顺序有时很重要。*提供者参数的位置可以在添加一行后中断记录集游标。我们在SQLOLEDB提供程序中看到了这种行为。

添加行后,行字段将不可用,除非将提供程序指定为连接字符串中的第一个参数。当提供程序位于连接字符串中除第一个参数以外的任何位置时,新插入的行字段将不可用。当我们将Provider移到第一个参数时,行字段神奇地出现了。


1
您能否告诉我们此评论如何回答/与提出的问题相关?我觉得这不应该大写/加粗。如果您的回答被认为是有帮助的,则用户会投票赞成。
2015年

许多用户可能因为没有有效的字段来标识刚刚添加的行而来到此页面。我们发现的这种行为(只需更改连接字符串中参数的顺序就可以立即访问新添加的行)是如此奇怪,以至于我认为值得大写,特别是因为这很可能会解决人们想要新的行列的原因。行ID和该行的其他字段。通过简单地将提供者作为第一个参数,问题就消失了。
David Guidos

您需要编辑和改善答案。它目前很嘈杂,并没有得到像样的回答甚至是尝试
詹姆斯(James)

您所说的“嘈杂”到底是什么意思?您需要解释您的投诉。它尽可能简单。如果更改连接字符串中参数的顺序,则会影响插入后行数据是否可用。
David Guidos
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.