Postgres对于RETURNING clause,INSERT
和DELETE
具有出色的UPDATE
...这让我有点贪婪。在某些情况下,我想获得的不仅是当前值,还有以前的值:
UPDATE analytic_productivity
SET points = 1000
WHERE points > 1000
RETURNING id,
points,
OLD.points;
我不认为有任何方法可以在触发器的寿命和上下文之外访问以前的值。因此,我猜想我不可能做到这一点。如果是正确的话,谁能建议另一种选择?我正在用一些设置值覆盖异常值,并想将修改后的值记录在另一个表中。这就是为什么我事先不知道当前值的原因。这是一种罕见的操作(而且很明显是可疑的),我不想在正常的插入和更新中记录更改。
作为替代方案,我认为我可以选择异常值,对其进行修改,然后写回修改。因此,通过对Postgres的几个请求来完成客户端的大部分工作。如果是这样,有人可以建议在我的初始SELECT
和后续的UPDATE
之间应用正确的锁定级别吗?我相信FOR UPDATE
锁是正确的。
关于在更新过程中无需触发即可捕获先前值的任何智能方式的建议,都将为您提供帮助。
关于查询并插入然后进行更新的建议启发了我尝试一些事情。为了画出更清晰的图片,我有一个带有阈值的实用程序表,可以与用于调整离群值的字段进行比较,如下所示:
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)
);
为了跟踪修改,我还有一个名为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();
我想做的是编写一个可以调用的函数,该函数可以迭代规则,记录异常并更新outlier_rule集中定义的值。因此,遍历outlier_rule,填充outlier_change,然后从outlier_change返回行。我在下面很接近,但是我还没有弄清楚如何重新获得行。问题似乎是查询包含two查询,因此我没有看到第一个查询的结果。我还没有弄清楚我将这些内容推送到输出的方式(或方式)。即,下面的RETURNING
列表。
非常欢迎提出建议。
DROP FUNCTION IF EXISTS data.outlier_fix ();
CREATE OR REPLACE FUNCTION data.outlier_fix ()
RETURNS TABLE (
outlier_rule_id uuid,
id uuid,
value_was integer,
set_to integer,
change_count integer
)
AS $$
DECLARE
rule record;
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;
END;
$$ LANGUAGE plpgsql;
ALTER FUNCTION data.outlier_fix() OWNER TO user_bender;
您可以通过一点技巧来实现。您可以像这样在更新查询中自行加入表:
UPDATE analytic_productivity NEW
SET points = 1000
FROM analytic_productivity OLD
WHERE NEW.points > 1000
and NEW.id = OLD.id
RETURNING NEW.id,
NEW.points,
OLD.points as old_points;