postgresql - PL/pgSQL 中的 EXECUTE...INTO...USING 语句无法执行到记录中?

标签 postgresql record plpgsql dynamic-sql composite

我试图在 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/

相关文章:

c# - 需要在 Windows 7 中使用 NAudio 录制声卡中的所有声音

postgresql 四舍五入函数

Python:使用子进程发送postgres密码

postgresql - 在 PostgreSQL 中使用多个模式时找不到表

asterisk - Asterisk 拨号方案中 ${CDR(duration)} 和 ${CDR(billsec)} 之间的区别

record - 更新 Coq 中的单个记录字段

SQL( Redshift ): Select distinct dates for each month from a column of timestamps

performance - Postgres 中的位图堆扫描非常慢

postgresql - Postgres 存储函数可以同时具有返回值和输出参数吗?

sql - 如何将记录作为参数传递给 PL/pgSQL 函数?