Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Postgresql触发器行为异常_Postgresql_Triggers - Fatal编程技术网

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所做的,类似于上面的。用例需要在记录插入阶段处理运行平衡,所以这就足够了,谢谢。