Geo*_*rge 4 postgresql triggers plpgsql
PostgreSQL 10/11。
如果目标单元格值为 ,我需要删除行而不是更新null。
所以我创建了这个触发函数:
CREATE OR REPLACE FUNCTION delete_on_update_related_table() RETURNS trigger
AS $$
DECLARE
refColumnName text = TG_ARGV[0];
BEGIN
IF TG_NARGS <> 1 THEN
RAISE EXCEPTION 'Trigger function expects 1 parameters, but got %', TG_NARGS;
END IF;
EXECUTE 'DELETE FROM ' || TG_TABLE_NAME || ' WHERE $1 = ''$2'''
USING refColumnName, OLD.id;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
还有一个BEFORE UPDATE触发器:
CREATE OR REPLACE FUNCTION delete_on_update_related_table() RETURNS trigger
AS $$
DECLARE
refColumnName text = TG_ARGV[0];
BEGIN
IF TG_NARGS <> 1 THEN
RAISE EXCEPTION 'Trigger function expects 1 parameters, but got %', TG_NARGS;
END IF;
EXECUTE 'DELETE FROM ' || TG_TABLE_NAME || ' WHERE $1 = ''$2'''
USING refColumnName, OLD.id;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
表格很简单:
id uuid primary key
def_id uuid not null
Run Code Online (Sandbox Code Playgroud)
测试:
UPDATE definition_products SET
def_id = NULL
WHERE id = 'f47415e8-6b00-4c65-aeb8-cadc15ca5890';
-- rows affected 0
Run Code Online (Sandbox Code Playgroud)
文档说:
BEFORE 触发的行级触发器可以返回 null以通知触发器管理器跳过该行的其余操作(即,不会触发后续触发器,并且不会对该行发生 INSERT/UPDATE/DELETE)。
以前,我使用 aRULE代替触发器。但无法在同一规则中使用WHERE&子句。RETURNING
您需要带有 RETURNING 子句的无条件 ON UPDATE DO INSTEAD 规则
那么,有办法吗?
虽然杰里米的回答很好,但仍有改进的空间。
您需要非常准确地定义目标。您的陈述:
如果目标单元格值为空,我需要删除行而不是更新。
...并不意味着该列已更改为NULL当前UPDATE状态。可能是NULL在你实施触发器之前。所以不是:
BEFORE UPDATE OF def_id ON public.definition_productsRun Code Online (Sandbox Code Playgroud)
只是:
BEFORE UPDATE ON public.definition_products
Run Code Online (Sandbox Code Playgroud)
当然,如果定义了列NOT NULL(可能应该如此),则没有任何有效的区别 - 除了噪音和额外的故障点。手册:
当特定列的任何列被列为命令列表
UPDATE OFcolumn_name中的目标时,将触发特定于列的触发器(使用语法定义的触发器) 。即使未触发触发器,列的值也可能发生更改,因为不考虑触发器对行内容所做的更改。UPDATESETBEFORE UPDATE
另外,您的问题中没有任何内容表明需要动态 SQL。( 如果您想为不同表上的多个触发器重用相同的触发器函数,情况就会如此。即使如此,出于多种原因,最好只创建几个不同的触发器函数:更简单、更快、更不容易出错、更容易阅读和维护,...)
至于“容易出错”:你原来的动态语句是无效的:
EXECUTE 'DELETE FROM ' || TG_TABLE_NAME || ' WHERE $1 = ''$2'''
USING refColumnName, OLD.id;Run Code Online (Sandbox Code Playgroud)
refColumnName) 传递。$2,它作为值传递,因此不需要引号。TG_TABLE_NAME可能会出错,这对于删除行的重量级函数尤其重要。Jeremy 的版本修复了大部分问题,但仍然具有不合格的TG_TABLE_NAME.
这会很好:
EXECUTE format('DELETE FROM %s WHERE %I = $1', TG_RELID::regclass, refColumnName) -- refColumnName still unquoted
USING OLD.id;
Run Code Online (Sandbox Code Playgroud)
或者:
EXECUTE format('DELETE FROM %I.%I WHERE %I = $1', TG_TABLE_SCHEMA, TG_TABLE_NAME, refColumnName)
USING OLD.id;
Run Code Online (Sandbox Code Playgroud)
有关的:
更简单的触发功能:
CREATE OR REPLACE FUNCTION delete_on_update_related_table()
RETURNS trigger AS
$func$
BEGIN
DELETE FROM public.definition_products WHERE id = OLD.id; -- def_id?
RETURN NULL;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
更简单的触发:
CREATE TRIGGER proper_delete
BEFORE UPDATE ON public.definition_products
FOR EACH ROW
WHEN (NEW.def_id IS NULL) -- that's the defining condition!
EXECUTE PROCEDURE delete_on_update_related_table(); -- no parameter
Run Code Online (Sandbox Code Playgroud)
您可能想使用OLD.id,但不是OLD.def_id。(要删除的行最好由它的 PK 来定义,而不是由更改为 的列来定义NULL。)但这并不完全清楚。