这个 T-SQL MERGE 语句有什么问题?

发布于 2024-12-28 11:39:08 字数 2530 浏览 0 评论 0原文

我是 MERGE 新手,我确信我的代码中有一些错误。

此代码将运行并创建我的场景:

我有两个表,一个名为 TempUpsert,由 SqlBulkCopy 操作填充(数以百万计的记录),另一个 >销售 表,保存要索引和使用的生产数据。

我希望将 TempUpsert 表与 Sales 表合并

我显然做错了什么,因为即使是最小的示例也会失败

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TempUpsert]') )
drop table TempUpsert;

CREATE TABLE [dbo].[TempUpsert](
      [FirstName] [varchar](200) NOT NULL,
      [LastName] [varchar](200) NOT NULL,
      [Score] [int] NOT NULL
) ON [PRIMARY] ;

CREATE TABLE [dbo].[Sales](
      [FullName] [varchar](200) NOT NULL,
      [LastName] [varchar](200) NOT NULL,
      [FirstName] [varchar](200) NOT NULL,
      [lastUpdated] [date] NOT NULL,
CONSTRAINT [PK_Sales] PRIMARY KEY CLUSTERED 
(
      [FullName] ASC
)

---- PROC

CREATE PROCEDURE  [dbo].[sp_MoveFromTempUpsert_to_Sales]
(@HashMod int)
AS
BEGIN
      -- SET NOCOUNT ON added to prevent extra result sets from
      -- interfering with SELECT statements.
      SET NOCOUNT ON;

MERGE Sales AS trget
    USING (

    SELECT 
--- Edit: Thanks to Mikal added DISTINCT
DISTINCT
            FirstName, LastName , [Score], LastName+'.'+FirstName  AS FullName
    FROM TempUpsert AS ups) AS src (FirstName, LastName, [Score], FullName)

    ON 
    (
            src.[Score] = @hashMod 
    AND 
            trget.FullName=src.FullName
    )

    WHEN MATCHED 
        THEN 

        UPDATE SET trget.lastUpdated = GetDate() 

      WHEN NOT MATCHED 
            THEN        INSERT   ([FullName], [LastName], [FirstName], [lastUpdated]) 
      VALUES (FullName, src.LastName, src.FirstName, GetDate())

   OUTPUT $action, Inserted.*, Deleted.* ;
      --print @@rowcount

END

GO

---  Insert dummie data

INSERT INTO TempUpsert (FirstName, LastName, Score)
VALUES ('John','Smith',2);


INSERT INTO TempUpsert (FirstName, LastName, Score)
VALUES ('John','Block',2);


INSERT INTO TempUpsert (FirstName, LastName, Score)
VALUES ('John','Smith',2); --make multiple on purpose

----- EXECUTE PROC
GO


DECLARE     @return_value int

EXEC  @return_value = [dbo].[sp_MoveFromTempUpsert_to_Sales]
            @HashMod = 2

SELECT      'Return Value' = @return_value
GO

这返回:

(1 行受影响)
(1 行受影响)
(1 行受影响)

消息 2627,级别 14,状态 1,过程 sp_MoveFromTempUpsert_to_Sales,第 12 行
违反主键约束“PK_Sales”。无法在对象中插入重复的键 'dbo.销售'。该声明已终止。

(1 行受影响)

请问我做错了什么?

非常感谢

I am new to MERGE, and I'm sure I have some error in my code.

This code will run and create my scenario:

I have two tables, one that is called TempUpsert that fills from a SqlBulkCopy operation (100s of millions of records) and a Sales table that holds the production data which is to be indexed and used.

I wish to merge the TempUpsert table with the Sales one

I am obviously doing something wrong as it fails with even the smallest example

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TempUpsert]') )
drop table TempUpsert;

CREATE TABLE [dbo].[TempUpsert](
      [FirstName] [varchar](200) NOT NULL,
      [LastName] [varchar](200) NOT NULL,
      [Score] [int] NOT NULL
) ON [PRIMARY] ;

