Синхронизировать две таблицы с одним SQL - PullRequest
0 голосов
/ 11 января 2019

Мне нужно синхронизировать две таблицы в Oracle. Я использовал MERGE для этой работы, но мне нужна помощь, чтобы получить работающий SQL для этого.

В моей целевой таблице есть PK и некоторые другие столбцы. Некоторые из этих столбцов не имеют нулевого ограничения.

Моя исходная таблица имеет другой формат и данные, чем моя целевая таблица, поэтому мне нужно запросить исходную таблицу и преобразовать данные в целевой макет.

Мой фактический код (упрощенно):

MERGE INTO TARGET t USING(
    WITH SRC AS ( --do the transformation
        SELECT ID, DECODE(VAL,'THIS','THAT','OTHER') VAL1, REGEXP_SUBSTR(VAL,'\d+') VAL2 FROM SOURCE
    )
    SELECT t.ROWID ROW_ID, s.* FROM SRC s
    FULL OUTER JOIN TARGET t ON s.ID=t.ID
) s ON (t.ROWID=s.ROW_ID)
WHEN MATCHED THEN UPDATE SET t.VAL1=s.VAL1 AND t.VAL2=s.VAL2
DELETE WHERE s.ID IS NULL
WHEN NOT MATCHED THEN INSERT(ID, VAL1, VAL2) VALUES (s.ID, s.VAL1, s.VAL2);

Проблема в том, что эти строки, соответствующие условию DELETE, выдают ORA-01407: cannot update (string) to NULL. Похоже, что Oracle сначала пытается обновить, а затем удалить. Это вызывает ОШИБКУ.

Ключевое слово MERGE действительно ужасно для синхронизации таблиц с удалением, но я бы хотел использовать один запрос, потому что мой SQL преобразования действительно тяжелый.

Есть ли какая-либо альтернатива MERGE или какое-либо предложение, что сделать, чтобы это работало?

Спасибо

Это мое решение. Может быть, это может кому-то помочь.

MERGE INTO TARGET t USING(
    WITH SRC AS ( --do the transformation
        SELECT ID, DECODE(VAL,'THIS','THAT','OTHER') VAL1, REGEXP_SUBSTR(VAL,'\d+') VAL2 FROM SOURCE
    )
    SELECT t.ROWID ROW_ID, NVL2(s.ID,null,1) delFlag, s.* 
    FROM SRC s
    FULL OUTER JOIN TARGET t ON s.ID=t.ID
) s ON (t.ROWID=s.ROW_ID)
WHEN MATCHED THEN UPDATE SET t.VAL1=NVL2(s.delFlag,t.VAL1,s.VAL1) AND t.VAL2=NVL2(s.delFlag,t.VAL1,s.VAL2)
DELETE WHERE s.delFlag IS NOT NULL
WHEN NOT MATCHED THEN INSERT(ID, VAL1, VAL2) VALUES (s.ID, s.VAL1, s.VAL2);

Но действительно странно, что строки, которые будут удалены, должны пройти проверку ограничений.

Ответы [ 2 ]

0 голосов
/ 11 января 2019

Чтобы удалить строки как часть оператора слияния, необходимо сначала обновить строку.

Следовательно, вам необходимо учитывать нулевые значения в операторе обновления (либо непосредственно в предложении set, либо в исходном запросе), например ::

MERGE INTO target t
  USING (WITH src AS ( --do the transformation
                      SELECT id,
                             DECODE(val, 'THIS', 'THAT', 'OTHER') val1,
                             regexp_substr(val, '\d+') val2
                      FROM   SOURCE)
         SELECT t1.rowid row_id,
                s1.id,
                NVL(s1.val1, t1.val1) val1,
                NVL(s1.val2, t1.val2) val2
         FROM   src s1
         FULL   OUTER JOIN target t1
         ON     s1.id = t1.id) s ON (t.rowid = s.row_id)
WHEN MATCHED THEN
  UPDATE SET t.val1 = s.val1 AND t.val2 = s.val2
  DELETE WHERE s.id IS NULL
WHEN NOT MATCHED THEN
  INSERT (id, val1, val2)
  VALUES (s.id, s.val1, s.val2);

N.B. Я предполагаю, что val1 и val2 в исходной и целевой таблицах не обнуляются. Внесите необходимые изменения в свои таблицы.

0 голосов
/ 11 января 2019

Как ни странно, вместо того, чтобы обновлять или удалять строки, Oracle MERGE удаляет только те строки, которые уже обновлены. Кажется, не имеет смысла, но это так. Пожалуйста, смотрите ответ Boneist для решения.

Обновление: я искал это в некоторых стандартных черновиках SQL. Исходя из того, что я понял, MERGE в стандартном SQL 2003 поддерживал только вставку и удаление, а WHEN MATCHED THEN и WHEN NOT MATCHED THEN не разрешалось появляться более одного раза.

Oracle, очевидно, хотел предоставить опцию удаления и решил, что странный подход заключается в расширении предложения обновления и применении удалений только к обновленным строкам.

В более новом черновике (предположительно текущем, найденном здесь: https://www.wiscorp.com/SQLStandards.html) добавлена ​​опция удаления, предложения WHEN расширены до WHEN MATCHED [ AND <search condition> ] THEN и WHEN NOT MATCHED [ AND <search condition> ] THEN, и эти предложения могут отображаться более чем один раз. Это выглядит намного лучше. Будем надеяться, что в скором времени Oracle примет новый синтаксис: -)

Проблема заключается в том, что эти строки, соответствующие условию DELETE, выдают ORA-01407: невозможно обновить (строку) до NULL. Похоже, что Oracle сначала пытается обновить, а затем удалить. Это вызывает ОШИБКУ.

Затем задайте для части ОБНОВЛЕНИЕ также условие:

WHEN MATCHED THEN
  UPDATE SET t.VAL1=s.VAL1 AND t.VAL2=s.VAL2 WHERE s.ID IS NOT NULL
  DELETE WHERE s.ID IS NULL

...