如果 Identity 不是主键,则 MERGE with IDENTITY_INSERT ON 不起作用

Mag*_*ier 3 sql-server sql-server-2008-r2 identity merge

我经常使用MERGE语句并且对它非常熟悉。现在我遇到了一些表的IDENTITY列不是主键的情况。在这种情况下,尽管在合并语句的生成脚本中检查了标识列的存在并且identity_insert在合并之前显式地打开了标识列,但脚本还是失败了。然而它仍然失败。

我为演示创建了一个较小的示例,该示例失败并抱怨IDENTITYColumn:

无法更新标识列“援助”。

我希望自从我转身之后Identity_Insert ON,我可以INSERT或我喜欢UPDATEIDENTITY列的价值。但它不起作用。

这是示例代码:

CREATE TABLE [dbo].[tm2]
(
    [id] [int] NOT NULL,
    [aid] [int] IDENTITY(1,1) NOT NULL,
    [txt] [nchar](10) NULL,

    CONSTRAINT [PK_tm2] 
       PRIMARY KEY CLUSTERED ([id] ASC) 
            WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

SET IDENTITY_INSERT [dbo].tm2 ON

MERGE INTO [dbo].tm2 AS Target
USING (VALUES
  (1,2,'qdqewqf'),
  (2,3,'#ED7F00')
) AS Source ([ID], [aid], [txt]) ON (Target.[ID] = Source.[ID])

WHEN MATCHED AND (Target.aid <> Source.aid OR Target.txt <> Source.txt ) THEN
    UPDATE 
    SET
       aid = Source.aid, 
       txt = Source.txt

WHEN NOT MATCHED BY TARGET THEN
    INSERT([ID], aid, txt)
    VALUES(Source.[ID], Source.aid, Source.txt)

WHEN NOT MATCHED BY SOURCE THEN 
    DELETE;

SET IDENTITY_INSERT [dbo].tm2 OFF
Run Code Online (Sandbox Code Playgroud)

技术细节:

  • SQL Server 2008 R2
  • 排序规则 SQL_Latin1_General_CP1_CI_AS

Han*_*non 6

无法更新标识值。如果为该表打开,可以插入它们IDENTITY_INSERT

这段代码展示了它是如何工作的:

USE tempdb;

CREATE TABLE dbo.TestIdentity
(
    ID INT NOT NULL
        IDENTITY(1,1)
    , SomeData VARCHAR(255) NOT NULL
);

INSERT INTO dbo.TestIdentity (SomeData)
VALUES ('This is a test');

--This works

SET IDENTITY_INSERT dbo.TestIdentity ON;

INSERT INTO dbo.TestIdentity (ID, SomeData)
VALUES (1, 'This is a test');

SET IDENTITY_INSERT dbo.TestIdentity OFF;

/*
    This fails with:

    Msg 8102, Level 16, State 1, Line 15
    Cannot update identity column 'ID'.
*/
SET IDENTITY_INSERT dbo.TestIdentity ON;

UPDATE dbo.TestIdentity
SET ID = 2
WHERE ID = 1;

SET IDENTITY_INSERT dbo.TestIdentity OFF;
Run Code Online (Sandbox Code Playgroud)

您可能希望将该IDENTITY列替换为使用手动递增值的列。如果您使用的是 SQL Server 2012+,则可以使用 aSEQUENCE来填充值。由于您使用的是 SQL Server 2008 R2,您将需要推出自己的解决方案来生成值来替换标识。此处详细介绍一种这样的方法。

您可以使用该OUTPUT子句同时删除该行,然后将其插入具有修改后的 ID 值的表中,从而潜在地解决您的问题。然而,这是基于不使用合并构造。

/*
    This works, but cannot be used with MERGE
*/
TRUNCATE TABLE dbo.TestIdentity;

INSERT INTO dbo.TestIdentity (SomeData)
VALUES ('This is a test');

SET IDENTITY_INSERT dbo.TestIdentity ON;

DELETE
FROM dbo.TestIdentity
OUTPUT 2 /* The new identity value */
    , deleted.SomeData
INTO dbo.TestIdentity (ID, SomeData)
WHERE ID = 1 /* the old identity value */;

SET IDENTITY_INSERT dbo.TestIdentity OFF;

SELECT *
FROM dbo.TestIdentity
Run Code Online (Sandbox Code Playgroud)