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

时间:2012-01-22 16:04:05

标签: tsql merge upsert

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

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

我有两个表,一个名为TempUpsert的表填充SqlBulkCopy操作(百万条记录中的100条)和一个Sales表,其中包含生成数据被索引和使用。

我希望将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行
  违反PRIMARY KEY约束'PK_Sales'。无法在对象中插入重复键   'dbo.Sales'。声明已经终止。

     

(1行受影响)

我做错了什么?

非常感谢

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

总结

MERGE要求其输入(使用)重复免费 Using是一个常规的SQL语句,因此您可以使用Group By,distinct和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

它有效!

谢谢大家:)

答案 2 :(得分:0)

在MERING的US部分中使用的子查询中没有DISTINCT或正确的AGGREGATE函数,将有两行适合MERGE的ON部分使用的标准,这是不允许的。 (两个John.Smith)

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

相反,如果ON子句没有成功,例如John.Smith的得分为2,而@HashMod = 1 - 那么如果你已经在目标表中有了John.Smith的行 - 那么主键会出错约束