SQL 服务器是否有类似 @@IDENTITY 的东西 returns 多个值?
Does SQL Server have something like @@IDENTITY that returns multiple values?
我知道当我将一行插入带有标识列的 table 时,我可以使用 SELECT @@IDENTITY
.
检索我刚刚插入的行的 ID
如果我执行插入语句将十行插入同一个 table,是否有实用的方法来获取所有十行的 ID?
是的,使用 OUTPUT 子句。无论如何,您应该将其作为首选。
更好的是,您可以 return 多个带有 OUTPUT 的字段。因此,您可以使用代理键和自然键填充 table 变量。
带有 INSERT 的 OUTPUT 子句有一个缺陷:在插入之后不可能说出源 table 中的每一行分配了哪个 ID。
declare @Persistent table (Id int not null identity primary key, Value varchar(10));
declare @Temporary table (Id int not null primary key, Value varchar(10));
insert into @Temporary values (11, 'AAA'), (22, 'AAA'), (33, 'CCC');
insert into @Persistent (Value)
output inserted.Id
select Value from @Temporary;
在上面的示例中,我们将在@Persistent table 中获取实际 ID 列表,但我们无法将这些 ID 映射到 @Temporary table 中的 ID,因为 INSERT 中的 OUPTUT 不会允许获取源 table 字段 - 它只获取 INSERTED table 字段。
与 INSERT 不同,MERGE 中的 OUPTUT 子句允许从源 table 获取字段。所以使用 MERGE 的 OUTPUT 解决了问题:
merge into @Persistent as target
using @Temporary as source on source.Id = target.Id
when matched then
update set Value = source.Value
when not matched then
insert (Value) values (Value)
output
inserted.Id, source.Id;
下面是使用 MERGE 命令在父子 table 中插入-更新-删除行的示例。从 MERGE OUTPUT 收集的 target-source-mapping 非常有用:
-- definition of persistent tables
declare @Parents table (Id int not null identity primary key, Name varchar(10));
declare @Children table (Id int not null identity primary key, ParentId int, Name varchar(10));
-- imagine that persistent tables contain some data
insert into @Parents (Name) select 'Alfa';
insert into @Children (ParentId, Name) select 1, 'Delta';
-- definition of temporary tables
declare @TempParents table (Id int not null primary key, Name varchar(10));
declare @TempChildren table (Id int not null primary key, ParentId int, Name varchar(10));
-- data to insert (with negative Ids) and update (with real positive Ids)
insert into @TempParents values
(1, 'Alpha'), (-2, 'Bravo'), (-1, 'Charlie');
insert into @TempChildren values
(-9, 1, 'Alpha-1'), (-8, 1, 'Alpha-2'), (-7, 1, 'Alpha-2'),
(-6, -2, 'Bravo-1'), (-5, -2, 'Bravo-2'), (-4, -2, 'Bravo-3'),
(-3, -1, 'Charlie-1'), (-2, -1, 'Charlie-2'), (-1, -1, 'Charlie-3');
-- table to collect mapping Ids from @TempParents to @Parents
declare @ParentIdMaps table (ParentId int, TempParentId int)
-- merge data into @Parents table and collection of mapping Ids
merge into @Parents as target
using @TempParents as source on source.Id = target.Id
when matched then
update set Name = source.Name
when not matched then
insert (Name) values (Name)
output
inserted.Id, source.Id
into @ParentIdMaps
(ParentId, TempParentId);
-- merge data into @Children table and use of mapping Ids
merge into @Children as target
using
(
select
Id,
ParentId = m.ParentId,
Name
from
@TempChildren tc
inner join @ParentIdMaps m on m.TempParentId = tc.ParentId
)
as source on source.Id = target.Id
when matched then
update set ParentId = source.ParentId, Name = source.Name
when not matched then
insert (ParentId, Name) values (ParentId, Name)
when not matched by source and target.ParentId in (select Id from @TempParents) then
delete;
-- checking the result
-- see that 'Alfa' was renamed to 'Alpha'
-- and 'Delta' was deleted because it was not mentioned in @TempChildren
select
p.*,
c.*
from
@Parents p
inner join @Children c on c.ParentId = p.Id
order by
p.Id,
c.Id;
我知道当我将一行插入带有标识列的 table 时,我可以使用 SELECT @@IDENTITY
.
如果我执行插入语句将十行插入同一个 table,是否有实用的方法来获取所有十行的 ID?
是的,使用 OUTPUT 子句。无论如何,您应该将其作为首选。
更好的是,您可以 return 多个带有 OUTPUT 的字段。因此,您可以使用代理键和自然键填充 table 变量。
带有 INSERT 的 OUTPUT 子句有一个缺陷:在插入之后不可能说出源 table 中的每一行分配了哪个 ID。
declare @Persistent table (Id int not null identity primary key, Value varchar(10));
declare @Temporary table (Id int not null primary key, Value varchar(10));
insert into @Temporary values (11, 'AAA'), (22, 'AAA'), (33, 'CCC');
insert into @Persistent (Value)
output inserted.Id
select Value from @Temporary;
在上面的示例中,我们将在@Persistent table 中获取实际 ID 列表,但我们无法将这些 ID 映射到 @Temporary table 中的 ID,因为 INSERT 中的 OUPTUT 不会允许获取源 table 字段 - 它只获取 INSERTED table 字段。
与 INSERT 不同,MERGE 中的 OUPTUT 子句允许从源 table 获取字段。所以使用 MERGE 的 OUTPUT 解决了问题:
merge into @Persistent as target
using @Temporary as source on source.Id = target.Id
when matched then
update set Value = source.Value
when not matched then
insert (Value) values (Value)
output
inserted.Id, source.Id;
下面是使用 MERGE 命令在父子 table 中插入-更新-删除行的示例。从 MERGE OUTPUT 收集的 target-source-mapping 非常有用:
-- definition of persistent tables
declare @Parents table (Id int not null identity primary key, Name varchar(10));
declare @Children table (Id int not null identity primary key, ParentId int, Name varchar(10));
-- imagine that persistent tables contain some data
insert into @Parents (Name) select 'Alfa';
insert into @Children (ParentId, Name) select 1, 'Delta';
-- definition of temporary tables
declare @TempParents table (Id int not null primary key, Name varchar(10));
declare @TempChildren table (Id int not null primary key, ParentId int, Name varchar(10));
-- data to insert (with negative Ids) and update (with real positive Ids)
insert into @TempParents values
(1, 'Alpha'), (-2, 'Bravo'), (-1, 'Charlie');
insert into @TempChildren values
(-9, 1, 'Alpha-1'), (-8, 1, 'Alpha-2'), (-7, 1, 'Alpha-2'),
(-6, -2, 'Bravo-1'), (-5, -2, 'Bravo-2'), (-4, -2, 'Bravo-3'),
(-3, -1, 'Charlie-1'), (-2, -1, 'Charlie-2'), (-1, -1, 'Charlie-3');
-- table to collect mapping Ids from @TempParents to @Parents
declare @ParentIdMaps table (ParentId int, TempParentId int)
-- merge data into @Parents table and collection of mapping Ids
merge into @Parents as target
using @TempParents as source on source.Id = target.Id
when matched then
update set Name = source.Name
when not matched then
insert (Name) values (Name)
output
inserted.Id, source.Id
into @ParentIdMaps
(ParentId, TempParentId);
-- merge data into @Children table and use of mapping Ids
merge into @Children as target
using
(
select
Id,
ParentId = m.ParentId,
Name
from
@TempChildren tc
inner join @ParentIdMaps m on m.TempParentId = tc.ParentId
)
as source on source.Id = target.Id
when matched then
update set ParentId = source.ParentId, Name = source.Name
when not matched then
insert (ParentId, Name) values (ParentId, Name)
when not matched by source and target.ParentId in (select Id from @TempParents) then
delete;
-- checking the result
-- see that 'Alfa' was renamed to 'Alpha'
-- and 'Delta' was deleted because it was not mentioned in @TempChildren
select
p.*,
c.*
from
@Parents p
inner join @Children c on c.ParentId = p.Id
order by
p.Id,
c.Id;