获取IDENTITY
插入行的最佳方法是什么?
我知道@@IDENTITY
和但不了解每个的优点和缺点IDENT_CURRENT
。SCOPE_IDENTITY
有人可以解释一下差异以及我应该何时使用它们吗?
获取IDENTITY
插入行的最佳方法是什么?
我知道@@IDENTITY
和但不了解每个的优点和缺点IDENT_CURRENT
。SCOPE_IDENTITY
有人可以解释一下差异以及我应该何时使用它们吗?
@@IDENTITY
返回在所有范围内为当前会话中的任何表生成的最后一个标识值。 您需要在这里小心,因为它是跨范围的。您可以从触发器而不是当前语句中获取值。
SCOPE_IDENTITY()
返回为当前会话和当前范围中的任何表生成的最后一个标识值。 一般你想用什么。
IDENT_CURRENT('tableName')
返回为任何会话和任何范围内的特定表生成的最后一个标识值。这使您可以指定要从哪个表中获取值,以防上述两个不是您所需要的(非常罕见)。此外,正如@ Guy Starbuck所提到的,“如果您想获取尚未插入记录的表的当前 IDENTITY 值,您可以使用它。”
该语句的OUTPUT
子句INSERT
将允许您访问通过该语句插入的每一行。由于它的范围仅限于特定语句,因此它比上面的其他函数更直接。但是,它有点冗长(您需要插入表变量/临时表然后查询它),即使在语句回滚的错误情况下也会给出结果。也就是说,如果您的查询使用并行执行计划,这是获取标识的唯一保证方法(没有关闭并行性)。但是,它在触发器之前执行,不能用于返回触发器生成的值。
我相信检索插入的 id 的最安全和最准确的方法是使用输出子句。
例如(取自以下MSDN文章)
USE AdventureWorks2008R2; GO DECLARE @MyTableVar table( NewScrapReasonID smallint, Name varchar(50), ModifiedDate datetime); INSERT Production.ScrapReason OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate INTO @MyTableVar VALUES (N'Operator error', GETDATE()); --Display the result set of the table variable. SELECT NewScrapReasonID, Name, ModifiedDate FROM @MyTableVar; --Display the result set of the table. SELECT ScrapReasonID, Name, ModifiedDate FROM Production.ScrapReason; GO
我说的和其他人一样,所以每个人都是正确的,我只是想让它更清楚。
@@IDENTITY
返回客户端与数据库的连接插入的最后一个东西的 id。
大多数情况下这工作正常,但有时触发器会插入一个你不知道的新行,你会从这个新行获取 ID,而不是你想要的
SCOPE_IDENTITY()
解决了这个问题。它返回您在发送到数据库的 SQL 代码中插入的最后一个内容的 ID。如果触发器去创建额外的行,它们不会导致返回错误的值。万岁
IDENT_CURRENT
返回任何人插入的最后一个 ID。如果某个其他应用碰巧在不幸的时间插入了另一行,您将获得该行的 ID 而不是您的 ID。
如果您想安全使用,请始终使用SCOPE_IDENTITY()
. 如果您坚持下去@@IDENTITY
并且有人决定稍后添加触发器,那么您的所有代码都会中断。
获取新插入行的标识的最佳(阅读:最安全)方法是使用以下output
子句:
create table TableWithIdentity
( IdentityColumnName int identity(1, 1) not null primary key,
... )
-- type of this table's column must match the type of the
-- identity column of the table you'll be inserting into
declare @IdentityOutput table ( ID int )
insert TableWithIdentity
( ... )
output inserted.IdentityColumnName into @IdentityOutput
values
( ... )
select @IdentityValue = (select ID from @IdentityOutput)
添加
SELECT CAST(scope_identity() AS int);
到您的插入 sql 语句的末尾,然后
NewId = command.ExecuteScalar()
将检索它。
来自 MSDN
@@IDENTITY、SCOPE_IDENTITY 和 IDENT_CURRENT 是相似的函数,因为它们返回插入到表的 IDENTITY 列中的最后一个值。
@@IDENTITY 和 SCOPE_IDENTITY 将返回当前会话中任何表中生成的最后一个标识值。但是,SCOPE_IDENTITY 只返回当前范围内的值;@@IDENTITY 不限于特定范围。
IDENT_CURRENT 不受范围和会话限制;它仅限于指定的表。IDENT_CURRENT 返回为任何会话和任何范围内的特定表生成的标识值。有关详细信息,请参阅 IDENT_CURRENT。
当您使用实体框架时,它在内部使用该OUTPUT
技术返回新插入的 ID 值
DECLARE @generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO @generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID ]
FROM @generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE @@ROWCOUNT > 0
输出结果存储在临时表变量中,连接回表,并从表中返回行值。
注意:我不知道为什么 EF 会将临时表内部连接回真实表(在什么情况下两者不匹配)。
但这就是 EF 所做的。
此技术 ( OUTPUT
) 仅适用于 SQL Server 2008 或更高版本。
Entity Framework 连接回原始表而不是简单地使用OUTPUT
值的原因是因为 EF 也使用这种技术来获取rowversion
新插入的行的值。
您可以使用以下Timestamp
属性在实体框架模型中使用乐观并发:
public class TurboEncabulator
{
public String StatorSlots)
[Timestamp]
public byte[] RowVersion { get; set; }
}
当你这样做时,实体框架将需要rowversion
新插入的行:
DECLARE @generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO @generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID], t.[RowVersion]
FROM @generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE @@ROWCOUNT > 0
为了检索它Timetsamp
,您不能使用OUTPUT
子句。
那是因为如果桌子上有一个触发器,Timestamp
那么你的任何输出都会是错误的:
如果表上有触发器,则返回的时间戳永远不会正确。所以你必须使用单独的SELECT
.
即使您愿意遭受不正确的行版本,执行单独的另一个原因SELECT
是您不能将rowversion
a 输出到表变量中:
DECLARE @generated_keys table([Id] uniqueidentifier, [Rowversion] timestamp)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID, inserted.Rowversion INTO @generated_keys
VALUES('Malleable logarithmic casing');
这样做的第三个原因是对称性。UPDATE
使用触发器对表执行操作时,不能使用OUTPUT
子句。不支持尝试UPDATE
使用 an OUTPUT
,并且会报错:
唯一的方法是使用后续SELECT
声明:
UPDATE TurboEncabulators
SET StatorSlots = 'Lotus-O deltoid type'
WHERE ((TurboEncabulatorID = 1) AND (RowVersion = 792))
SELECT RowVersion
FROM TurboEncabulators
WHERE @@ROWCOUNT > 0 AND TurboEncabulatorID = 1
@@IDENTITY是使用当前 SQL 连接插入的最后一个标识。这是从插入存储过程返回的一个很好的值,您只需要为新记录插入标识,而无需关心之后是否添加了更多行。
SCOPE_IDENTITY是使用当前 SQL 连接插入的最后一个标识,并且在当前范围内——也就是说,如果在您插入之后基于触发器插入了第二个标识,它不会反映在 SCOPE_IDENTITY 中,只有您执行的插入. 坦率地说,我从来没有理由使用它。
IDENT_CURRENT(tablename)是最后插入的身份,无论连接或范围如何。如果您想获取尚未插入记录的表的当前 IDENTITY 值,则可以使用它。
我无法与其他版本的 SQL Server 交谈,但在 2012 年,直接输出就可以了。您无需为临时表烦恼。
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES (...)
顺便说一句,这种技术在插入多行时也有效。
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES
(...),
(...),
(...)
输出
ID
2
3
4
始终使用 scope_identity(),永远不需要其他任何东西。
创建一个uuid
并将其插入到列中。然后,您可以使用 uuid 轻松识别您的行。那是您可以实施的唯一 100% 工作解决方案。所有其他解决方案都太复杂或无法在相同的边缘情况下工作。例如:
1) 创建行
INSERT INTO table (uuid, name, street, zip)
VALUES ('2f802845-447b-4caa-8783-2086a0a8d437', 'Peter', 'Mainstreet 7', '88888');
2)获取创建的行
SELECT * FROM table WHERE uuid='2f802845-447b-4caa-8783-2086a0a8d437';
保证您插入的行的标识的另一种方法是指定标识值并使用SET IDENTITY_INSERT ON
and then OFF
。这可以保证您确切地知道身份值是什么!只要这些值没有被使用,那么您就可以将这些值插入到标识列中。
CREATE TABLE #foo
(
fooid INT IDENTITY NOT NULL,
fooname VARCHAR(20)
)
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (1,
'one'),
(2,
'Two')
SET IDENTITY_INSERT #foo OFF
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
INSERT INTO #foo
(fooname)
VALUES ('Three')
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
-- YOU CAN INSERT
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (10,
'Ten'),
(11,
'Eleven')
SET IDENTITY_INSERT #foo OFF
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SELECT *
FROM #foo
如果您从另一个源加载数据或合并来自两个数据库的数据等,这可能是一种非常有用的技术。
尽管这是一个较旧的线程,但有一种较新的方法可以避免旧版本 SQL Server 中 IDENTITY 列的一些缺陷,例如服务器重新启动后标识值的间隙。序列在 SQL Server 2016 和转发中可用,这是较新的方法是使用 TSQL 创建一个 SEQUENCE 对象。这允许您在 SQL Server 中创建自己的数字序列对象并控制其递增方式。
这是一个例子:
CREATE SEQUENCE CountBy1
START WITH 1
INCREMENT BY 1 ;
GO
然后在 TSQL 中,您将执行以下操作来获取下一个序列 ID:
SELECT NEXT VALUE FOR CountBy1 AS SequenceID
GO
在您的插入语句之后,您需要添加它。并确保数据插入的表名。您将获得当前行,而不是您的插入语句刚刚影响的行。
IDENT_CURRENT('tableName')