3

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

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

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

4

2 回答 2

1

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

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

此外,与合并类似,即使源和目标中的名称匹配,此更新语句也会更新名称列。如果您不想要那个,也可以将该条件添加到 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 解决方案的性能将不会那么好。

于 2011-06-15T21:47:48.013 回答
0

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

处理记录以逐行合并。尝试进行更新,如果更新失败则插入,反之亦然,具体取决于您是否希望大多数记录需要更新或插入(即针对最常见的情况进行优化,以减少触发的 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;

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

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

于 2011-06-15T21:16:29.910 回答