Проблемы с оператором SQL Server MERGE

Таблица источников

Id, Name, Address
1   A     #202
1   A     #203
1   A     #204
2   A     #202

Таблица целей

Id, Name, Address
1   A     NULL

После объединения

Id, Name, Address
1   A     #202
2   A     #202

Я использую этот SQL

create table #S   (ID int, Name varchar(25) NULL, Address varchar(25) NULL)
create table #T   (ID int, Name varchar(25) NULL, Address varchar(25) NULL)

 INSERT #S values(1, 'A', '#202')
 INSERT #S values(1, 'A', '#203')
 INSERT #S values(1, 'A', '#203')
 INSERT #S values(1, 'A', '#204')

 INSERT #T values(1, 'A', NULL)

 MERGE #T USING
  (
Select id, name, address 
from #S
  ) AS S(id,name,address)
 on #T.id=S.id and #T.Name=S.Name
 when not matched THEN
    INSERT values(S.id,S.Name, S.Address)
 when matched then
    update set Address = S.Address;
 GO 

 Select * from #T
 GO 

 Select * from #S
 GO 

Это вызывает ошибку

Msg 8672, уровень 16, состояние 1, строка 18
Заявление MERGE пыталось ОБНОВИТЬ или УДАЛИТЬ одну и ту же строку более одного раза. Это происходит, когда целевая строка соответствует более чем одной строке источника. Оператор MERGE не может обновлять/удалять одну и ту же строку целевой таблицы несколько раз. Уточните предложение ON, чтобы гарантировать, что целевая строка соответствует не более одной исходной строке, или используйте предложение GROUP BY для группировки исходных строк.

Я хочу обновить строку в с помощью значения Address из любого из трех значений соответствия. Как это сделать?

Ответы

Ответ 1

Любое из четырех значений в #S будет соответствовать вашему единственному значению целевой таблицы (все значения в #S имеют id = 1 и name = 'A' - поэтому все они соответствуют одной строке в целевом объекте), таким образом это значение будет обновляться четыре раза - это то, что говорит ошибка, и это абсолютно правильно.

Что вы действительно хотите здесь достичь?

Вы хотите установить адрес для первого из значений из исходной таблицы? Используйте предложение TOP 1 в вашем подзапросе:

MERGE #T 
USING (SELECT TOP 1 id, name, address FROM #S) AS S
ON #T.id = S.id AND #T.Name = S.Name
WHEN NOT MATCHED THEN
    INSERT VALUES(S.id,S.Name, S.Address)
WHEN MATCHED THEN
    UPDATE SET Address = S.Address;

Вы хотите установить адрес в случайный элемент из значений из исходной таблицы? Используйте предложение TOP 1 и ORDER BY NEWID() в вашем подзапросе:

MERGE #T 
USING (SELECT TOP 1 id, name, address FROM #S ORDER BY NEWID()) AS S
ON #T.id = S.id AND #T.Name = S.Name
WHEN NOT MATCHED THEN
    INSERT VALUES(S.id,S.Name, S.Address)
WHEN MATCHED THEN
    UPDATE SET Address = S.Address;

Если вы сопоставляете четыре строки источника с одной целевой строкой, вы никогда не получите полезный результат - вам нужно знать, что вы действительно хотите.

Марк

Ответ 2

Удалите дубликат, используя

select R.* 
from  (SELECT Customer,Material,Received_date_time,
row_number() over (Partition by Customer, Material   
order by  Customer,Material,Received_date_time) as rn
      from Customer_Table WHERE Status=0     
     ) as R
where R.rn = 1

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