不允许时替代 MERGE INTO Oracle 语法的模式

发布于 2024-11-15 09:11:08 字数 415 浏览 3 评论 0原文

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

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

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

I have an application that uses the Oracle MERGE INTO... DML statement to update table A to correspond with some of the changes in another table B (table A is a summary of selected parts of table B along with some other info). In a typical merge operation, 5-6 rows (out of 10's of thousands) might be inserted in table B and 2-3 rows updated.

It turns out that the application is to be deployed in an environment that has a security policy on the target tables. The MERGE INTO... statement can't be used with these tables (ORA-28132: Merge into syntax does not support security policies)

So we have to change the MERGE INTO... logic to use regular inserts and updates instead. Is this a problem anyone else has run into? Is there a best-practice pattern for converting the WHEN MATCHED/WHEN NOT MATCHED logic in the merge statement into INSERT and UPDATE statements? The merge is within a stored procedure, so it's fine for the solution to use PL/SQL in addition to the DML if that is required.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

浅忆流年 2024-11-22 09:11:09

另一种方法(除了合并)是使用两个 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 解决方案的性能不会那么好。

Another way to do this (other than Merge) would be using two sql statements one for insert and one for update. The "WHEN MATCHED" and "WHEN NOT MATCHED" can be handled using joins or "in" Clause.

If you decide to take the below approach, it is better to run the update first (sine it only runs for the matching records) and then insert the non-Matching records. The Data sets would be the same either way, it just updates less number of records with the order below.

Also, Similar to the Merge, this update statement updates the Name Column even if the names in Source and Target match. If you dont want that, add that condition to the where as well.

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;

There could be better ways to do this, but this seems simple and SQL-oriented. If the Data set is Large, then a PL/SQL solution won't be as performant.

橙味迷妹 2024-11-22 09:11:09

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

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

There are at least two options I can think of aside from digging into the security policy, which I don't know much about.

Process the records to merge row by row. Attempt to do the update, if it fails to update then insert, or vise versa, depending on whether you expect most records to need updating or inserting (ie optimize for the most common case that will reduce the number of SQL statements fired), eg:

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;

Another option may be to bulk collect the records you want to merge into an array, and then attempted to bulk insert them, catching all the primary key exceptions (I cannot recall the syntax for this right now, but you can get a bulk insert to place all the rows that fail to insert into another array and then process them).

Logically a merge statement has to check for the presence of each records behind the scenes anyway, and I think it is processed quite similarly to the code I posted above. However, merge will always be more efficient than coding it in PLSQL as it will be only 1 SQL call instead of many.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文