Как часто будет запускаться триггер FOR EACH STATEMENT, если операция вызвана ограничением FK с помощью UPDATE CASCADE?

11

Я понимаю, что триггер в таблице t, определенной с помощью, FOR EACH STATEMENTбудет запущен один раз, когда я выполню UPDATE t ....

Теперь, когда tопределено с помощью FOREIGN KEY ... REFERENCES a ... ON UPDATE CASCADE, и я обновляю N строк a, вызовет ли триггер вызов один раз или N раз?

Другими словами, являются ли изменения в таблице, каскадные ограничением FK, более похожими на одиночную UPDATEили более на серию UPDATEs?

Ханно Фиц
источник
4
Вы можете создать контрольный пример! Вставьте в другую таблицу в теле триггера и посмотрите, сколько строк вы получите. Тогда напишите это в своем собственном ответе на этот вопрос (это разрешено, даже поощряется)!
Colin 't Hart
2
Упоминание FOR EACH STATEMENTв главном предложении ортогонально остальной части вопроса. Ограничения FK реализуются с помощью специальных триггеров FOR EACH ROW.
Эрвин Брандштеттер,
1
@ Эрвин "ДЛЯ КАЖДОГО РЯДА А" или "ДЛЯ КАЖДОГО РЯДА Т"?
ypercubeᵀᴹ
@ypercube: я добавил ответ с подробностями.
Эрвин Брандштеттер,

Ответы:

6

Ограничения внешнего ключа в настоящее время реализуются с помощью специальных внутренних триггеров. Все они запущены FOR EACH ROW.

Обратите внимание, что это детали реализации, которые могут измениться, поэтому не полагайтесь на это. Но основы не изменились за последние пару основных версий, поэтому серьезные изменения маловероятны.

Я провел быстрый тест с простым ограничением FK от tblдо tbltype. Простой FK реализован с четырьмя простыми внутренними триггерами FOR EACH ROWв моем тесте на стр. 9.4.
Вот краткое изложение того, как исследовать:

SELECT oid  -- 74791
FROM   pg_constraint
WHERE  conrelid = 'tbl'::regclass
AND    contype = 'f';

SELECT objid, classid::regclass  -- 74792,74793,74794,74795 / 'pg_trigger'
FROM   pg_depend
WHERE  refobjid = 74791
AND   deptype = 'i'

SELECT tgrelid::regclass, tgname, tgfoid, tgtype FROM pg_trigger
WHERE  oid IN (74792,74793,74794,74795) ORDER BY tgfoid;

'tbl'    ;'RI_ConstraintTrigger_c_74794';1644;5
'tbl'    ;'RI_ConstraintTrigger_c_74795';1645;17
'tbltype';'RI_ConstraintTrigger_a_74792';1654;9
'tbltype';'RI_ConstraintTrigger_a_74793';1655;17

SELECT oid, proname FROM pg_proc
WHERE oid IN (1654,1655,1644,1645);

1644;'RI_FKey_check_ins'
1645;'RI_FKey_check_upd'
1654;'RI_FKey_noaction_del'
1655;'RI_FKey_noaction_upd'

Два внутренних «noaction» запускаются tbltype.
Два внутренних «проверки» запускаются tbl.
Все они запускаются FOR EACH ROW, как указано нечетными числами в tgtype.

2 байта Postgres tgtype smallintпредставляют int16исходный код на C, где кодируется младший значащий бит TRIGGER_TYPE_ROW. Подробное объяснение здесь:

Вы можете легко проверить это с парой идентичных триггеров, где вы только меняете FOR ROW/ STATEMENT...

Эрвин Брандштеттер
источник
5

Он выполняется N раз, и самый простой способ убедиться в этом - выполнить оператор с EXPLAIN ANALYZEдобавлением, то есть

EXPLAIN ANALYZE UPDATE a SET col = 1 WHERE othercol = 'foo';

Это даст вам информацию, подобную этой:

Trigger for constraint t_col_fk on a: time=1.300 calls=9

(протестировано с 9.2)

Ханно Фиц
источник
1
Это несколько удивительно для меня. Проверено на 9.4 с теми же результатами.
Дезсо