Evi*_*t7x 27 postgresql triggers sql-update
这可能吗?我有兴趣找出在UPDATE
请求中指定了哪些列,而不管正在发送的新值可能是也可能不是已存储在数据库中的值.
我想这样做的原因是因为我们有一个表可以从多个来源接收更新.以前,我们没有记录更新源自哪个来源.现在,该表存储了哪些源执行了最新更新.我们可以更改一些来源以发送标识符,但这不是所有内容的选项.所以我希望能够识别UPDATE
请求何时没有标识符,以便我可以替换默认值.
Erw*_*ter 23
如果"源"不"发送标识符",则列将保持不变.然后,您无法检测当前UPDATE
是由与最后一个源相同的源还是由根本未更改列的源完成.换句话说:这不能正常工作.
如果"源"可由任何会话信息功能识别,则可以使用该功能.喜欢:
NEW.column = session_user;
Run Code Online (Sandbox Code Playgroud)
每次更新都无条件.
我找到了解决原始问题的方法.在未更新列的任何更新中(不在列表中),该列将设置为默认值.SET
UPDATE
关键元素是PostgreSQL 9.0中引入的每列触发器 - 使用该UPDATE OF
column_name
子句的特定于列的触发器.
只有在列出至少一个列出的列作为
UPDATE
命令的目标时,触发器才会触发.
这是我发现的唯一一种简单方法,可以区分列是否使用与旧值相同的新值进行更新,而不是更新.
一个可能还通过解析返回的文本current_query()
.但这似乎很棘手且不可靠.
我假设一个列已col
定义NOT NULL
.
第1步:设置col
为NULL
如果不变:
CREATE OR REPLACE FUNCTION trg_tbl_upbef_step1()
RETURNS trigger AS
$func$
BEGIN
IF OLD.col = NEW.col THEN
NEW.col := NULL; -- "impossible" value
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
第2步:恢复旧值.只有在实际更新了值时才会触发触发器(见下文):
CREATE OR REPLACE FUNCTION trg_tbl_upbef_step2()
RETURNS trigger AS
$func$
BEGIN
IF NEW.col IS NULL THEN
NEW.col := OLD.col;
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
第3步:现在我们可以识别缺少的更新并设置默认值:
CREATE OR REPLACE FUNCTION trg_tbl_upbef_step3()
RETURNS trigger AS
$func$
BEGIN
IF NEW.col IS NULL THEN
NEW.col := 'default value';
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
每列触发步骤2的触发器!
CREATE TRIGGER upbef_step1
BEFORE UPDATE ON tbl
FOR EACH ROW
EXECUTE PROCEDURE trg_tbl_upbef_step1();
CREATE TRIGGER upbef_step2
BEFORE UPDATE OF col ON tbl -- key element!
FOR EACH ROW
EXECUTE PROCEDURE trg_tbl_upbef_step2();
CREATE TRIGGER upbef_step3
BEFORE UPDATE ON tbl
FOR EACH ROW
EXECUTE PROCEDURE trg_tbl_upbef_step3();
Run Code Online (Sandbox Code Playgroud)
触发器名称是相关的,因为它们按字母顺序触发(全部都是BEFORE UPDATE
)!
可以使用"per-not-column triggers"或任何其他方式来简化程序,以检查触发器中的目标列表UPDATE
.但我认为没有办法解决这个问题.
如果col
可以NULL
,使用任何其他"不可能"的中间值并NULL
在触发功能1中另外检查:
IF OLD.col IS NOT DISTINCT FROM NEW.col THEN
NEW.col := '#impossible_value#';
END IF;
Run Code Online (Sandbox Code Playgroud)
相应调整其余部分.
Dem*_*nez 16
另一种方法是利用最新版本的 PostgreSQL 中的 JSON/JSONB 函数。它的优点是可以处理任何可以转换为 JSON 对象(行或任何其他结构化数据)的内容,您甚至不需要知道记录类型。
要查找任何两行/记录之间的差异,您可以使用这个小技巧:
SELECT pre.key AS columname, pre.value AS prevalue, post.value AS postvalue
FROM jsonb_each(to_jsonb(OLD)) AS pre
CROSS JOIN jsonb_each(to_jsonb(NEW)) AS post
WHERE pre.key = post.key AND pre.value IS DISTINCT FROM post.value
Run Code Online (Sandbox Code Playgroud)
哪里OLD
和NEW
是在触发器函数中找到的内置记录,分别代表更改记录的前后状态。请注意,我使用了表别名pre
andpost
而不是old
andnew
以避免与 OLD 和 NEW 内置对象发生冲突。还要注意使用 ofIS DISTINCT FROM
而不是简单的!=
or<>
来NULL
适当地处理值。
当然,这也适用于任何 ROW 构造函数,例如ROW(1,2,3,...)
或其简写(1,2,3,...)
。它也适用于任何两个具有相同键的 JSONB 对象。
例如,考虑一个包含两行的示例(出于示例的目的,已经转换为 JSONB):
SELECT pre.key AS columname, pre.value AS prevalue, post.value AS postvalue
FROM jsonb_each('{"col1": "same", "col2": "prediff", "col3": 1, "col4": false}') AS pre
CROSS JOIN jsonb_each('{"col1": "same", "col2": "postdiff", "col3": 1, "col4": true}') AS post
WHERE pre.key = post.key AND pre.value IS DISTINCT FROM post.value
Run Code Online (Sandbox Code Playgroud)
查询将显示已更改值的列:
columname | prevalue | postvalue
-----------+-----------+------------
col2 | "prediff" | "postdiff"
col4 | false | true
Run Code Online (Sandbox Code Playgroud)
这种方法很酷的一点是按列过滤很简单。例如,假设您只想检测列中的更改,col1
并且col2
:
SELECT pre.key AS columname, pre.value AS prevalue, post.value AS postvalue
FROM jsonb_each('{"col1": "same", "col2": "prediff", "col3": 1, "col4": false}') AS pre
CROSS JOIN jsonb_each('{"col1": "same", "col2": "postdiff", "col3": 1, "col4": true}') AS post
WHERE pre.key = post.key AND pre.value IS DISTINCT FROM post.value
AND pre.key IN ('col1', 'col2')
Run Code Online (Sandbox Code Playgroud)
col3
即使新结果的值发生了变化,它也会从结果中排除:
columname | prevalue | postvalue
-----------+-----------+------------
col2 | "prediff" | "postdiff"
Run Code Online (Sandbox Code Playgroud)
很容易看出如何以多种方式扩展这种方法。例如,假设您想在某些列更新时抛出异常。您可以使用通用触发器函数来实现这一点,即可以应用于任何/所有表的函数,而无需知道表类型:
CREATE OR REPLACE FUNCTION yourschema.yourtriggerfunction()
RETURNS TRIGGER AS
$$
DECLARE
immutable_cols TEXT[] := ARRAY['createdon', 'createdby'];
BEGIN
IF TG_OP = 'UPDATE' AND EXISTS(
SELECT 1
FROM jsonb_each(to_jsonb(OLD)) AS pre, jsonb_each(to_jsonb(NEW)) AS post
WHERE pre.key = post.key AND pre.value IS DISTINCT FROM post.value
AND pre.key = ANY(immutable_cols)
) THEN
RAISE EXCEPTION 'Error 12345 updating table %.%. Cannot alter these immutable cols: %.',
TG_TABLE_SCHEMA, TG_TABLE_NAME, immutable_cols;
END IF;
END
$$
LANGUAGE plpgsql VOLATILE
Run Code Online (Sandbox Code Playgroud)
然后,您可以通过以下方式将上述触发器函数注册到您想要控制的任何和所有表:
CREATE TRIGGER yourtiggername
BEFORE UPDATE ON yourschema.yourtable
FOR EACH ROW EXECUTE PROCEDURE yourschema.yourtriggerfunction();
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
18005 次 |
最近记录: |