CREATE TABLE [dbo].[Sales](
      [FullName] [varchar](200) NOT NULL,
      [LastName] [varchar](200) NOT NULL,
      [FirstName] [varchar](200) NOT NULL,
      [lastUpdated] [date] NOT NULL,
CONSTRAINT [PK_Sales] PRIMARY KEY CLUSTERED 
(
      [FullName] ASC
)

---- PROC

CREATE PROCEDURE  [dbo].[sp_MoveFromTempUpsert_to_Sales]
(@HashMod int)
AS
BEGIN
      -- SET NOCOUNT ON added to prevent extra result sets from
      -- interfering with SELECT statements.
      SET NOCOUNT ON;

MERGE Sales AS trget
    USING (

    SELECT 
--- Edit: Thanks to Mikal added DISTINCT
DISTINCT
            FirstName, LastName , [Score], LastName+'.'+FirstName  AS FullName
    FROM TempUpsert AS ups) AS src (FirstName, LastName, [Score], FullName)

    ON 
    (
            src.[Score] = @hashMod 
    AND 
            trget.FullName=src.FullName
    )

    WHEN MATCHED 
        THEN 

        UPDATE SET trget.lastUpdated = GetDate() 

      WHEN NOT MATCHED 
            THEN        INSERT   ([FullName], [LastName], [FirstName], [lastUpdated]) 
      VALUES (FullName, src.LastName, src.FirstName, GetDate())

   OUTPUT $action, Inserted.*, Deleted.* ;
      --print @@rowcount

END

GO

---  Insert dummie data

INSERT INTO TempUpsert (FirstName, LastName, Score)
VALUES ('John','Smith',2);


INSERT INTO TempUpsert (FirstName, LastName, Score)
VALUES ('John','Block',2);


INSERT INTO TempUpsert (FirstName, LastName, Score)
VALUES ('John','Smith',2); --make multiple on purpose

----- EXECUTE PROC
GO


DECLARE     @return_value int

EXEC  @return_value = [dbo].[sp_MoveFromTempUpsert_to_Sales]
            @HashMod = 2

SELECT      'Return Value' = @return_value
GO

This returns:

(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)

Msg 2627, Level 14, State 1, Procedure sp_MoveFromTempUpsert_to_Sales, Line 12
Violation of PRIMARY KEY constraint 'PK_Sales'. Cannot insert duplicate key in object
'dbo.Sales'. The statement has been terminated.

(1 row(s) affected)

What am I doing wrong please?

Greatly appreciated

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

葬心 2025-01-04 11:39:08

临时表中的前两行将为您提供重复的 PK。违反。 Conc 是 PK,并且您插入具有相同值的 tmain+dmain 两次。

The first two rows in your staging table will give you the duplicate PK. violation. Conc is the PK and you insert tmain+dmain with the same value twice.

没企图 2025-01-04 11:39:08

在求和中,

MERGE 要求其输入(使用)不重复
using 是常规 SQL 语句,因此您可以使用 Group By、distinct 和having 以及Where 子句。

我的最终合并看起来像这样:

MERGE Sales AS trget
    USING (

    SELECT     FirstName, LastName, Score, LastName + '.' + FirstName AS FullName
    FROM         TempUpsert AS ups
    WHERE Score = @hashMod  
    GROUP BY FirstName, LastName, Score, LastName + '.' + FirstName

    ) AS src (FirstName, LastName, [Score], FullName)


    ON 
    (
    --        src.[Score] = @hashMod 
    --AND 
            trget.FullName=src.FullName
    )

    WHEN MATCHED 
        THEN 

        UPDATE SET trget.lastUpdated = GetDate() 


      WHEN NOT MATCHED 
            THEN        INSERT   ([FullName], [LastName], [FirstName], [lastUpdated]) 
      VALUES (FullName, src.LastName, src.FirstName, GetDate())

   OUTPUT $action, Inserted.*, Deleted.* ;
      --print @@rowcount

END

而且它有效!

谢谢大家:)

In Summation

MERGE requires its input (Using) to be duplicates free
the Using is a regular SQL statement, so you can use Group By, distinct and having as well as Where clauses.

My final Merge looks like so :

MERGE Sales AS trget
    USING (

    SELECT     FirstName, LastName, Score, LastName + '.' + FirstName AS FullName
    FROM         TempUpsert AS ups
    WHERE Score = @hashMod  
    GROUP BY FirstName, LastName, Score, LastName + '.' + FirstName

    ) AS src (FirstName, LastName, [Score], FullName)


    ON 
    (
    --        src.[Score] = @hashMod 
    --AND 
            trget.FullName=src.FullName
    )

    WHEN MATCHED 
        THEN 

        UPDATE SET trget.lastUpdated = GetDate() 


      WHEN NOT MATCHED 
            THEN        INSERT   ([FullName], [LastName], [FirstName], [lastUpdated]) 
      VALUES (FullName, src.LastName, src.FirstName, GetDate())

   OUTPUT $action, Inserted.*, Deleted.* ;
      --print @@rowcount

END

And it works!

Thanks to you all :)

掌心的温暖 2025-01-04 11:39:08

如果在 MERGE 的 USING 部分中使用的子查询中没有 DISTINCT 或正确的 AGGREGATE 函数,将会有两行适合 MERGE 的 ON 部分中使用的条件,这是不允许的。 (两个John.Smith)

AND

将条件src.[Score] = @hashMod移至子查询内,

如果ON子句不成功,例如John.Smith有分数2,并且 @HashMod = 1 - 那么如果目标表中已经有 John.Smith 的行 - 您将收到主键约束错误

Without DISTINCT or proper AGGREGATE function in subquery used in USING part of MERGE there will be two rows which suits criteria used in ON part of MERGE, which is not allowed. (Two John.Smith)

AND

Move the condition src.[Score] = @hashMod inside the subquery,

instead if ON clause not succeed, for example John.Smith have score of 2, and @HashMod = 1 - then if you already have the row with John.Smith in target table - you'll get an error with Primary Key Constraint

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文