Iva*_*van 9 postgresql trigger dynamic-sql plpgsql
我正在尝试在 Postgres 9.4 中编写一个触发器函数。像这样的东西(还没有工作):
CREATE FUNCTION set_point_from_coords(source _regclass, target _regclass)
RETURNS trigger AS $func$
BEGIN
NEW.target := ST_SetSRID(ST_Point(NEW.source[1], NEW.source[2]), 4326);
RETURN NEW;
END;
$func$ LANGUAGE plpgsql
Run Code Online (Sandbox Code Playgroud)
在这种情况下,target
是一个类型的列geometry
并且source
是一个小数数组。
当一行插入coords
数组时,我想将其转换为point
. 如果我只是对列名进行硬编码,则上述方法会起作用,但我想使用相同的函数为不同的表和不同的列对执行此操作。而且我对它INSERT
本身没有直接控制权。
这是我的一些实验:http : //sqlfiddle.com/#!15/ddddcd/1
找到了这篇相关的博客文章,我很难解析它。
如果这样更容易编码,我可以在插入/更新之后而不是之前运行它。
Erw*_*ter 17
你必须选择所有可能的并发症聚集在一起的地方。
SQL(或 PL/pgSQL)不允许参数化标识符。EXECUTE
为此,您需要使用动态 SQL 。
但是NEW
触发器函数中的特殊 plpgsql 变量在使用EXECUTE
.
通过将列名作为参数传递给CREATE TRIGGER
.
并且使目标列动态化是不够的,您想从该行的另一个动态列中获取源值。
除非您对所有相关问题都了如指掌,否则我宁愿尝试更简单的方法。为每个触发器编写单独的触发器函数,并分配给目标列,无需动态 SQL。
这就是说,它可以用做一个代码行-和解释很多行。对于问题中的原始示例,假设此表定义:
CREATE TABLE tbl (
tbl_id serial PRIMARY KEY,
geom geometry,
coords double precision[]
);
Run Code Online (Sandbox Code Playgroud)
您需要hstore
安装附加模块(每个数据库一次)才能工作。或者,您也可以将json_populate_record()
/jsonb_populate_record()
的未记录功能用于相同目的:
CREATE OR REPLACE FUNCTION trg_demo()
RETURNS trigger AS
$func$
BEGIN
EXECUTE format('SELECT ($1 #= hstore(%L, ST_SetSRID(ST_Point($1.%2$I[1], $1.%2$I[2]), 4326)::text)).*'
, TG_ARGV[0], TG_ARGV[1]) -- target (geom), source (coords)
USING NEW
INTO NEW;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
扳机:
CREATE TRIGGER demo
BEFORE INSERT OR UPDATE ON tbl
FOR EACH ROW EXECUTE PROCEDURE trg_demo('geom', 'coords');
Run Code Online (Sandbox Code Playgroud)
如果你不明白这里发生了什么,请考虑我上面的建议。
我格式化了geometry
粗体的动态计算,以帮助您掌握这一点。与下面的简单案例进行比较。
db<> fiddle here - 使用point
而不是geometry
,因为未安装 PostGIS。
旧的sqlfiddle。
这是一个更简单的版本,只分配空text
值(这需要目标列geom
是 type text
)。再次加粗的简化部分:
EXECUTE format('SELECT ($1 #= hstore(%L, $1.%I::text)).*'
, TG_ARGV[0], TG_ARGV[1]) -- target (geom), source (coords)
USING NEW
INTO NEW;
Run Code Online (Sandbox Code Playgroud)
核心功能是hstore 运算符#=
(根据文档):
record
用匹配的值替换中的字段hstore
目标和源都是新行的列,这使问题复杂化。如果源值是一个常量,我们可以简单地:
NEW := NEW #= hstore(TG_ARGV[0], 'POINT(123.0 456.0)');
Run Code Online (Sandbox Code Playgroud)
有关的:
但是我们需要动态 SQL 来解析列名并获取源值。
用于format()
安全地连接查询字符串。
TG_ARGV[0]
并将TG_ARGV[1]
前两个元素访问到(从 0 开始!)传递的参数文本数组CREATE TRIGGER
。
%I
连接format()
作为标识符传递给的参数(安全防止 SQL 注入)。
$1
引用传递到值EXECUTE
中USING
的条款。
我们需要::text
在计算几何图形后进行转换,因为hstore
需要text
.
我们需要NEW
将分配的行分解回来,因为 plpgsql 将行分配为逐列的目标。