Что я делаю не так с этим запросом MERGE / INSERT? - PullRequest
0 голосов
/ 23 января 2019

У меня есть таблица базы данных, которую я хочу обновить с помощью SQL.По сути, он содержит набор описательной информации для частей буклета расписания, но это не важно.Некоторые данные вводятся уже через приложение, но это отнимает много времени, и их блоки - это «котельная плита», которую нужно обновлять каждую неделю.В некоторых случаях я, возможно, пропустил ввод данных через приложение и поэтому также хочу автоматически создавать данные, если они не существуют.

Это побуждает меня использовать запрос MERGE следующим образом:

MERGE INTO TTP_LINE_DESCRIPTION o
USING
  (SELECT DISTINCT lv.lv_nv_id TLDE_NV_ID,
                   lv.lv_id TLDE_LV_ID,
                   dir.dir_id TLDE_DIR_ID,
                   8 TLDE_MED_FLAG,
                   1 TLDE_TYPE,
                   0 TLDE_SORT_NO,
                   'Timetable valid from ' || to_char(lv.lv_valid_from,'DD/MM/YYYY') || ' until ' || nvl2(lv.lv_valid_until,to_char(lv.lv_valid_until,'DD/MM/YYYY'),'further notice') TLDE_TEXT,
                   0 TLDE_ALIGNMENT, 
                   null TLDE_FONT_SIZE, 
                   null TLDE_FONT_STYLE 
   FROM LINE_VERSION lv 
   JOIN line_point_sequence lps ON (lv.lv_id = lps.lps_lv_id)
   JOIN direction dir ON (dir.dir_id = lps.lps_dir_id)
   where lv.lv_nv_id=3799 and lv.lv_id=10455244) n

ON (o.TLDE_NV_ID=n.TLDE_NV_ID 
   and o.TLDE_LV_ID=n.TLDE_LV_ID 
   and o.TLDE_DIR_ID=n.TLDE_DIR_ID 
   and o.TLDE_TYPE=n.TLDE_TYPE 
   and o.TLDE_SORT_NO=n.TLDE_SORT_NO)

WHEN MATCHED THEN
   UPDATE SET o.TLDE_TEXT=n.TLDE_TEXT,
      o.TLDE_ALIGNMENT=n.TLDE_ALIGNMENT,
      o.TLDE_FONT_SIZE=n.TLDE_FONT_SIZE,
      o.TLDE_FONT_STYLE=n.TLDE_FONT_STYLE

WHEN NOT MATCHED THEN
   INSERT (o.tlde_id, o.tlde_nv_id, o.tlde_lv_id, o.tlde_dir_id, 
           o.tlde_med_flag, o.tlde_type, o.tlde_sort_no, o.tlde_text,
           o.tlde_alignment, o.tlde_font_size, o.tlde_font_style, 
           o.updated_by, o.updated_on, o.updated_prog)
   VALUES ((select max(tld.tlde_id)+1 from TTP_LINE_DESCRIPTION tld),
           n.tlde_nv_id, n.tlde_lv_id, n.tlde_dir_id, n.tlde_med_flag,
           n.tlde_type, n.tlde_sort_no, n.tlde_text, n.tlde_alignment, 
           n.tlde_font_size, n.tlde_font_style, 'STUARTR', 
           SYSDATE, 'PL/SQL Developer');

Здесь нужно отметить одну вещь: предложение WHERE в SELECT DISTINCT.lv.lv_id=10455244 - это ссылка, которую, я знаю, заставит select возвращать только одну пару строк в этом SELECT, чтобы я мог ограничить свое тестирование.Для этой цели 10455244 является допустимым значением, которого в настоящее время нет в таблице TTP_LINE_DESCRIPTION.

Когда я использую значение в таблице, код WHEN MATCHED выполняется правильно и обновляет пару строк в 0,016.s.

Выполнение оператора SELECT самостоятельно с использованием значения, которое я показал выше, возвращает две строки, которые нужно добавить в 0,109 с.

Получение максимального идентификатора и добавление одного к нему (этоявляется первичным ключом) в соответствии с первым элементом в строке VALUES в конце занимает 0 с.

Наконец, если я напишу INSERT INTO и явно напишу все значения, которые я хочу записать для одного изстроки, я могу сделать вставку одной строки в 0.016 с.

Но собрать все вместе и ... ничего.Выполнение просто сидит там, выполняется, и, похоже, не заканчивается.Или я нервничаю, ожидая, когда это закончится.Я оставил это разумное время, и, кажется, ничего не происходит.

Итак, что происходит, и почему он не будет делать то, что я думаю, что должен?

1 Ответ

0 голосов
/ 23 января 2019

Вы делаете это:

create table t (id, nv_id, val) as (select 1, 101, 'A' from dual);

merge into t o
using (
  select 102 nv_id, 'P' val from dual union all
  select 103 nv_id, 'Q' val from dual ) n
on (o.nv_id = n.nv_id)
when matched then update set val = n.val
when not matched then 
  insert (o.id, o.nv_id, o.val)
  values ((select max(id) + 1 from t), n.nv_id, n.val);

В моем случае это сработало, но id для вставленных строк было таким же: 2. Когда я сделал откат и добавил ограничение первичного ключа:

alter table t add constraint t_pk primary key(id);

merge вызвано ошибка - уникальное ограничение нарушено. Я подозреваю, что это что-то связано с вашим tlde_id, может быть, не ограничение, а что-то еще. Генерация ценностей таким способом является большой нет-нет. Если у вас версия Oracle 12c или выше, вы можете изменить этот столбец на автоматически сгенерированный

generated by default on null as identity

или в более старых версиях используйте последовательность и триггер (найдите max id, добавьте 1 и установите его в качестве начального значения для последовательности)

create sequence seq_t_id start with 2;

create or replace trigger t_on_insert  
  before insert on t for each row
begin
  select seq_t_id.nextval into :new.id from dual;
end;

Затем измените ваш merge, удалив id из предложения вставки, или просто вставьте null, и триггер установит правильные значения:

merge into t o
using (
  select 102 nv_id, 'P' val from dual union all
  select 103 nv_id, 'Q' val from dual ) n
on (o.nv_id = n.nv_id)
when matched then update set val = n.val
when not matched then 
  insert (o.nv_id, o.val)
  values (n.nv_id, n.val);

Даже если это не решит вашу проблему, вы не должны генерировать id s как max() + 1, это вызывает проблемы с параллельными сеансами, фиксациями, несколькими вставками и т. Д.

...