【问题标题】:Pattern to substitute for MERGE INTO Oracle syntax when not allowed在不允许时替换 MERGE INTO Oracle 语法的模式
【发布时间】:2011-09-15 19:08:03
【问题描述】:

我有一个应用程序使用 Oracle MERGE INTO... DML 语句来更新表 A 以对应另一个表 B 中的一些更改(表 A 是表 B 的选定部分的摘要以及其他一些信息)。在典型的合并操作中,可能会在表 B 中插入 5-6 行(成千上万行)并更新 2-3 行。

事实证明,应用程序将部署在对目标表具有安全策略的环境中。 MERGE INTO... 语句不能与这些表一起使用(ORA-28132:合并到语法不支持安全策略)

所以我们必须更改 MERGE INTO... 逻辑以使用常规插入和更新。这是其他人遇到的问题吗?是否有将合并语句中的 WHEN MATCHED/WHEN NOT MATCHED 逻辑转换为 INSERT 和 UPDATE 语句的最佳实践模式?合并是在存储过程中进行的,因此如果需要,除了 DML 之外,解决方案还可以使用 PL/SQL。

【问题讨论】:

    标签: oracle dml security-policy


    【解决方案1】:

    执行此操作的另一种方法(Merge 除外)是使用两个 sql 语句,一个用于插入,一个用于更新。 “WHEN MATCHED”和“WHEN NOT MATCHED”可以使用joins或“in”子句来处理。

    如果您决定采用以下方法,最好先运行更新(因为它只对匹配的记录运行),然后插入不匹配的记录。无论哪种方式,数据集都是相同的,它只是按照以下顺序更新较少数量的记录。

    此外,与 Merge 类似,即使 Source 和 Target 中的名称匹配,此更新语句也会更新名称列。如果您不希望这样,也可以将该条件添加到 where。

    create table src_table(
       id number primary key,
       name varchar2(20) not null
    );
    
    create table tgt_table(
       id number primary key,
       name varchar2(20) not null
    );
    
    insert into src_table values (1, 'abc');
    insert into src_table values (2, 'def');
    insert into src_table values (3, 'ghi');
    
    insert into tgt_table values (1, 'abc');
    insert into tgt_table values (2,'xyz');
    
    SQL> select * from Src_Table;
    
            ID NAME
    ---------- --------------------
             1 abc
             2 def
             3 ghi
    
    SQL> select * from Tgt_Table;
    
            ID NAME
    ---------- --------------------
             2 xyz
             1 abc
    
    Update tgt_Table tgt
       set Tgt.Name = 
          (select Src.Name
              from Src_Table Src
              where Src.id = Tgt.id
          );
    
    2 rows updated. --Notice that ID 1 is updated even though value did not change
    
    select * from Tgt_Table;
    
       ID NAME
    ----- --------------------
        2 def
        1 abc
    
    insert into tgt_Table
    select src.*
      from Src_Table src,
           tgt_Table tgt
      where src.id = tgt.id(+)
        and tgt.id is null;
    
    1 row created.
    
    SQL> select * from tgt_Table;
    
            ID NAME
    ---------- --------------------
             2 def
             1 abc
             3 ghi
    
    commit;
    

    可能有更好的方法来做到这一点,但这似乎很简单且面向 SQL。如果数据集很大,那么 PL/SQL 解决方案的性能将不会那么好。

    【讨论】:

    • insert 与外连接效率不高。最好使用not in (...) 或not exists (...)。
    【解决方案2】:

    除了深入了解我不太了解的安全策略之外,我至少可以想到两个选择。

    处理记录以逐行合并。尝试进行更新,如果更新失败则插入,反之亦然,具体取决于您是否希望大多数记录需要更新或插入(即针对最常见的情况进行优化,以减少触发的 SQL 语句的数量),例如:

    begin
      for row in (select ... from source_table) loop
        update table_to_be_merged
        if sql%rowcount = 0 then -- no row matched, so need to insert
          insert ...
        end if;
      end loop;
    end;
    

    另一个选项可能是批量收集要合并到数组中的记录,然后尝试批量插入它们,捕获所有主键异常(我现在不记得语法,但你可以得到批量插入将所有插入失败的行放入另一个数组中,然后处理它们。

    从逻辑上讲,无论如何,合并语句必须在幕后检查每条记录的存在,我认为它的处理方式与我上面发布的代码非常相似。然而,合并总是比在 PLSQL 中编码更有效,因为它只有 1 个 SQL 调用,而不是很多。

    【讨论】:

      猜你喜欢
      • 2016-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      • 1970-01-01
      相关资源
      最近更新 更多