Триггеры DB2 for i — один раз для оператора, но сравнение значений до и после

Можно ли в DB2 для IBM i создать триггер, который запускается один раз для каждой инструкции, но может перебирать все затронутые строки, чтобы определить, действительно ли изменились какие-либо значения?

Вариант использования — обновить материализованную таблицу запросов (MQT) с помощью триггера в базовых таблицах. Но мы хотим обновить MQT только в том случае, если значения действительно изменились. Если мы создадим триггер для запуска один раз для каждой строки, мы сможем получить желаемую функциональность. Но если мы вставляем, обновляем и/или удаляем несколько строк одновременно, инструкция «Обновить» выполняется для каждой строки. И в настоящее время для этого MQT в нашей системе требуется около 10 секунд.

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

Если есть какой-то другой способ автоматического обновления MQT эффективным образом, я также открыт для этого.


person d.lanza38    schedule 21.03.2019    source источник


Ответы (3)


Пример:

create table test (id int not null primary key, a int)@
create table test_mqt (cnt) as (select sum(a) from test) data initially deferred refresh deferred maintained by user@

insert into test values (1, 1), (2, 1), (3, 1) with nc@

create or replace trigger test_aus 
after update on test
referencing 
new table as n
old table as o
for each statement
mode db2sql
begin atomic
  if (exists (select 1 from n,o where n.id=o.id and n.a<>o.a)) then
    refresh table test_mqt;
  end if;
end@

-- trigger IS NOT fired after the following update
update test set a=1 with nc@
-- the following select returns 0
select cnt from test_mqt@

-- trigger IS fired after the following update
update test set a=2 with nc@
-- the following select returns 6
select cnt from test_mqt@
person Mark Barinstein    schedule 21.03.2019
comment
Итак, ссылки на OLD TABLE и NEW TABLE представляют одну строку за раз, но всегда будут перебирать каждую затронутую строку без явных итерационных структур? - person d.lanza38; 21.03.2019
comment
n и o здесь — корреляционные имена для перехода tables (новый и старый наборы затронутых строк), а не отдельных затронутых rows. Цикл по каждой затронутой строке создается оператором SQL select в определении триггера. - person Mark Barinstein; 21.03.2019
comment
Я не думаю, что запуск обновления триггера имеет смысл ... см. мой ответ для альтернативы. - person Charles; 22.03.2019
comment
@Charles Мой пример был в основном о том, как обнаруживать изменения таблицы с помощью триггера for each statement и запускать некоторый оператор, если они обнаружены. Что касается реального обслуживания MQT с помощью триггеров - это может быть более сложное решение. Очевидно, нам нужны триггеры after insert и after delete, а также переписанный триггер after update, если мы не используем полное обновление в какой-то момент и хотим постепенно поддерживать наш MQT. - person Mark Barinstein; 22.03.2019

Да, документация находится здесь. Однако в зависимости от того, как вы пытаетесь это сделать, между командой IBM i ADDPFTRG и командой SQL CREATE TRIGGER есть некоторые различия.

ADDPFTRG не поддерживает триггеры уровня оператора, но CREATE TRIGGER поддерживает.

При использовании триггера уровня оператора вы можете ссылаться на затронутые строки с помощью

REFERENCING OLD TABLE AS ___
            NEW TABLE AS ___

Вы можете обрабатывать старые и новые ссылки на таблицы с помощью операторов SQL так же, как если бы они были обычными таблицами.

Просто для ясности: OLD TABLE ссылается на затронутые строки в том виде, в каком они были до выполнения оператора SQL, а NEW TABLE ссылается на затронутые строки в том виде, в каком они будут после завершения оператора SQL.

