有人问我是否可以有一个插入语句,它有一个 ID 字段是一个“身份”列,并且分配的值是否也可以插入到同一条记录中的另一个字段中,在同一个插入语句中。
这可能吗(SQL Server 2008r2)?
谢谢。
有人问我是否可以有一个插入语句,它有一个 ID 字段是一个“身份”列,并且分配的值是否也可以插入到同一条记录中的另一个字段中,在同一个插入语句中。
这可能吗(SQL Server 2008r2)?
谢谢。
您不能真正做到这一点 - 因为将用于IDENTITY
列的实际值实际上只是在INSERT
完成时固定和设置。
但是,您可以使用例如触发器
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
关于检查的答案:
您不能真正做到这一点 - 因为将用于 IDENTITY 列的实际值实际上只在 INSERT 完成时固定和设置。
marc_s我想,你实际上是不对的。是的,他可以!))
解决方法是IDENT_CURRENT()
:
CREATE TABLE TemporaryTable(
Id int PRIMARY KEY IDENTITY(1,1) NOT NULL,
FkId int NOT NULL
)
ALTER TABLE TemporaryTable
ADD CONSTRAINT [Fk_const] FOREIGN KEY (FkId) REFERENCES [TemporaryTable] ([Id])
INSERT INTO TemporaryTable (FkId) VALUES (IDENT_CURRENT('[TemporaryTable]'))
INSERT INTO TemporaryTable (FkId) VALUES (IDENT_CURRENT('[TemporaryTable]'))
INSERT INTO TemporaryTable (FkId) VALUES (IDENT_CURRENT('[TemporaryTable]'))
INSERT INTO TemporaryTable (FkId) VALUES (IDENT_CURRENT('[TemporaryTable]'))
UPDATE TemporaryTable
SET [FkId] = 3
WHERE Id = 2
SELECT * FROM TemporaryTable
DROP TABLE TemporaryTable
此外,您甚至可以使用IDENT_CURRENT()
asDEFAULT CONSTRAINT
并且它可以代替SCOPE_IDENTITY()
example。试试这个:
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
你可以两者都做。
要插入具有“身份”列的行,您需要set identity_insert off
.
请注意,您仍然不能重复值!
您可以在此处查看命令。以后注意 set identity_insert on
。
要创建具有相同记录的表,您只需:
如果需要同时插入值,可以使用@@identity
全局变量。它会给你最后插入的。所以我认为你需要做一个@@identity + 1
. 在这种情况下,它可能会给出错误的值,因为@@identity
它适用于所有表。因此,如果插入发生在另一个具有标识的表中,它将计算在内。
另一种解决方案是获取最大 id 并添加一个 :) 并获得所需的值!
使用这个简单的代码 `SCOPE_IDENTITY()+1
我知道原来的帖子是很久以前的了。但是,最重要的解决方案是在插入记录后使用触发器来更新字段,我认为有一种更有效的方法。
为此使用触发器一直困扰着我。似乎总是有更好的方法。该触发器基本上使每个插入对数据库执行 2 次写入,(1) 插入,然后 (2) 更新第二个 int。触发器也在做一个连接回到表中。对于大型数据库和大型表来说,这有点开销。而且我怀疑随着表变大,这种方法的开销也会增加。也许我错了。但是,在一张大桌子上,这似乎不是一个好的解决方案。
我写了一个函数 fn_GetIdent 可以用来做这个。有趣的是,它是多么简单,但确实需要一些工作才能弄清楚。我最终偶然发现了这一点。事实证明,从从 INSERT 语句 SET 值分配子句调用的函数中调用 IDENT_CURRENT(@variableTableName) 与直接从 INSERT 语句调用 IDENT_CURRENT(@variableTableName) 的行为不同。它使您可以为您插入的记录获取新的身份值。
有一个警告。当标识为 NULL(即 - 一个没有记录的空表)时,它的行为略有不同,因为 sys.identity_columns.last_value 为 NULL。因此,您必须以稍微不同的方式处理输入的第一条记录。我在函数中添加了代码来解决这个问题,现在它可以工作了。
这是有效的,因为对函数的每次调用,即使在同一个 INSERT 语句中,都在函数内它自己的新“范围”内。(我相信这是正确的解释)。因此,您甚至可以使用此函数通过一条 INSERT 语句插入多行。如果直接从 INSERT 语句调用 IDENT_CURRENT(@variableTableName),它将为所有行中的 newID 分配相同的值。这是因为在整个 INSERT 语句完成处理后(在同一范围内),身份会更新。但是,从函数中调用 IDENT_CURRENT(@variableTableName) 会导致每个插入更新每个输入行的标识值。但是,这一切都是在 INSERT 语句本身的函数调用中完成的。因此,一旦创建了函数,就很容易实现。
这种方法是调用一个函数(来自 INSERT 语句),该函数从函数内的 sys.identity_columns.last_value 中读取(查看它是否为 NULL 以及是否存在记录),然后调用 IDENT_CURRENT(@variableTableName) 和然后从函数返回到 INSERT 语句以插入行。因此,这是一次小读取(对于插入的每一行),然后是插入的一次写入,这比我认为的触发方法的开销要小。如果您将触发器方法用于具有大表的大型数据库中的所有表,则该触发器方法可能会相当低效。与触发器相比,我没有对其进行任何性能分析。但是,我认为这会更有效率,尤其是在大桌子上。
我一直在测试它,这似乎在所有情况下都有效。我欢迎就是否有人发现这不起作用或这种方法是否有任何问题提供反馈。任何人都可以在这种方法中打出漏洞吗?如果是这样,请告诉我。如果没有,你能投票吗?我认为这是一种更好的方法。
所以,也许是因为 COVID-19 而被困住了,结果证明对某些事情很有成效。感谢微软让我忙得不可开交。有人招人吗?:) 不,说真的,有人招聘吗?好吧,既然我已经完成了这个,那么现在我该怎么办呢?:) 祝大家安全。
这是下面的代码。想知道这种方法是否有任何漏洞。欢迎反馈。
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 !
*/