如何使用 postgres 使触发器在会计软件中计算余额

Posted

技术标签:

【中文标题】如何使用 postgres 使触发器在会计软件中计算余额【英文标题】:how to cause a trigger to calculate balances in an accounting software using postgres 【发布时间】:2014-08-04 05:16:12 【问题描述】:

根据我之前回答的问题,我想知道如何使用 postgres 触发器进行类似的余额计算。

how to calculate balances in an accounting software using postgres window function

ID   Date         In       Out    Balance
1    1/1       100.00    0.00   100.00
2    2/1        10.00    0.00   110.00
3    3/1         0.00   70.00    40.00
4    5/1         5.00    0.00    45.00
5    6/1         0.00   60.00   -15.00 

现在我需要一个“触发器”,它会给我以下结果:

ID   Date         In       Out    Balance
1    1/1        100.00    0.00   100.00
2    2/1         10.00    0.00   110.00
3    3/1          0.00   70.00    40.00
6    4/1         20.00    0.00    60.00  <--- inserted new row
4    5/1          5.00    0.00    65.00
5    6/1          0.00   60.00     5.00

如何在 postgres 中创建触发器来更新“连续”行的余额(不更新旧行)?

【问题讨论】:

阅读this @dude ...为什么?那里有什么相关的?请详细说明。是的,这是一个“为我做”的问题,但我不确定该链接是否有用。 @CraigRinger this pdf 文件很好地解释了 Triggers 并带有可以理解的例子,正如你所说的问题是 "do it for me" 这将帮助他理解触发因素,以便他可以轻松地抓住答案因为他的问题是avaialble 【参考方案1】:

这实际上比看起来更难。

可能不止一个人同时插入一行,他们的提交顺序可能与创建行的顺序不同。这意味着计算余额的触发器不会看到它应该看到的行,并且余额将是错误的。

为了使这项工作可靠,您必须在执行INSERT 之前LOCK TABLE ... IN EXCLUSIVE MODE,或者您必须在9.2 或更高版本中使用SERIALIZABLE 隔离。两者都需要重试失败的事务:

SERIALIZABLE 将中止有问题的事务并迫使您重新尝试;和 在触发器中加锁涉及锁升级,这会导致死锁,从而导致事务中止。

因此您的应用必须准备好重试失败的事务。

为避免这种情况,您可以使用READ COMMITTED 隔离,并让应用程序明确地LOCK TABLE ... IN EXCLUSIVE MODE 感兴趣的表之前它会尝试用它做任何其他事情,甚至是SELECT,如果它认为它可能稍后需要INSERT 一行进入它。但是,并发性会有些糟糕。

因此,鉴于此,您可以编写如下内容:

CREATE OR REPLACE FUNCTION balance_insert_trigger() 
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN 
    -- This can deadlock unless you already took the lock earlier
    -- in the transaction; we put it here only for safety to make
    -- sure to block concurrent inserts.
    LOCK TABLE mytable IN EXCLUSIVE MODE;

    IF tg_op = 'INSERT' THEN
        NEW.balance = (SELECT sum(in) - sum(out) FROM mytable);
        RETURN NEW
    ELSE
        -- It's not safe to DELETE FROM or UPDATE this table because of the
        -- running balance.
        RAISE EXCEPTION '% operation not permitted on this table', tg_op;
    END IF;
END;
$$;

CREATE TRIGGER balance_insert_trigger
BEFORE INSERT OR UPDATE OR DELETE ON mytable 
FOR EACH ROW EXECUTE PROCEDURE balance_insert_trigger();

那么总是:

BEGIN;
LOCK TABLE mytable IN EXCLUSIVE MODE;
INSERT INTO mytable ...;
COMMIT;

如果你也想支持UPDATEDELETE,那么事情就会变得令人兴奋。

【讨论】:

哇,看起来很精致。只需先在 Answared 中标记,然后再尝试。非常感谢你们。

以上是关于如何使用 postgres 使触发器在会计软件中计算余额的主要内容,如果未能解决你的问题,请参考以下文章

用友如何删除会计科目

如何创建一个 Postgres 11 触发器函数,该函数在插入或更新到表“a”时在表“b”中插入一个新行?

金蝶财务软件引入会计科目-从模块中引入科目后如何撤销,恢复原来的数据?

编程大白话之-uni-app中计时器的封装

如何在 Hadoop 中计数? [复制]

如何在 Python 中计算学生化残差?