У Postgres есть отличное предложение ВОЗВРАТ для INSERT
, DELETE
и UPDATE
... и это делает меня немного жадным. В некоторых случаях я хотел бы получить не только текущее значение, но и предыдущее значение:
UPDATE analytic_productivity
SET points = 1000
WHERE points > 1000
RETURNING id, points, OLD.points;
Я не верю, что есть какой-либо способ получить доступ к предыдущим значениям за пределами срока службы и контекста триггера. Итак, я думаю, что я хотел бы, невозможно как таковое. Если это правильно, кто-нибудь может предложить альтернативу? Я перезаписываю выбросы с некоторыми установленными значениями и хотел бы записать измененные значения в другую таблицу. Вот почему я не знаю текущее значение заранее. Это редкая (и явно подозрительная) операция, и я не хочу записывать изменения при обычных вставках и обновлениях.
В качестве альтернативы я думаю, что могу выбрать выбросы, пересмотреть их, а затем записать изменения. Итак, сделайте большую часть работы на стороне клиента с парой запросов к Postgres. Если да, может ли кто-нибудь предложить правильный уровень блокировки, который можно применить между моим начальным SELECT
и моим следующим UPDATE
? Я считаю, что блокировка FOR UPDATE
правильна.
Любые предложения по интеллектуальному способу захвата предыдущих значений во время обновления без триггера было бы здорово услышать.
Follow-up
Благодаря комментариям я немного поэкспериментировал и нашел решение, которое работает в моем случае. Чтобы сделать мои цели более понятными:
- У меня есть таблица с именем
outlier_rule
, которая определяет значения, которые слишком велики для определенного столбца. - Цель состоит в том, чтобы выполнить цикл потаблицу, и применять правила, чтобы установить выбросы на фиксированное значение.
- Стучать по таким выбросам ... сомнительно. В пользовательском интерфейсе приложения должны быть утечки, которые допускают необоснованные значения. Чтобы отследить их, я записываю большие значения в таблицу с именем
outlier_change
. - . Я бы хотел перенести это поведение в функцию на стороне сервера, чтобы любой из наших серверов, независимо от ихверсия кодовой базы, может вызывать текущую логику.
- Клиентские серверы составляют и отправляют электронную почту со сводкой результатов, когда найдены и исправлены выбросы.
Итак, на стороне сервераФункция делать все, регистрировать некоторые данные и возвращать результат. У меня это работает, но у него запах "Ты не знаешь, что делаешь", так что продолжай добавлять код, пока он не заработает. По крайней мере, я лучше справляюсь с использованием FORMAT и думаю, что теперь я понимаю, что одна функция может делать много вещей, и что вы можете выбирать, что возвращать, с помощью предложения RETURN. Для справки, различные биты кода:
CREATE TABLE IF NOT EXISTS data.outlier_rule (
id uuid NOT NULL DEFAULT extensions.gen_random_uuid(),
schema_name text NOT NULL DEFAULT NULL,
table_name text NOT NULL DEFAULT NULL,
column_name text NOT NULL DEFAULT NULL,
threshold integer,
set_to integer,
CONSTRAINT outlier_rule_id_pkey
PRIMARY KEY (schema_name,table_name,column_name)
);
For tracking the modifications, I've got a second table named outlier_change:
------------------------------
-- Table
------------------------------
DROP TABLE IF EXISTS data.outlier_change CASCADE;
CREATE TABLE IF NOT EXISTS data.outlier_change (
id uuid NOT NULL DEFAULT NULL,
outlier_rule_id uuid NOT NULL DEFAULT NULL,
value_was integer NOT NULL DEFAULT NULL,
set_to integer NOT NULL DEFAULT NULL,
change_count integer NOT NULL DEFAULT 0,
last_changed_dts timestamptz NOT NULL DEFAULT NOW(),
CONSTRAINT outlier_change_id_pkey
PRIMARY KEY (id,outlier_rule_id)
);
ALTER TABLE data.outlier_change OWNER TO user_change_structure;
------------------------------
-- Trigger Function
------------------------------
CREATE OR REPLACE FUNCTION data.on_outlier_change_upsert()
RETURNS pg_catalog.trigger AS $BODY$
BEGIN
NEW.last_changed_dts := NOW();
NEW.change_count := OLD.change_count + 1;
RETURN NEW; -- important!
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
------------------------------
-- Trigger
------------------------------
CREATE TRIGGER outlier_change_upsert BEFORE INSERT OR UPDATE ON data.outlier_change
FOR EACH ROW
EXECUTE PROCEDURE data.on_outlier_change_upsert();
DROP FUNCTION IF EXISTS data.outlier_fix ();
CREATE OR REPLACE FUNCTION data.outlier_fix ()
RETURNS TABLE (
schema_name text,
table_name text,
column_name text,
id uuid,
value_was integer,
set_to integer,
change_count integer
)
AS $$
DECLARE
rule record;
now_ timestamptz = NOW();
BEGIN
FOR rule IN SELECT * FROM data.outlier_rule LOOP
EXECUTE FORMAT (
'INSERT INTO outlier_change (
outlier_rule_id,
set_to,
id,
value_was)
SELECT %6$L,
%5$s,
%2$I.id,
%2$I.%3$I
FROM %1$I.%2$I
WHERE %3$I > %4$s
ON CONFLICT(id,outlier_rule_id) DO UPDATE SET
value_was = EXCLUDED.value_was,
set_to = EXCLUDED.set_to
RETURNING outlier_rule_id,
id,
value_was,
set_to
change_count;
UPDATE %1$I.%2$I
SET %3$I = %5$s
WHERE %3$I > %4$s;',
rule.schema_name,
rule.table_name,
rule.column_name,
rule.threshold,
rule.set_to,
rule.id);
END LOOP;
RETURN QUERY EXECUTE ('
SELECT outlier_rule.schema_name,
outlier_rule.table_name,
outlier_rule.column_name,
outlier_change.id,
outlier_change.value_was,
outlier_change.set_to,
outlier_change.change_count
FROM outlier_change
JOIN outlier_rule ON (outlier_rule.id = outlier_change.outlier_rule_id)
WHERE last_changed_dts = $1')
USING now_;
END;
$$ LANGUAGE plpgsql;
ALTER FUNCTION data.outlier_fix() OWNER TO user_bender;