我正在尝试在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" has no field "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) 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] -- 1 PK col!
WHERE i.indrelid = 't'::regclass
AND i.indisprimary;
-- Get list of columns excluding PK column
_cols := array_to_string(ARRAY(
SELECT 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$ LANGUAGE plpgsql;
通话:
SELECT f_clone_row('t', 1, '"col1"=>"foo_new","col2"=>"bar_new"'::hstore);
使用regclass
作为输入参数类型,因此只能使用有效的表名开头,并且排除SQL注入。如果您应该提供一个非法的表名,该函数也会更早地失败,并且更优雅地出现。
使用regclass
参数(OUT
)简化语法。
无需手动找出主键的下一个值。它会自动插入并在事实结束后返回。这样不仅更简单,更快捷,而且还避免了序列号的浪费或乱序。
使用pk_new
可以简化动态查询字符串的组装,并使其不那么容易出错。请注意如何分别使用位置参数作为标识符和字符串。
我在您的隐式assumption
上构建,该表允许一个表具有一个[[integer类型的单主键列,默认为列]format()
列。format()
:serial
将覆盖值与现有行合并,并立即分解结果行。serial
中选择相关的列。INSERT
子句将其取回。#=
operator参数中。#=
比我想象的要长很多,但是它可以正常工作并且非常快速。
Erwin的解决方案很棒-代码中只有一个错误。这行:在哪里i.indrelid ='t':: regclass
应为:在哪里i.indrelid = in_table_name :: regclass
(对这个额外的答案很抱歉……信誉不足,无法发表评论!)