【问题标题】:When doing a MERGE in Oracle SQL, how can I update rows that aren't matched in the SOURCE?在 Oracle SQL 中执行 MERGE 时,如何更新 SOURCE 中不匹配的行?
【发布时间】:2012-05-10 17:54:36
【问题描述】:

我有一个main 数据库和一个report 数据库,我需要将一个表从main 同步到report

但是,当 main 数据库中的项目被删除时,我只想在 report 数据库中设置一个 IsDeleted 标志。

有什么优雅的方法可以做到这一点?

我目前正在使用 MERGE 语句,如下所示:

MERGE INTO report.TEST target
USING (SELECT * FROM main.TEST) source
   ON (target.ID = source.ID)
WHEN MATCHED THEN
    UPDATE SET (target... = source...)
WHEN NOT MATCHED THEN
    INSERT (...) VALUES (source...)
;

WHEN NOT MATCHED 语句为我提供了来自main 的所有新值,但我还想更新来自report 的所有旧值。

我正在使用 Oracle PL/SQL。

【问题讨论】:

  • 您要使用哪些值更新main.test?你会从哪里得到它们?
  • 如果从main.test中删除该行,则对应的report.test行将不匹配。我想为所有不匹配的行设置report.test.IsDeleted 标志。
  • 我认为这是不可能的,因为底层 JOIN (using (...)) 将不包含该行。那么这些数据应该从哪里来呢?
  • 那么,MERGE 是否类似于 LEFT OUTER JOIN?我想我希望可能有一个FULL OUTER JOIN 选项。
  • 我认为 SQL Server 2008 有一个 WHEN NOT MATCHED ON SOURCE 子句执行 FULL OUTER JOIN!我找不到太多关于它的文档,但我在 Oracle 中尝试过但没有成功,所以我想我会看看其他选项。

标签: sql oracle sql-merge


【解决方案1】:

您可以使用单独的 UPDATE 语句来做到这一点

UPDATE report.TEST target
SET    is Deleted = 'Y'
WHERE  NOT EXISTS (SELECT 1
                   FROM   main.TEST source
                   WHERE  source.ID = target.ID);

我不知道有什么方法可以将它整合到您的 MERGE 语句中。

【讨论】:

  • 我不能用 MERGE 来做这件事似乎很奇怪。我会考虑这个答案。
  • 如果我做了一个OUTER JOIN 并在哪里更新main.test is null,会不会更“优雅”/高效?
  • 这里有一个相关问题stackoverflow.com/questions/4863960/…,我认为它会进行外连接,而不是完全外连接,所以我认为您的建议不会起作用。
  • 非常感谢,那个链接很有帮助 我学到的东西:如果MERGE 只有WHEN MATCHED THEN,它使用常规的JOIN。如果有WHEN NOT MATCHED THEN,它使用LEFT OUTER JOIN。现在,我希望有一个功能,例如WHEN NOT MATCHED ON SOURCE THEN,它将执行FULL OUTER JOIN。但这可能只适用于 SQL Server,而不适用于 Oracle。
【解决方案2】:

下面的答案是将数据合并到同一张表中

MERGE INTO YOUR_TABLE d
USING (SELECT 1 FROM DUAL) m
    ON ( d.USER_ID = '123' AND d.USER_NAME= 'itszaif') 
WHEN NOT MATCHED THEN
        INSERT ( d.USERS_ID, d.USER_NAME)
        VALUES ('123','itszaif');

该命令检查USER_IDUSER_NAME是否匹配,如果不匹配则插入。

【讨论】:

    【解决方案3】:
    MERGE INTO target
    USING
    (
        --Source data
        SELECT id, some_value, 0 deleteMe FROM source
        --And anything that has been deleted from the source
        UNION ALL
        SELECT id, null some_value, 1 deleteMe
        FROM
        (
            SELECT id FROM target
            MINUS
            SELECT id FROM source
        )
    ) source
       ON (target.ID = source.ID)
    WHEN MATCHED THEN
        --Requires a lot of ugly CASE statements, to prevent updating deleted data
        UPDATE SET target.some_value =
            CASE WHEN deleteMe=1 THEN target.some_value ELSE source.some_value end
        ,isDeleted = deleteMe
    WHEN NOT MATCHED THEN
        INSERT (id, some_value, isDeleted) VALUES (source.id, source.some_value, 0)
    
    --Test data
    create table target as
    select 1 ID, 'old value 1' some_value, 0 isDeleted from dual union all
    select 2 ID, 'old value 2' some_value, 0 isDeleted from dual;
    
    create table source as
    select 1 ID, 'new value 1' some_value, 0 isDeleted from dual union all
    select 3 ID, 'new value 3' some_value, 0 isDeleted from dual;
    
    
    --Results:
    select * from target;
    
    ID  SOME_VALUE   ISDELETED
    1   new value 1  0
    2   old value 2  1
    3   new value 3  0
    

    【讨论】:

    • 感谢您的创意!我不会这样做,但它确实可以完成工作。
    【解决方案4】:
    merge into x as target using y as Source on target.ID = Source.ID
    when not matched by target then insert
    when matched then update
    when not matched by source and target.ID is not null then
    update whatevercolumn = 'isdeleted' ;
    

    【讨论】:

    • 你在哪里找到了这个语法? (by target)
    • 这正是我正在寻找的......但是,正如@FlorinGhita 所说,你在哪里找到这个语法?这是 MS SQL 语法吗?
    • -1 因为这是 SQL Server merge 语法而不是 Oracle merge
    • 仅供大家参考,这是一个很好的答案,但是not matched by targetnot matched by source是只有MS SQL Server才支持的功能。这个特性可以解决我的问题,所以我希望 Oracle SQL 支持它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-04
    • 2017-09-25
    • 1970-01-01
    • 1970-01-01
    • 2014-01-23
    • 2017-05-31
    • 1970-01-01
    相关资源
    最近更新 更多