使用单个SQL同步两个表

时间:2019-01-11 10:03:41

标签: sql oracle merge

我需要在Oracle中同步两个表。我使用MERGE来完成这项工作,但是我需要帮助来获得运行中的SQL来完成这项工作。

我的目标表具有PK和其他一些列。其中一些列没有null约束。

我的源表与目标表具有不同的布局和数据,因此我需要查询源表并将数据转换为目标布局。

我的实际代码是(简体):

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 :(得分:2)

要删除行作为merge语句的一部分,您需要先更新该行。

因此,您需要考虑update语句(直接在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);

我假设源表和目标表中的val1和val2不可为空。修改适当的表。

答案 1 :(得分:0)

看起来很奇怪,Oracle的MERGE只删除已经更新的行,而不是更新或删除行。似乎没有道理,但事实就是如此。 请参阅Boneist的答案以寻求解决方案。

更新:我已经在一些标准SQL草案中进行了查找。据我所知,标准SQL 2003中的MERGE仅支持插入和删除,并且WHEN MATCHED THENWHEN NOT MATCHED THEN都不允许出现多次。

Oracle显然想提供一个delete选项,并决定采用怪异的方法来扩展update子句,并仅在更新的行上应用删除。

在较新的草稿中(可能是当前的草稿,可以在这里找到:https://www.wiscorp.com/SQLStandards.html)中添加了删除选项,WHEN子句扩展到WHEN MATCHED [ AND <search condition> ] THENWHEN NOT MATCHED [ AND <search condition> ] THEN,并且这些子句可以出现多次。这看起来更好。我们希望甲骨文很快会采用这种新语法:-)

  

问题在于,这些与DELETE条件匹配的行将引发ORA-01407:无法将(字符串)更新为NULL。看来,Oracle首先尝试进行更新,然后再进行删除。这会导致错误。

然后也给UPDATE部分设置条件:

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