person jmarkmurphy    schedule 21.03.2019
comment
Спасибо @jmarkmurphy. Итак, я уже прочитал эту документацию, но что я не нашел интуитивно понятным, так это то, что если я использую FOR EACH STATEMENT, а не FOR EACH ROW, как мне все еще перебирать каждую затронутую строку, чтобы я мог сравнить значения до и после? - person d.lanza38; 21.03.2019
comment
Спасибо за попытку прояснить. Но я не уверен, что понимаю, как перебирать каждую строку. Буду ли я открывать курсор на основе псевдонимов, которые я создал в определении? Так курсор на T1, если бы он определялся как REFERENCING OLD TABLE AS T1...? - person d.lanza38; 21.03.2019
comment
Не думайте об этом как о повторении. Думайте наборами. Соедините их вместе и выберите набор строк с различиями. Если возвращаются какие-либо различия, вы должны обновить свой MQT, если набор возвращается пустым, не обновляйте MQT. - person jmarkmurphy; 21.03.2019
comment
Тем не менее, вы все еще можете обновлять этот MQT часто. Кажется, что если вы хотите синхронизировать вещи, MQT может быть не подходящим способом, а скорее представлением или табличной функцией. - person jmarkmurphy; 21.03.2019
comment
Итак, OLD TABLE и NEW TABLE имеют ту же структуру, что и рассматриваемая таблица, но содержат только затронутые записи и ничего больше. Думаю, теперь я понял. Я думал, что эти ссылки представляют всю таблицу, а не только затронутые строки. - person d.lanza38; 21.03.2019

Я не думаю, что имеет смысл запускать обновление с помощью триггера...

Весь смысл триггера + MQT заключается в том, чтобы триггер напрямую обновлял таблицу MQT. Таким образом, вам нужно иметь дело только с теми строками, которые изменились; вместо того, чтобы заставить БД снова прочитать всю базовую таблицу.

create or replace trigger test_aus 
after update on test
referencing 
new row as n
old row as o
for each row
mode db2sql
begin atomic
  if n.a <> o.a then
    update test_mqt set cnt = cnt + n.a - o.a;
  end if;
end@

Но обратите внимание, что если вас интересует только изменение a, то вы можете определить триггер как

after update of a on test 
for each row 

таким образом триггер будет срабатывать только в том случае, если a действительно обновляется.

Если у вас есть больше SQL, установленных за один раз, обновления выполняются, чем однострочные обновления RPG (или SQL), то, возможно, стоит использовать триггер оператора...

Что-то вроде этого:

create or replace trigger test_aus 
after update on test
referencing 
new table as n
old table as o
for each statement
mode db2sql
begin atomic
   update test_mqt 
     set cnt = cnt + (select sum(n.a - o.a) 
                        from N join O using(id)
                        where n.a <> o.a
                      );

end@

Правка: альтернатива MQT
Я никогда не использовал MQT в производстве, поскольку IBM i не поддерживает поддерживаемые системой MQT.

В сценарии, когда вас интересует обновленный агрегат, индексы кодированных векторов (EVI) с включенными агрегатами могут предоставить лучшее решение; поскольку они поддерживаются автоматически.

CREATE ENCODED VECTOR INDEX sales_fact_location_id_evi 
ON sales_fact(sale_location_id ASC) 
INCLUDE(SUM(sale_amount_measure))

Приведенный выше пример взят из статьи Ускоренная аналитика — более быстрое агрегирование с использованием технологии IBM DB2 for i encoded vector index (EVI) . Это хорошая статья, в которой EVI сравнивается с MQT для хранения агрегатов.

person Charles    schedule 21.03.2019
comment
Что касается реального обслуживания MQT с помощью триггеров. К сожалению, в DB2 для IBM i до сих пор нет refresh immediate или refresh deferred с MQT инкрементного обслуживания, как, например, в Db2 для LUW. Поэтому даже для такого простейшего случая нужны еще триггеры after insert и after delete. Но когда все становится немного сложнее, такая логика триггера может очень скоро стать слишком сложной, и мы, наконец, можем решить, что в какой-то момент придем к такому решению, как запуск полного обновления. - person Mark Barinstein; 22.03.2019
comment
@MarkBarinstein согласен с тем, что, к сожалению, Db2 для i не поддерживает MQT, поддерживаемый системой. Но если вам нужны обновления MQT в реальном времени, то триггеры, обновляющие MQT, — единственный вариант. Если в режиме реального времени не требуется, то можно выполнить запланированное обновление (либо с оператором обновления, либо с пользовательской программой). Наличие триггера, использующего команду обновления, не имеет смысла. - person Charles; 22.03.2019