我正在写一个验证触发器。触发器必须验证数组的总和是否等于另一个字段。由于我有很多这种验证的实例,我想编写一个过程并创建多个触发器,每个触发器都有一组不同的字段要检查。
例如,我有以下架构:
CREATE TABLE daily_reports(
start_on date
, show_id uuid
, primary key(start_on, show_id)
-- _graph are hourly values, while _count is total for the report
, impressions_count bigint not null
, impressions_graph bigint[] not null
-- interactions_count, interactions_graph
-- twitter_interactions_count, twitter_interactions_graph
);
验证必须确认impressions_count = sum(impressions_graph)
.
我被卡住了,因为我不知道如何NEW
从 plpgsql 中动态访问字段:
CREATE FUNCTION validate_sum_of_array_equals_other() RETURNS TRIGGER AS $$
DECLARE
total bigint;
array_sum bigint;
BEGIN
-- TG_NARGS = 2
-- TG_ARGV[0] = 'impressions_count'
-- TG_ARGV[1] = 'impressions_graph'
-- How to access impressions_count and impressions_graph from NEW?
RETURN NEW;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER validate_daily_reports_impressions
ON daily_reports BEFORE INSERT OR UPDATE
FOR EACH ROW EXECUTE
validate_sum_of_array_equals_other('impressions_count', 'impressions_graph');
我尝试通过做来执行动态命令EXECUTE 'SELECT $1 FROM NEW' INTO total USING TG_ARGV[0]
,但是 PL/PGsql 抱怨 NEW 是一个未知的关系。
我专门针对 PostgreSQL 9.1。
实际上,由于
NEW
是一个定义良好的复合类型,您可以使用简单明了的属性表示法访问任何列。SQL 本身不允许使用动态标识符(表或列名等)。但是您可以在 PL/pgSQL 函数中使用动态 SQL 。EXECUTE
演示
演员表
text
是可选的。使用它,因为它普遍适用。如果您知道类型,则无需强制转换即可工作...使用
format()
with%s
,因为此时标识符已被转义。否则,使用
format()
with%I
来防止 SQL 注入。或者,在 Postgres 9.3 或更高版本中,您可以将列转换
NEW
为 JSON,to_json()
并将列作为键访问:由于列名没有串联成 SQL 字符串,因此无法进行 SQL 注入,并且不需要对名称进行转义。
db<>fiddle here(使用
EXCEPTION
而不是NOTICE
使效果可见)。有关的: