Postgresql Триггер ведет себя странно - PullRequest
0 голосов
/ 13 апреля 2020

У меня есть таблица:

-- 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

Затем я создал триггер функция и ее триггер, который должен вставлять значения bal_before в только что вставленную строку из значения bal_after самой предыдущей строки. После этого заполняют только что вставленный столбец bal_after строки значением, вычисленным путем вычитания значения столбца bill_amt вставленной строки jusst из значения bal_before той же строки (это значение bal_before, которое было вычислено из оконной функции LAG Postgresql). Указанные функция и триггер следующие:

-- 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();

При вставке строк в таблицу процедура триггера действует странно; время от времени он заполняется должным образом, иногда он повторяет значения столбца, что означает, что он не извлекает самую ближайшую предыдущую строку, чтобы использовать ее для заполнения следующей прямой строки. Пример странного вывода приведен ниже:

Странное поведение в Ubuntu и Windows, если я использую функцию generate_series для заполнения (чередующиеся строки имеют нулевые значения 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 |            |
  3 | 2020-04-13 11:52:11.326197 |    10.00 |         1 |     1 |        2 |     190.00 |    180.00
  4 | 2020-04-13 11:52:13.629896 |    10.00 |         1 |     1 |        2 |            |
  5 | 2020-04-13 11:52:14.977964 |    10.00 |         1 |     1 |        2 |     180.00 |    170.00
  6 | 2020-04-13 11:52:16.062277 |    10.00 |         1 |     1 |        2 |            |

Странное поведение при Windows, если я заполняю немного медленно (значения не обновляются в некотором bal_before)

id  |         bill_date          | bill_amt | charge_id | sp_id | batch_id  | bal_before | bal_after
----+----------------------------+----------+-----------+-------+-----------+------------+-----------
1   | 2020-04-13 11:28: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:42:32.655569 |  10.00   |   1       |   1   |   2       |   180.00   |  170.00
5   | 2020-04-13 11:42:43.739626 |  10.00   |   1       |   1   |   2       |   170.00   |  160.00
6   | 2020-04-13 11:42:44.788808 |  10.00   |   1       |   1   |   2       |   170.00   |  160.00

Желаемый результат как следует:

 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

Я считаю, что должна быть проблема в моей функции триггера. Когда функция пропускает некоторые строки (оставляя нулевые значения в нужных столбцах), обновление работает должным образом последовательно, только если оно пропускает некоторые строки. Пожалуйста помогите.

1 Ответ

1 голос
/ 14 апреля 2020

Первое предупреждение . Вы пытаетесь поддерживать текущий баланс. Это вообще плохая (и сложная) идея. Гораздо проще вычислить значение на лету, когда это необходимо. Я не смог воспроизвести результаты, на которые вы претендуете, и ничего похожего, с Postgres 12 ни на Windows 10, ни на Ubuntu 19.04. Затем вам может понадобиться создать Минимум, воспроизводимый пример . Тем не менее, это не может быть необходимым, по крайней мере, пока. Ваш триггер имеет фундаментальный недостаток, вызванный, я думаю, ошибочным предположением. Триггер, кажется, вы пытаетесь компенсировать, получив 2-й предыдущий столбец bal_after. Однако строка, которую вы вставляете , еще не существует . Таким образом, вам нужно получить только последний ряд. Пересмотренный триггер (ниже) не будет работать для обновления, но тогда у вас не будет исходной версии. Для обновления вам нужно только вычислить дельту для обновляемой строки и применить это значение. Затем примените ту же дельту ко всем последующим строкам (в зависимости от начисления, sp и идентификаторов партии). И это даже не касается удаления или нескольких пользователей одновременно. Все, что вам нужно решить. Но если вы все еще хотите продолжить текущий баланс, вам необходима функция триггера вставки:

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;
$$;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...