35

Say I have a table with an identity field. I want to insert a record in it if it doesn't already exist. In the below example, I check if the value stored in @Field1 already exists in the table. If not, I insert a new record:

Definition of the table:

MyTable (MyTableId int Identity not null, Field1 int not null, Field2 int not null)  

This is how I check if the value already exists and insert it if necessary

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);

Getting the identity value when the record didn't already exist in the table can be done by adding:

output Inserted.MyTableId  

but what if the record was already in the table (ie if there was a match)?

The only way I found is to query the table after executing the Merge statement:

select MyTableId from MyTable where Field1=@Field1

Is there a way to get the identity value directly from the Merge?

4

3 回答 3

35

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

DECLARE @MatchedId INTEGER;

MERGE MyTable as t
....
....
WHEN MATCHED THEN
    UPDATE SET @MatchedId = t.MyTableId;

更新:
这是一个完整的例子。这展示了一种方式:

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

需要注意的点是:

  • $action 将为您提供对一行执行的操作类型(INSERT、UPDATE、DELETE)
  • @ChangeResult 表将保存有关所做更改类型的信息
  • 对于 WHEN MATCHED 情况,我基本上是在设置一个虚拟变量。除了确保 UPDATE 路径被命中以在输出中生成 UPDATE 行之外,这在这里没有任何用途。即@UpdateVariable 不用于其他任何事情。如果您确实想更新现有行,那么您应该在此处放置一个适当的 UPDATE ,但如果您不想实际更新现有行,那么这个“虚拟”更新似乎是必需的。
于 2012-05-18T10:25:24.483 回答
31

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

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;

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

于 2015-07-23T05:09:38.890 回答
6

这里有其他选择:

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  ;

如果您检查输出文档

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

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

于 2020-02-18T13:04:25.913 回答