在创建标识的同一个T-SQL语句中获取标识值?
我被问到是否可以有一个insert语句,它的ID字段是一个“identity”列,并且分配的值是否也可以插入同一记录中的另一个字段,即同一个insert语句中 这是否可能(SQL Server 2008r2)在创建标识的同一个T-SQL语句中获取标识值?,sql,sql-server,tsql,sql-server-2008-r2,identity-column,Sql,Sql Server,Tsql,Sql Server 2008 R2,Identity Column,我被问到是否可以有一个insert语句,它的ID字段是一个“identity”列,并且分配的值是否也可以插入同一记录中的另一个字段,即同一个insert语句中 这是否可能(SQL Server 2008r2) 谢谢。两者都可以。 要插入带有列“identity”的行,您需要设置identity\u insert off 请注意,您仍然不能复制值 您可以看到该命令。 请注意set identity\u insert on之后 要创建具有相同记录的表,只需: 创建新列 插入空值或其他内容 在插入后
谢谢。两者都可以。 要插入带有列“identity”的行,您需要
设置identity\u insert off
请注意,您仍然不能复制值
您可以看到该命令。
请注意set identity\u insert on
之后
要创建具有相同记录的表,只需:
- 创建新列李>
- 插入空值或其他内容李>
- 在插入后使用标识列的值更新该列
@@identity
全局变量。它会给你最后一次插入。所以我认为您需要执行一个@@identity+1
。在这种情况下,它可能给出错误的值,因为@@identity
适用于所有表。因此,如果插入发生在另一个具有标识的表中,则它将计数
另一个解决方案是获取max id并添加一个:),然后获得所需的值 您无法真正做到这一点-因为将用于
标识
列的实际值实际上只有在插入
完成时才是固定的和设置的
但是,您可以使用触发器等
CREATE TRIGGER trg_YourTableInsertID ON dbo.YourTable
AFTER INSERT
AS
UPDATE dbo.YourTable
SET dbo.YourTable.OtherID = i.ID
FROM dbo.YourTable t2
INNER JOIN INSERTED i ON i.ID = t2.ID
这将在插入任何行后立即触发,并将
OtherID
列设置为插入行的IDENTITY
列的值。但是严格来说,它不是在同一个语句中,而是在原始语句之后。您可以通过在表中设置一个计算列来实现这一点:
DECLARE @QQ TABLE (ID INT IDENTITY(1,1), Computed AS ID PERSISTED, Letter VARCHAR (1))
INSERT INTO @QQ (Letter)
VALUES ('h'),
('e'),
('l'),
('l'),
('o')
SELECT *
FROM @QQ
1 1 h
2 2 e
3 3 l
4 4 l
5 5 o
关于勾选答案:
您无法真正做到这一点,因为将使用的实际值 对于标识列,实际上只有在插入 已经完成 马克我想,你实际上是不对的<是的,他能)) 解决方法是
IDENT_CURRENT()
:
此外,您甚至可以使用IDENT_CURRENT()
作为默认约束
,例如,它可以代替SCOPE_IDENTITY()
。试试这个:
CREATE TABLE TemporaryTable(
Id int PRIMARY KEY IDENTITY(1,1) NOT NULL,
FkId int NOT NULL DEFAULT IDENT_CURRENT('[TemporaryTable]')
)
ALTER TABLE TemporaryTable
ADD CONSTRAINT [Fk_const] FOREIGN KEY (FkId) REFERENCES [TemporaryTable] ([Id])
INSERT INTO TemporaryTable (FkId) VALUES (DEFAULT)
INSERT INTO TemporaryTable (FkId) VALUES (DEFAULT)
INSERT INTO TemporaryTable (FkId) VALUES (DEFAULT)
INSERT INTO TemporaryTable (FkId) VALUES (DEFAULT)
UPDATE TemporaryTable
SET [FkId] = 3
WHERE Id = 2
SELECT * FROM TemporaryTable
DROP TABLE TemporaryTable
使用这个简单的代码
`SCOPE_IDENTITY()+1我知道原来的帖子是很久以前的了。但是,最顶级的解决方案是在插入记录后使用触发器更新字段,我认为有一种更有效的方法 用触发器来做这件事总是让我感到不安。似乎总有更好的办法。该触发器基本上使每个insert执行2次对数据库的写入,(1)insert,然后(2)第2个int的更新。该触发器还将连接回表中。特别是对于大型数据库和大型表,这会带来一些开销。我怀疑,随着表变大,这种方法的开销也会变大。也许我错了。但是,在一张大桌子上,这似乎不是一个好的解决方案 我编写了一个函数fn_GetIdent,可用于此。有趣的是,这是多么简单,但实际上是一些工作要弄清楚。我最终偶然发现了这一点。事实证明,在从INSERT语句集值赋值子句调用的函数中调用IDENT_CURRENT(@variableTableName)与直接从INSERT语句调用IDENT_CURRENT(@variableTableName)的行为不同。它使您可以为插入的记录获取新的标识值 有一个警告。当标识为NULL时(即,一个没有记录的空表),由于sys.identity\u columns.last\u值为NULL,它的行为略有不同。因此,您必须以稍微不同的方式处理输入的第一条记录。我在函数中添加了代码来解决这个问题,现在它可以工作了 这是因为对函数的每次调用,即使在同一个INSERT语句中,都在函数中自己的新“作用域”中。(我认为这是正确的解释)。因此,您甚至可以使用此函数使用一条insert语句插入多行。如果直接从INSERT语句调用IDENT_CURRENT(@variableTableName),它将为所有行中的newID分配相同的值。这是因为标识在整个INSERT语句完成处理(在同一范围内)后更新。但是,从函数中调用IDENT_CURRENT(@variableTableName)会导致每个insert使用输入的每一行更新标识值。但是,这一切都是在INSERT语句本身的函数调用中完成的。因此,一旦创建了函数,就很容易实现 这种方法是调用函数(从INSERT语句),从函数中读取sys.identity_columns.last_值(查看它是否为NULL以及是否存在记录),然后调用IDENT_CURRENT(@variableTableName),然后从函数返回INSERT语句以插入行。因此,它是一个小的读取(对于插入的每一行),然后是插入的一个写入,这比我认为的触发器方法的开销要小。如果对具有大型表的大型数据库中的所有表都使用触发器方法,则触发器方法可能会非常低效。与触发器相比,我还没有做过任何性能分析。但是,我认为这会更有效率,尤其是在大桌子上 我一直在测试,这似乎在所有情况下都有效。我希望得到反馈,了解是否有人发现了这种方法不起作用的地方,或者这种方法是否存在任何问题。任何人都可以在这种方法中打洞吗?如果有,请告诉我。如果没有,你能投赞成票吗?我认为这是一个更好的选择
CREATE TABLE TemporaryTable(
Id int PRIMARY KEY IDENTITY(1,1) NOT NULL,
FkId int NOT NULL DEFAULT IDENT_CURRENT('[TemporaryTable]')
)
ALTER TABLE TemporaryTable
ADD CONSTRAINT [Fk_const] FOREIGN KEY (FkId) REFERENCES [TemporaryTable] ([Id])
INSERT INTO TemporaryTable (FkId) VALUES (DEFAULT)
INSERT INTO TemporaryTable (FkId) VALUES (DEFAULT)
INSERT INTO TemporaryTable (FkId) VALUES (DEFAULT)
INSERT INTO TemporaryTable (FkId) VALUES (DEFAULT)
UPDATE TemporaryTable
SET [FkId] = 3
WHERE Id = 2
SELECT * FROM TemporaryTable
DROP TABLE TemporaryTable
IF OBJECT_ID('dbo.fn_GetIdent') IS NOT NULL
DROP FUNCTION dbo.fn_GetIdent;
GO
CREATE FUNCTION dbo.fn_GetIdent(@inTableName AS VARCHAR(MAX))
RETURNS Int
WITH EXECUTE AS CALLER
AS
BEGIN
DECLARE @tableHasIdentity AS Int
DECLARE @tableIdentitySeedValue AS Int
/*Check if the tables identity column is null - a special case*/
SELECT
@tableHasIdentity = CASE identity_columns.last_value WHEN NULL THEN 0 ELSE 1 END,
@tableIdentitySeedValue = CONVERT(int, identity_columns.seed_value)
FROM sys.tables
INNER JOIN sys.identity_columns
ON tables.object_id = identity_columns.object_id
WHERE identity_columns.is_identity = 1
AND tables.type = 'U'
AND tables.name = @inTableName;
DECLARE @ReturnValue AS Int;
SET @ReturnValue = CASE @tableHasIdentity WHEN 0 THEN @tableIdentitySeedValue
ELSE IDENT_CURRENT(@inTableName)
END;
RETURN (@ReturnValue);
END
GO
/* The function above only has to be created the one time to be used in the example below */
DECLARE @TableHasRows AS Bit
DROP TABLE IF EXISTS TestTable
CREATE TABLE TestTable (ID INT IDENTITY(1,1),
New INT,
Letter VARCHAR (1))
INSERT INTO TestTable (New, Letter)
VALUES (dbo.fn_GetIdent('TestTable'), 'H')
INSERT INTO TestTable (New, Letter)
VALUES (dbo.fn_GetIdent('TestTable'), 'e')
INSERT INTO TestTable (New, Letter)
VALUES (dbo.fn_GetIdent('TestTable'), 'l'),
(dbo.fn_GetIdent('TestTable'), 'l'),
(dbo.fn_GetIdent('TestTable'), 'o')
INSERT INTO TestTable (New, Letter)
VALUES (dbo.fn_GetIdent('TestTable'), ' '),
(dbo.fn_GetIdent('TestTable'), 'W'),
(dbo.fn_GetIdent('TestTable'), 'o'),
(dbo.fn_GetIdent('TestTable'), 'r'),
(dbo.fn_GetIdent('TestTable'), 'l'),
(dbo.fn_GetIdent('TestTable'), 'd')
INSERT INTO TestTable (New, Letter)
VALUES (dbo.fn_GetIdent('TestTable'), '!')
SELECT * FROM TestTable
/*
Result
ID New Letter
1 1 H
2 2 e
3 3 l
4 4 l
5 5 o
6 6
7 7 W
8 8 o
9 9 r
10 10 l
11 11 d
12 12 !
*/