触发自引用 table

Trigger on self-referenced table

我有一个像这样的 invoices table :

CREATE TABLE invoices (
    idinvoice SERIAL PRIMARY KEY
    ,idclient INTEGER REFERENCES clients(idclient)
    -- other fields here
);

和一个 invoiceitems table :

CREATE TABLE invoiceitems (
    idinvoiceitem SERIAL PRIMARY  KEY
    ,idinvoice INTEGER REFERENCES invoices(idinvoice)
    ,amount NUMERIC(15,2)
    ,sortorder INTEGER DEFAULT 10
);

我的问题是 sortorder 字段:每次插入或更新发票项目时,我都希望使用这样的查询重新计算同一张发票的所有项目的 sortorders :

WITH pre AS (
    SELECT idinvoiceitem,10*ROW_NUMBER() OVER (ORDER BY sortorder,idinvoiceitem) AS rownum
    FROM invoiceitems
    WHERE idinvoice=xx
    ORDER BY sortorder,idinvoiceitem
)
UPDATE invoiceitems di
SET sortorder=rownum
FROM pre p
WHERE p.idinvoiceitem=di.idinvoiceitem;

这样,每次添加新项目时,都会编号为 10+最后一项,如果我决定通过手动设置值来更改项目的排序顺序,所有 sortorder s 将以 10 为增量重新计算:

id | sortorder
---+----------
 1 | 10
 2 | 20
 3 | 30
 4 | 40  ← I change this to 25

然后查询后得到:

id | sortorder
---+----------
 1 | 10
 2 | 20
 4 | 30
 3 | 40

我想把它放在触发器中。问题是第 4 项将被触发器更新(因此再次调用触发器),我将得到某种无休止的递归。

有办法吗?例如,设置一些 semaphore 以防止在触发器本身发生更新时调用触发器?或者可能是显式锁定?

(使用 PostgreSQL 11+)

您可以做的一件事是使用 pg_trigger_depth() 函数,该函数记录在 here.

那么您的触发器应该与此类似:

CREATE TRIGGER trigger_name
AFTER UPDATE OR INSERT ON invoiceitem
FOR EACH ROW
WHEN (pg_trigger_depth() = 0)
EXECUTE PROCEDURE your_procedure();

我建议阅读有关此方法的一些讨论 (1, 2),但通常对于不复杂的系统,这应该没问题。