我试图在 PL/pgSQL 中编写一个函数区域,该区域循环遍历 hstore
并将记录的列(hstore
的键)设置为特定值(hstore
的值)。我正在使用 Postgres 9.1。
hstore
看起来像:' "column1"=>"value1","column2"=>"value2"'
通常,这是我想要的函数,该函数接受一个 hstore
并有一个包含要修改的值的记录:
FOR my_key, my_value IN
SELECT key,
value
FROM EACH( in_hstore )
LOOP
EXECUTE 'SELECT $1'
INTO my_row.my_key
USING my_value;
END LOOP;
我在这段代码中遇到的错误:
“myrow”没有字段“my_key”
。我一直在寻找解决方案很长一段时间,但我为达到相同结果而尝试的所有其他方法都没有奏效。
最佳答案
您发布的答案的更简单替代方案。应该表现得更好。
此函数从给定表 (in_table_name
) 和主键值 (in_row_pk
) 中检索一行,并将其作为新行插入到同一表中,并替换一些值 (in_override_values
)。返回默认的新主键值 ( pk_new
)。
CREATE OR REPLACE FUNCTION f_clone_row(in_table_name regclass
, in_row_pk int
, in_override_values hstore
, OUT pk_new int)
LANGUAGE plpgsql AS
$func$
DECLARE
_pk text; -- name of PK column
_cols text; -- list of names of other columns
BEGIN
-- Get name of PK column
SELECT INTO _pk a.attname
FROM pg_catalog.pg_index i
JOIN pg_catalog.pg_attribute a ON a.attrelid = i.indrelid
AND a.attnum = i.indkey[0] -- single PK col!
WHERE i.indrelid = in_table_name
AND i.indisprimary;
-- Get list of columns excluding PK column
SELECT INTO _cols string_agg(quote_ident(attname), ',')
FROM pg_catalog.pg_attribute
WHERE attrelid = in_table_name -- regclass used as OID
AND attnum > 0 -- exclude system columns
AND attisdropped = FALSE -- exclude dropped columns
AND attname <> _pk; -- exclude PK column
-- INSERT cloned row with override values, returning new PK
EXECUTE format('
INSERT INTO %1$I (%2$s)
SELECT %2$s
FROM (SELECT (t #= $1).* FROM %1$I t WHERE %3$I = $2) x
RETURNING %3$I'
, in_table_name, _cols, _pk)
USING in_override_values, in_row_pk -- use override values directly
INTO pk_new; -- return new pk directly
END
$func$;
调用:
SELECT f_clone_row('tbl', 1, '"col1"=>"foo_new","col2"=>"bar_new"');
db<> fiddle here
<子>旧sqlfiddle
使用 regclass
作为输入参数类型,因此只能使用有效的表名开头,并排除 SQL 注入(inject)。如果您提供非法表名,该函数也会更早更优雅地失败。
使用OUT
参数 ( pk_new
) 来简化语法。
无需手动计算主键的下一个值。它会自动插入并在事后返回。这不仅更简单、更快速,您还可以避免序列号浪费或乱序。
使用 format()
简化动态查询字符串的组装并使其不易出错。请注意我如何将位置参数分别用于标识符和不带引号的字符串。
我基于您隐含的假设,即允许表有一个具有列默认值的整数类型的单个主键列。通常 serial
列。
函数的关键元素是最后的 INSERT
:
使用
#=
operator 将覆盖值与现有行合并在子选择中并立即分解结果行。然后您可以在主
SELECT
中仅选择相关列.让 Postgres 为 PK 分配默认值并用
RETURNING
取回它条款。将返回值写入
OUT
直接参数。所有操作都在一个 SQL 命令中完成,这通常是最快的。
关于postgresql - PL/pgSQL 中的 EXECUTE...INTO...USING 语句无法执行到记录中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17547666/