如果匹配,如何在使用MERGE后获得身份值?

Ant*_*ony 29 t-sql

假设我有一个带有身份字段的表.如果它还不存在,我想在其中插入一条记录.在下面的示例中,我检查存储在@ Field1中的值是否已存在于表中.如果没有,我插入一条新记录:

表的定义:

MyTable (MyTableId int Identity not null, Field1 int not null, Field2 int not null)  
Run Code Online (Sandbox Code Playgroud)

这是我检查值是否已存在的方法,并在必要时插入

merge MyTable as t
using (@Field1, @Field2) as s (Field1,Field2)
on (t.Field1=s.Field1)
when not matched then
    insert (Field1,Field2) values (s.Field1,s.Field2);
Run Code Online (Sandbox Code Playgroud)

在表中尚不存在记录时获取标识值可以通过添加:

output Inserted.MyTableId  
Run Code Online (Sandbox Code Playgroud)

但如果记录已经在表中(即如果有匹配)怎么办?

我找到的唯一方法是在执行Merge语句后查询表:

select MyTableId from MyTable where Field1=@Field1
Run Code Online (Sandbox Code Playgroud)

有没有办法直接从Merge获取标识值?

Ada*_*Dev 28

在记录已存在的情况下,您可以将匹配的id存储到变量中,如下所示:

DECLARE @MatchedId INTEGER;

MERGE MyTable as t
....
....
WHEN MATCHED THEN
    UPDATE SET @MatchedId = t.MyTableId;
Run Code Online (Sandbox Code Playgroud)

更新:
这是一个完整的例子.这证明了一种方式:

DECLARE @UpdateVariable bit
DECLARE @ChangeResult TABLE (ChangeType VARCHAR(10), Id INTEGER)
DECLARE @Data TABLE (Id integer IDENTITY(1,1), Val VARCHAR(10))
INSERT @Data ([Val]) VALUES ('A');

MERGE @data AS TARGET
USING (SELECT 'A' AS Val UNION ALL SELECT 'B' AS Val) AS SOURCE ON TARGET.Val = SOURCE.Val
WHEN NOT MATCHED THEN
    INSERT ([Val])
    VALUES (SOURCE.Val)
WHEN MATCHED THEN 
    UPDATE SET @UpdateVariable = 1
OUTPUT $action, inserted.Id INTO @ChangeResult;

SELECT * FROM @data
SELECT * FROM @ChangeResult
Run Code Online (Sandbox Code Playgroud)

要点是:

  • $ action将为您提供对行执行的操作类型(INSERT,UPDATE,DELETE)
  • @ChangeResult表将保存有关所做更改类型的信息
  • 对于WHEN MATCHED案例,我基本上设置了一个虚拟变量.除了确保UPDATE路径被命中以在输出中生成UPDATE行之外,这不起任何作用.即@UpdateVariable不用于其他任何东西.如果你真的想要更新现有的行,那么你可以在这里添加一个正确的UPDATE,但是在你不想实际更新现有行的情况下,似乎需要这个"虚拟"更新.

  • 感谢更新.是的,这样可以工作,但看起来它的代码要比在Merge之后"从MyTable中选择MyTableId,其中Field1 = @ Field1"(显然它取决于真实表的大小等)要多得多.我只是希望在Merge语句中有一个"内置"解决方案,它会在两种情况下返回标识值(匹配或不匹配). (4认同)

Dan*_*ner 24

这是一种替代的,稍微简单的方法(在我看来):

DECLARE @Id [int];

MERGE INTO [MyTable] AS [t]
USING (VALUES
  (@FieldA, @FieldB)
)
 AS [x] (FieldA, FieldB)
 ON [t].[FieldA] = [x].[FieldA]
AND [t].[FieldB] = [x].[FieldB]
WHEN NOT MATCHED BY TARGET THEN
    INSERT (FieldA, FieldB)
    VALUES (FieldA, FieldB)
WHEN MATCHED THEN
    UPDATE SET @Id = [t].[Id]

IF @Id IS NULL
BEGIN
    SET @Id = CAST(SCOPE_IDENTITY() as [int]);
END
SELECT @Id;
Run Code Online (Sandbox Code Playgroud)

如果merge语句导致匹配,@Id则将设置为匹配行的标识.如果不匹配,将插入新行,并准备好从中选择新标识SCOPE_IDENTITY().

  • 不支持多行插入 (2认同)

Jos*_* M. 5

这里有其他选择:

DECLARE @FakeVar BIT

MERGE MyTable AS T
USING VALUES((@Field1, @Field2)) AS S (Field1, Field2)
    ON (T.Field1 = S.Field1)
WHEN NOT MATCHED THEN
    INSERT (Field1, Field2)
    VALUES (S.Field1, T.Field2)
WHEN MATCHED THEN
    UPDATE SET @FakeVar = NULL -- do nothing, only force "an update" to ensure output on updates
OUTPUT INSERTED.MyTableId  ;
Run Code Online (Sandbox Code Playgroud)

如果您查看 OUTPUT 文档

INSERTED 是列前缀,指定插入或更新操作添加的值。

你只需要在更新集子句上做“某事”