我需要在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);
但是,要删除的行必须通过约束检查真的很奇怪。
答案 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 THEN
和WHEN NOT MATCHED THEN
都不允许出现多次。
Oracle显然想提供一个delete选项,并决定采用怪异的方法来扩展update子句,并仅在更新的行上应用删除。
在较新的草稿中(可能是当前的草稿,可以在这里找到:https://www.wiscorp.com/SQLStandards.html)中添加了删除选项,WHEN
子句扩展到WHEN MATCHED [ AND <search condition> ] THEN
和WHEN 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