Как использовать MERGE в SQL Server, когда у вас есть составной первичный ключ в исходной таблице - PullRequest
2 голосов
/ 30 марта 2019

Я пытаюсь использовать оператор MERGE для обновления целевой таблицы из исходной таблицы, но получаю сообщение об ошибке, поскольку SQL пытается ОБНОВИТЬ или УДАЛИТЬ более одной записи. Для фона - у меня есть «исходная» таблица, которая, по сути, представляет собой скользящую таблицу, в которой записываются все экземпляры данных, добавляемых бизнесом, - для нее используется только автоинкрементный «идентификатор записи». Таблица «target» может иметь только один экземпляр первичного ключа, то есть «string». Идея состоит в том, чтобы посмотреть на исходную таблицу - если первичный ключ совпадает, то UPDATE назвал строки новыми данными, если нет - ВСТАВЬТЕ всю информацию из новых строк. Проблема в том, что я продолжаю получать сообщение об ошибке, что я пытаюсь ОБНОВИТЬ или УДАЛИТЬ одну и ту же запись дважды. Я полагаю, что это происходит потому, что в исходной таблице действительно есть ДВУХ (или более) экземпляров строки (первичного ключа). Единственным отличием является поле date_added. Как я могу переписать это, чтобы включить оба? Я очень новичок в этой функции SQL, и я пробовал несколько вещей, но все возвращают ошибки. Все благодарности другому пользователю, который первым предложил мне MERGE - я пытался использовать IF / THEN.

Я попытался использовать дату записи MAX в предложениях ON и MATCH, которые возвращали ошибки из-за синтаксиса

MERGE 
    SCM_Top_Up_Operational O 
    USING SCM_Top_Up_Rolling R ON (O.String = R.string)
WHEN MATCHED 
    THEN UPDATE SET 
        O.Date_Added    = R.Date_Added,
        O.Real_Exfact   = R.Real_Exfact,
        O.Excess_Top_Up = R.Excess_Top_Up 
WHEN NOT MATCHED BY TARGET 
    THEN INSERT (  String,   Date_Added,   Real_Exfact,   Article_ID,   Excess_Top_Up,   Plant)
         VALUES (R.String, R.Date_Added, R.Real_Exfact, R.Article_ID, R.Excess_Top_Up, R.Plant);

Вот некоторые примеры данных. Если я запрашиваю scm_top_up_rolling для строки в ('B418496220', 'B111116220'), я получаю следующие результаты:

RECORD_ID   String     Date_Added Real_Exfact Article_ID Excess_Top_Up                           Plant
----------- ---------- ---------- ----------- ---------- --------------------------------------- -----
3108        B418496220 2019-02-25 2019-05-15  B41849     1235                                    6220
3211        B418496220 2019-03-28 2019-03-28  B41849     1                                       6220
3212        B111116220 2019-03-28 2019-03-28  B11111     1                                       6220

Теперь, если я запрашиваю scm_top_up_operational для тех же строк, я получаю:

String     Date_Added Real_Exfact Article_ID Excess_Top_Up                           Plant
---------- ---------- ----------- ---------- --------------------------------------- -----
B418496220 2019-02-25 2019-05-15  B41849     1235                                    6220

Моя цель состояла бы в том, чтобы scm_top_up_operational ОБНОВЛЯЛСЯ самой последней записью для B418496220, поскольку она уже существует в операционной таблице. Тогда я хотел бы ВСТАВИТЬ новую запись для B111116220, поскольку она не существует в операционной таблице.

Надеюсь, это поможет и спасибо.

1 Ответ

2 голосов
/ 21 апреля 2019

Вы не можете использовать SCM_Top_Up_Rolling непосредственно в операторе слияния, поскольку ключ (то есть строка), используемый для объединения, должен быть уникальным.

Что вам нужно сделать, это подготовить исходные данные, добавив несколько слоев CTE (Common Table Expression) поверх оператора слияния. Цель этого шага - удалить дубликаты и вернуть уникальный список строк.

Посмотрите на решение ниже:

;with cte
as
(
    select String, Date_Added, Real_Exfact, Article_ID, Excess_Top_Up, Plant
        , row_number() over (partition by String order by Date_Added desc) as 'rank'
    from dbo.SCM_Top_Up_Rolling
)
, cte_source
as
(
    select *
    from cte
    where rank = 1
)
merge SCM_Top_Up_Operational O 
using cte_source R              on (O.String = R.String)
when matched then 
    update set 
    O.Date_Added    = R.Date_Added,
    O.Real_Exfact   = R.Real_Exfact,
    O.Excess_Top_Up = R.Excess_Top_Up 
when not matched by target then 
    insert (  String,   Date_Added,   Real_Exfact,   Article_ID,   Excess_Top_Up,   Plant)
    values (R.String, R.Date_Added, R.Real_Exfact, R.Article_ID, R.Excess_Top_Up, R.Plant);

Ключевым компонентом запроса является оконная функция:

row_number() over (partition by String order by Date_Added desc) as 'rank'

, который генерирует новый столбец rank :

String     Date_Added   rank    
----------------------------    
B418496220 2019-02-25   2
B418496220 2019-03-28   1       <= To be used in the merge, e.g. where rank = 1
B111116220 2019-03-28   1       <= To be used in the merge, e.g. where rank = 1

Как только мы создадим уникальный список (то есть список строк с самой последней датой добавления), мы можем затем использовать этот список в качестве нашего источника для слияния.

merge SCM_Top_Up_Operational O 
using cte_source R              on (O.String = R.String)

Надеюсь, ответ достаточно ясен. Удачи.

...