Что не так с этим оператором T-SQL MERGE? - PullRequest
4 голосов
/ 22 января 2012

Я новичок в MERGE, и я уверен, что в моем коде есть какая-то ошибка.

Этот код запустится и создаст мой сценарий:

У меня есть две таблицы, одна из которых называется TempUpsert, которая заполняется из операции SqlBulkCopy (100 миллионов записей), и таблица Sales, которая содержит производственные данные, которые нужно проиндексировать и использовать.

Я хочу объединить таблицу TempUpsert с Sales one

Я, очевидно, что-то делаю не так, потому что это не получается даже с самым маленьким примером

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 ]

2 голосов
/ 22 января 2012

Первые две строки в вашей промежуточной таблице дадут вам дубликат PK. нарушение. Conc - это PK, и вы дважды вставляете tmain + dmain с одинаковым значением.

1 голос
/ 23 января 2012

В суммировании

MERGE требует, чтобы его вход (Использование) был свободен от дубликатов. Использование - это обычный оператор SQL, так что вы можете использовать Group By, различные и имеющие, а также предложения 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

И это работает!

Спасибо всем вам:)

0 голосов
/ 22 января 2012

Без DISTINCT или надлежащей функции AGGREGATE в подзапросе, используемом в части USER части MERGE, будет две строки, которые соответствуют критериям, используемым в ON части MERGE, что недопустимо. (Два Джона. Смит)

И

Переместить условие src.[Score] = @hashMod в подзапрос,

вместо этого, если предложение ON не выполнено, например, John.Smith имеет оценку 2 и @HashMod = 1 - тогда, если у вас уже есть строка с John.Smith в целевой таблице - вы получите ошибку с Primary Key Constraint

...