postgresql中级联触发器的执行顺序

时间:2014-12-02 21:49:35

标签: postgresql triggers

我想知道在PostgreSQL(9.3)中有关于孙子表的UPDATE CASCADE的FK触发器的执行/触发顺序

这就是我所拥有的:

Parent:      Invoices           (invoice_id)
Child:       Invoice_Lines      (invoice_id, line_nbr)
Grandchild:  Invoice_Line_Taxes (invoice_id, line_nbr, tax)

Invoice_Lines has FOREIGN KEY (invoice_id) REFERENCES Invoices ON UPDATE CASCADE;
Invoice_Line_Taxes has FOREIGN KEY (invoice_id, line_nbr) REFERENCES Invoice_Lines ON UPDATE CASCADE;

我还在发票上有一个自定义UPDATE触发器(谁的名字以S开头,后面是RI_)。此触发器总结了发票金额。我的实际更新语句更改了invoice_id,它会传播到子孙。

麻烦的是,在我的自定义触发器中/期间,Invoice_Lines的invoice_id已经更改,但不是孙子Invoice_Line_Taxes。

我使用RAISE从自定义触发器中转储行:

invoice_lines: (5,1)
invoice_line_taxes: (-1,1,HST)

触发后:

SELECT * from invoice_line_taxes where invoice_id IN (5,-1);
 invoice_id | line_nbr | tax_nm
------------+----------+--------
          5 |        1 | HST

所以我想知道,关于级联触发器的触发器执行顺序是什么?

我会假设这样的事情:

RI_on_invoices
RI_on_invoice_lines
S_custom_trigger

有什么想法吗?有人知道在哪里可以得到关于执行顺序的官方文档吗?我已经尝试过查找有关此内容的详细文档,但我发现所有内容都是按字母顺序排列的。也许如果我找到有关订购的具体细节,我可以在它周围建立一些东西。但是现在,我将基于猜测工作。

谢谢。

2 个答案:

答案 0 :(得分:2)

触发器执行顺序确实是按字母顺序排列的,您希望在此处将其理解为ASCII字符串顺序"0" < "A" < "_" < "a",同时请记住"A3" < "a1" < "a10" < "a2"

如果要以特定顺序强制执行,则可以更简单地为触发器名称添加前缀,例如(注意标识符以数字开头时的双引号):

create trigger _01_do_stuff  ...
create trigger "01_do_stuff" ...

旁白:

  • 如果您的应用依赖于按特定顺序触发的触发器,或级联更新发票ID,那么可能表示您的架构,设计中的某些内容有问题,或者在你的代码流中。
  • 如果内存服务,外键在内部强制执行为约束触发器,因此触发器触发的顺序可能会因为你依赖它们来应用逻辑而触发你。
  • 根据经验,避免使触发器对触发它们的表产生副作用,无论是直接还是间接。此规则的唯一例外是在影响触发它们的行的触发器之前。任何其他东西都会很难修复错误。

FWIW,我最好的猜测是你的实际问题是你有两个或三个这些点,而MVCC对你不利。也就是说,触发器可能使PG将原始行标记为已死并插入新的实时行;随后的触发器(例如级联更新?)然后使PG标记新的实时行也是死的,导致另一个带有意外数据的实时行,并且术语可能会触发进一步的副作用的进一步触发。 / p>

换句话说,你的加注通知不是观察最后更新的行,而是一个被一个或多个后续更新压扁的中间行。观察所涉及的行的ctid列应该揭示这一点,如果这确实是正在发生的事情。

最后,请注意explain analyze将显示触发器在目标表上执行的顺序。 (最后我尝试了它,它没有显示级联触发器下降,但在最近的版本中可能已经改变了。)在调试触发器交互或识别性能问题时,使用该命令会很有用。

答案 1 :(得分:0)

在所涉及的表上设置了一些触发器,只是简单地发出了他们要调用的通知,我观察了以下一系列事件(尽管如此,由于上面的答案,EXPLAIN ANALYZE更容易实现这一点):

  1. 发票更新
  2. 按顺序触发发票上的所有触发器
    • 其中一些触发器会影响Invoices_Lines(子表)上的更新
  3. 按顺序触发Invoice_Lines上的所有触发器(但在完成所有父触发器之后)
    • 其中一些触发器会影响Invoice_Line_Taxes(孙子表)上的更新
  4. 按顺序触发Invoice_Line_Taxes上的所有触发器(但在完成其所有父触发器之后)
  5. 因此,触发器的命名仅影响其“本地”执行顺序。

    无论如何,我最终将触发器设置为INITIALLY DEFERRED,并且完成了工作。虽然是的,但我同意,以这种方式使用触发器是微不足道的。