我正在尝试创建一个动态函数以用于设置触发器。
CREATE OR REPLACE FUNCTION device_bid_modifiers_count_per()
RETURNS TRIGGER AS
$$
DECLARE
devices_count INTEGER;
table_name regclass := TG_ARGV[0];
column_name VARCHAR := TG_ARGV[1];
BEGIN
LOCK TABLE device_types IN EXCLUSIVE MODE;
EXECUTE format('LOCK TABLE %s IN EXCLUSIVE MODE', table_name);
SELECT INTO devices_count device_types_count();
IF TG_OP = 'DELETE' THEN
SELECT format(
'PERFORM validate_bid_modifiers_count(%s, %s, OLD.%s, %s)',
table_name,
column_name,
column_name,
devices_count
);
ELSE
SELECT format(
'PERFORM validate_bid_modifiers_count(%s, %s, NEW.%s, %s)',
table_name,
column_name,
column_name,
devices_count
);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
我的问题是执行动态功能validate_bid_modifiers_count()
。当前抛出:
ERROR: query has no destination for result data HINT: If you want to discard the results of a SELECT, use PERFORM instead. CONTEXT: PL/pgSQL function device_bid_modifiers_count_per() line 21 at SQL statement
我实在无法解决这个问题。我知道format()
返回带有参数的函数调用的正确字符串。如何解决并使其正常工作?
答案 0 :(得分:2)
这应该做到:
CREATE OR REPLACE FUNCTION device_bid_modifiers_count_per()
RETURNS TRIGGER AS
$func$
DECLARE
devices_count int := device_types_count();
table_name regclass := TG_ARGV[0];
column_name text := TG_ARGV[1];
BEGIN
LOCK TABLE device_types IN EXCLUSIVE MODE;
EXECUTE format('LOCK TABLE %s IN EXCLUSIVE MODE', table_name);
IF TG_OP = 'DELETE' THEN
PERFORM validate_bid_modifiers_count(table_name
, column_name
, (row_to_json(OLD) ->> column_name)::bigint
, devices_count);
ELSE
PERFORM validate_bid_modifiers_count(table_name
, column_name
, (row_to_json(NEW) ->> column_name)::bigint
, devices_count);
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
错误消息的直接原因是外部SELECT
。如果没有目标,则需要在plpgsql中将其替换为PERFORM
。但是传递给PERFORM
的查询字符串中的内部EXECUTE
也是错误的。 PERFORM
是一个plpgsql命令,在传递给EXECUTE
的SQL字符串中无效,该字符串需要SQL代码。您必须在此处使用SELECT
。最终OLD
和NEW
在EXECUTE
内部不可见,它们各自会以您拥有的方式引发异常。所有问题都可以通过删除EXECUTE
来解决。
从行类型OLD
和NEW
获取动态列名的简单快捷方法:强制转换为json
,然后您可以像演示一样对键名进行参数化。应该比动态SQL的替代方案更简单,更快-这也是可能的,例如:
...
EXECUTE format('SELECT validate_bid_modifiers_count(table_name
, column_name
, ($1.%I)::bigint
, devices_count)', column_name)
USING OLD;
...
相关:
在旁边:不确定为什么需要沉重的锁。
除了2:考虑为每个触发器编写一个单独的触发器函数。嘈杂的DDL,但执行起来更简单,更快。
答案 1 :(得分:0)
正如我在对Erwin Brandstetter's answer的评论中指出的那样,起初我有一个几乎相同的解决方案。
但是问题是我遇到了错误
ERROR: record "new" has no field "column_name"
CONTEXT: SQL statement "SELECT validate_bid_modifiers_count(table_name, column_name, NEW.column_name, devices_count)"
PL/pgSQL function device_bid_modifiers_count_per() line 15 at PERFORM
这就是为什么我认为我需要一种动态评估事物的方法。
当前在以下解决方案中仍然无法解决我的问题(很丑陋,因为我不喜欢2条IF
语句,我希望它具有超级动态性,但是也许我要求太多) :
CREATE OR REPLACE FUNCTION device_bid_modifiers_count_per()
RETURNS TRIGGER AS
$func$
DECLARE
row RECORD;
table_name regclass := TG_ARGV[0];
column_name text := TG_ARGV[1];
devices_count INTEGER;
BEGIN
LOCK TABLE device_types IN EXCLUSIVE MODE;
EXECUTE format('LOCK TABLE %s IN EXCLUSIVE MODE', table_name);
devices_count := device_types_count();
IF TG_OP = 'DELETE' THEN
row := OLD;
ELSE
row := NEW;
END IF;
IF column_name = 'campaign_id' THEN
PERFORM validate_bid_modifiers_count(table_name, column_name, row.campaign_id, devices_count);
ELSIF column_name = 'adgroup_id' THEN
PERFORM validate_bid_modifiers_count(table_name, column_name, row.adgroup_id, devices_count);
ELSE
RAISE EXCEPTION 'invalid_column_name %', column_name;
END IF;
RETURN NEW;
END;
$func$ LANGUAGE plpgsql;
我愿意接受更可靠的解决方案建议。
基本上,第二种条件'a几乎破坏了具有单个功能的目的,在这一点上,我可以将其分为两个功能。因为目标是使用此函数定义多个(2)触发器(为其提供参数)。