Шаблон для замены синтаксиса MERGE INTO Oracle, когда это не разрешено - PullRequest
3 голосов
/ 15 июня 2011

У меня есть приложение, которое использует оператор Oracle MERGE INTO ... DML для обновления таблицы A, чтобы соответствовать некоторым изменениям в другой таблице B (таблица A представляет собой сводку выбранных частей таблицы B вместе с некоторой другой информацией).В типичной операции объединения 5-6 строк (из 10 тысяч) могут быть вставлены в таблицу B, а 2-3 строки обновлены.

Оказывается, что приложение должно быть развернуто в среде, котораяимеет политику безопасности на целевых таблицах.Оператор MERGE INTO ... нельзя использовать с этими таблицами (ORA-28132: объединение в синтаксис не поддерживает политики безопасности)

Поэтому нам необходимо изменить логику MERGE INTO ..., чтобы она использовалась регулярновставляет и обновляет вместо.Кто-то еще сталкивался с этой проблемой?Существует ли шаблон наилучшей практики для преобразования логики WHEN MATCHED / WHEN NOT MATCHED в операторе слияния в операторы INSERT и UPDATE?Слияние происходит в рамках хранимой процедуры, поэтому для решения хорошо использовать PL / SQL в дополнение к DML, если это требуется.

Ответы [ 2 ]

1 голос
/ 16 июня 2011

Другой способ сделать это (кроме слияния) - использовать два оператора 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 не будет таким производительным.

0 голосов
/ 16 июня 2011

Есть, по крайней мере, два варианта, о которых я могу подумать, кроме как копаться в политике безопасности, о которых я не знаю много.

Обрабатывать записи для слияния строка за строкой.Попытайтесь выполнить обновление, если оно не обновляется, затем вставьте или наоборот, в зависимости от того, ожидаете ли вы, что большинство записей нуждаются в обновлении или вставке (т. Е. Оптимизируйте для наиболее распространенного случая, который уменьшит количество запущенных операторов 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, поскольку это будет всего один вызов SQL вместо многих.

...