【问题标题】:How to perform two different actions in merge when "not matched"“不匹配”时如何在合并中执行两个不同的操作
【发布时间】:2016-03-21 10:04:28
【问题描述】:

我有这两张桌子

TABLE1
nrb   score  note  source
nrb1    500  abc   e1
nrb2    500  def   e1

TABLE2
nrb   score  note  source
nrb1    500  gls   e1
nrb3    500  dls   e1

在三种情况下,我想执行不同的操作。我需要用新值修改 Table1:

  1. 当匹配时(由 NRB)(例如“nrb1”)我想保留从 table1 到这个 nrb 的值,所以基本上:对这个记录不做任何事情

    李>
  2. 当不匹配 #1 时:table2 中有 nrb,但 table1 中没有(例如 'nrb3')我想将这个 nrb 的值从 table2 插入到 @ 987654331@

  3. 当不匹配时#2:如果table1 中有nrbtable2 中没有(例如'nrb2')我想将table1 中的score 从500 更改为5

所以最后应该是这样的:

TABLE1
nrb   score  note  source
nrb1    500  abc   e1 [stays the same]
nrb2      5  def   e1 [score changed]
nrb3    500  dls   e1 [new record from table2]

我需要使用 Oracle 的 merge 语句来执行此任务,但我不知道如何执行两个不同的操作。

【问题讨论】:

    标签: sql oracle merge


    【解决方案1】:

    您需要使用两个不同的 SQL 执行两个单独的任务。因为 MERGE 不支持 WHEN NOT MATCHED 条件的 UPDATE 子句。

    • 合并插入
    • 更新不存在的地方

    设置

    SQL> CREATE TABLE t1(
      2  nrb VARCHAR2(10), score NUMBER, note VARCHAR2(10), SOURCE VARCHAR2(10)
      3  );
    
    Table created.
    
    SQL> INSERT INTO t1 VALUES('nrb1', 500, 'abc', 'e1');
    
    1 row created.
    
    SQL> INSERT INTO t1 VALUES('nrb2', 500, 'def', 'e1');
    
    1 row created.
    
    SQL> COMMIT;
    
    Commit complete.
    
    SQL> CREATE TABLE t2(
      2  nrb VARCHAR2(10), score NUMBER, note VARCHAR2(10), SOURCE VARCHAR2(10)
      3  );
    
    Table created.
    
    SQL> INSERT INTO t2 VALUES('nrb1', 500, 'gls', 'e1');
    
    1 row created.
    
    SQL> INSERT INTO t2 VALUES('nrb3', 500, 'dls', 'e1');
    
    1 row created.
    
    SQL> COMMIT;
    
    Commit complete.
    

    MERGE INSERT:插入不匹配的行

    SQL> MERGE INTO t1
      2  USING t2
      3  ON (t1.nrb = t2.nrb)
      4  WHEN NOT MATCHED THEN
      5    INSERT
      6      (
      7        nrb,
      8        score,
      9        note,
     10        SOURCE
     11      )
     12      VALUES
     13      (
     14        t2.nrb,
     15        t2.score,
     16        t2.note,
     17        t2.SOURCE
     18      );
    
    1 row merged.
    
    SQL> COMMIT;
    
    Commit complete.
    
    SQL> SELECT * FROM t1;
    
    NRB             SCORE NOTE       SOURCE
    ---------- ---------- ---------- ----------
    nrb1              500 abc        e1
    nrb2              500 def        e1
    nrb3              500 dls        e1
    

    UPDATE 语句:用自定义值更新不匹配的行

    SQL> UPDATE t1
      2  SET    score = 5
      3  WHERE  NOT EXISTS (SELECT NULL
      4                     FROM   t2
      5                     WHERE  t1.nrb = t2.nrb);
    
    1 row updated.
    
    SQL>                    COMMIT;
    
    Commit complete.
    
    SQL> SELECT * FROM t1;
    
    NRB             SCORE NOTE       SOURCE
    ---------- ---------- ---------- ----------
    nrb1              500 abc        e1
    nrb2                5 def        e1
    nrb3              500 dls        e1
    

    NOTE 您可以简单地编写一个 INSERT 语句,而不是仅使用 INSERT 的 MERGE。虽然,从10g 起,The MATCHED 和 NOT MATCHED 子句是可选的,因此您可以有单个 INSERT 或单个 UPDATE 语句。

    【讨论】:

    • 值得注意的是,这会将所有不匹配的值设置为 5。取决于您的数据集和围绕非 500 分数的规则(例如,如果分数是 502?6000?等)然后如果需要,您还需要将其添加到 where 子句中。
    • 完美运行,非常感谢。考虑到您的更新语句,有一个问题,为什么您的“不存在”比简单的“不在(选择”)更优化?像这样:更新 tab1 set score = 5 where nrb not in (select nrb from tab2);跨度>
    • asktom.oracle.com/pls/asktom/… 这是比之前链接的更好的响应。
    • @bobdylan 这里的重点是 MERGE 和非映射行的逻辑,OP 应该能够理解它的范围和其他方面或要求进一步澄清。解决方案在问题中发布的任何 OP 的范围内:-)
    • @ŁukaszStasiak NOT EXISTS 和 NOT IN 之间存在巨大差异。这是一个FAQ,在谷歌中搜索差异,可能Tom Kyte在AskTom中已经回答得很漂亮了。
    【解决方案2】:

    你不能在一次合并中做到这一点。 when not matched 子句只能插入,不能更新,如the syntax diagram 所示。

    同样,when matched 子句只能更新,不能插入。它们之间不能混搭。

    您必须有一个单独的更新声明。你可以合并做第一部分:

    merge into table1 t1
    using (select * from table2) t2
    on (t2.nrb = t1.nrb)
    when not matched then insert (nrb, score, note, source)
      values (t2.nrb, t2.score, t2.note, t2.source);
    

    或执行等效插入:

    insert into table1 (nrb, score, note, source)
    select t2.nrb, t2.score, t2.note, t2.source
    from table2 t2
    where not exists (
      select null
      from table1 t1
      where t1.nrb = t2.nrb
    );
    
    select * from table1;
    
    NRB       SCORE NOT SO
    ---- ---------- --- --
    nrb1        500 abc e1
    nrb2        500 def e1
    nrb3        500 dls e1
    

    然后无论你做什么,更新不匹配的地方:

    update table1 t1
    set score = score/100 -- or fixed value 5; unclear which you need
    where not exists (
      select null
      from table2 t2
      where t2.nrb = t1.nrb
    );
    
    select * from table1;
    
    NRB       SCORE NOT SO
    ---- ---------- --- --
    nrb1        500 abc e1
    nrb2          5 def e1
    nrb3        500 dls e1
    

    如果您真的想这样做,您可以通过合并进行更新,但这会使 using 子句更加复杂 - table1 的子集在 @987654331 中没有匹配记录@ - 所以我看不到任何好处:

    merge into table1 t1
    using (
      select * from table1 t1
      where not exists (
        select null from table2 t2 where t2.nrb = t1.nrb)) t2
    on (t2.nrb = t1.nrb)
    when matched then update set t1.score = score/100;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-19
      • 2021-12-01
      • 2019-12-27
      • 2015-03-23
      • 2020-04-17
      • 1970-01-01
      • 2015-05-13
      相关资源
      最近更新 更多