Postgresql触发器行为异常
我有一张桌子:Postgresql触发器行为异常,postgresql,triggers,Postgresql,Triggers,我有一张桌子: -- TABLE -- DROP SEQUENCE IF EXISTS my_db.tbl_a_id_seq CASCADE; DROP TABLE IF EXISTS my_db.tbl_a CASCADE; CREATE SEQUENCE my_db.tbl_a_id_seq; CREATE TABLE my_db.tbl_a ( id integer NOT NULL DEFAULT nextval('my_db.tbl_a_id_seq'::regclass),
-- TABLE --
DROP SEQUENCE IF EXISTS my_db.tbl_a_id_seq CASCADE;
DROP TABLE IF EXISTS my_db.tbl_a CASCADE;
CREATE SEQUENCE my_db.tbl_a_id_seq;
CREATE TABLE my_db.tbl_a
(
id integer NOT NULL DEFAULT nextval('my_db.tbl_a_id_seq'::regclass),
bill_date timestamp without time zone,
bill_amt numeric(10,2),
charge_id integer,
sp_id integer,
batch_id integer,
bal_before numeric(10,2),
bal_after numeric(10,2),
CONSTRAINT tbl_a_pkey PRIMARY KEY (id)
)
WITH (
OIDS = FALSE
);
DROP INDEX IF EXISTS idx_tbl_a_my_cols CASCADE;
CREATE INDEX idx_tbl_a_my_cols
ON my_db.tbl_a USING btree
(bill_date ASC NULLS LAST, bal_before ASC NULLS LAST);
然后,我用两行种子数据填充上面的表,因此该表现在看起来如下所示:
id | bill_date | bill_amt | charge_id | sp_id | batch_id | bal_before | bal_after
----+----------------------------+----------+-----------+-------+----------+------------+-----------
1 | 2020-04-13 11:21:26.51637 | 10.00 | 1 | 1 | 2 | 200.00 | 190.00
2 | 2020-04-13 11:23:37.317907 | 10.00 | 1 | 1 | 2 | 190.00 | 180.00
-- TRIGGER FUNCTION --
SET search_path TO 'my_db';
CREATE OR REPLACE FUNCTION func_calc_balances() RETURNS TRIGGER AS
$$
BEGIN
NEW.bal_before:=
(
WITH filter_table as (
SELECT id, bill_amt, bal_after
FROM my_db.tbl_a
WHERE charge_id = 1
AND sp_id = 1
AND batch_id = 2
ORDER BY id DESC LIMIT 2
)
SELECT
lag(bal_after, 1) over (order by id)
FROM filter_table
ORDER BY id DESC LIMIT 1
);
NEW.bal_after := (NEW.bal_before - NEW.bill_amt);
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
-- ABOVE FUNCTION'S TRIGGER --
DROP TRIGGER IF EXISTS trigger_calc_balances ON my_db.tbl_a CASCADE;
CREATE TRIGGER trigger_calc_balances
BEFORE INSERT OR UPDATE ON "tbl_a" FOR EACH ROW EXECUTE PROCEDURE func_calc_balances();
然后我创建了一个trigger函数及其触发器,它应该在刚刚插入的行中插入bal_before值,而不是前一行的bal_after值。然后,使用从同一行之前的余额中减去just inserted row的bill_amt列的值(这是从LAG
Postgresql窗口函数计算得出的bal_before值)来填充刚刚插入行的bal_after列。所述功能和触发器如下:
id | bill_date | bill_amt | charge_id | sp_id | batch_id | bal_before | bal_after
----+----------------------------+----------+-----------+-------+----------+------------+-----------
1 | 2020-04-13 11:21:26.51637 | 10.00 | 1 | 1 | 2 | 200.00 | 190.00
2 | 2020-04-13 11:23:37.317907 | 10.00 | 1 | 1 | 2 | 190.00 | 180.00
-- TRIGGER FUNCTION --
SET search_path TO 'my_db';
CREATE OR REPLACE FUNCTION func_calc_balances() RETURNS TRIGGER AS
$$
BEGIN
NEW.bal_before:=
(
WITH filter_table as (
SELECT id, bill_amt, bal_after
FROM my_db.tbl_a
WHERE charge_id = 1
AND sp_id = 1
AND batch_id = 2
ORDER BY id DESC LIMIT 2
)
SELECT
lag(bal_after, 1) over (order by id)
FROM filter_table
ORDER BY id DESC LIMIT 1
);
NEW.bal_after := (NEW.bal_before - NEW.bill_amt);
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
-- ABOVE FUNCTION'S TRIGGER --
DROP TRIGGER IF EXISTS trigger_calc_balances ON my_db.tbl_a CASCADE;
CREATE TRIGGER trigger_calc_balances
BEFORE INSERT OR UPDATE ON "tbl_a" FOR EACH ROW EXECUTE PROCEDURE func_calc_balances();
在向表中插入行时,触发器过程的行为异常;有时它会正确填充,有时它会重复列值,这意味着它不会获取最前面的行以使用它填充下一行。怪异输出的示例如下所示:
如果我使用generate_series函数进行填充,Ubuntu和Windows上会出现奇怪的行为(交替行的前值和后值分别为null bal_和null bal_)
如果我的填充速度有点慢,Windows上会出现奇怪的行为(以前某些bal_中没有更新值)
所需输出如下:
id | bill_date | bill_amt | charge_id | sp_id | batch_id | bal_before | bal_after
----+----------------------------+----------+-----------+-------+-----------+------------+-----------
1 | 2020-04-13 11:29:09.667957 | 10.00 | 1 | 1 | 2 | 200.00 | 190.00
2 | 2020-04-13 11:29:09.667957 | 10.00 | 1 | 1 | 2 | 190.00 | 180.00
3 | 2020-04-13 11:30:09.667957 | 10.00 | 1 | 1 | 2 | 180.00 | 170.00
4 | 2020-04-13 11:28:09.667957 | 10.00 | 1 | 1 | 2 | 170.00 | 160.00
5 | 2020-04-13 11:42:32.655569 | 10.00 | 1 | 1 | 2 | 160.00 | 150.00
6 | 2020-04-13 11:42:43.739626 | 10.00 | 1 | 1 | 2 | 150.00 | 140.00
7 | 2020-04-13 11:42:44.788808 | 10.00 | 1 | 1 | 2 | 140.00 | 130.00
我相信我的触发功能一定有问题。当函数跳过某些行(在所需列中保留空值)时,更新按顺序正常工作,只是跳过了一些行。请帮忙 首先要小心。你正在努力保持跑步平衡。这通常是一个糟糕(而且困难)的想法。在需要时更容易计算动态值在Windows10和Ubuntu19.04上,我无法复制你声称的结果,也无法复制任何相近的结果。然后,您可能需要创建一个
然而,这可能没有必要,至少目前是这样。我认为,你的触发器有一个根本性的缺陷,是由一个错误的假设造成的。触发器似乎是您试图通过获得列后的第二个前bal_来进行补偿。但是,要插入的行尚不存在。因此,您只需要检索最后一行
修改后的触发器(如下)不适用于更新,但也不会有您的原始版本。对于更新,您只需要计算正在更新的行的增量并应用该值。然后将相同的增量应用到后面的所有行(取决于费用、sp和批次ID)。这甚至不考虑删除或多个用户同时命中该表。所有这些你都需要解决。但是,如果您仍想追求运行平衡,则需要插入触发器功能:
create or replace function func_calc_balances()
returns trigger
language plpgsql
as $$
begin
new.bal_before:=
(select bal_after
from my_db_tbl_a
where charge_id = 1
and sp_id = 1
and batch_id = 2
order by id desc limit 1
);
new.bal_after := (new.bal_before - new.bill_amt);
return new;
END;
$$;
当然,问题是lag窗口函数,它在后面取2行而不是1行,这是一个普通的select所做的,类似于上面的。用例需要在记录插入阶段处理运行平衡,所以这就足够了,谢谢。