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

问题描述 投票:4回答:2

我正在尝试在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"。我一直在寻找解决方案已经有一段时间了,但是我尝试达到相同结果的其他所有方法均无效。

postgresql record plpgsql dynamic-sql composite
2个回答
8
投票

您发布的答案的简单替代方法。应该表现得更好。

此函数从给定的表(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);

SQL Fiddle.

  • 使用regclass作为输入参数类型,因此只能使用有效的表名开头,并且排除SQL注入。如果您应该提供一个非法的表名,该函数也会更早地失败,并且更优雅地出现。

  • 使用regclass参数(OUT)简化语法。

  • 无需手动找出主键的下一个值。它会自动插入并在事实结束后返回。这样不仅更简单,更快捷,而且还避免了序列号的浪费或乱序。

  • 使用pk_new可以简化动态查询字符串的组装,并使其不那么容易出错。请注意如何分别使用位置参数作为标识符和字符串。

  • 我在您的隐式assumption

    上构建,该表允许一个表具有一个[[integer类型的单主键列,默认为列]
  • 。通常为format()列。
  • 该功能的关键元素是最后的format()

      使用子选择中的serial将覆盖值与现有行合并,并立即分解结果行。
  • 然后您只能在主serial中选择相关的列。
  • 让Postgres为PK分配默认值,并通过INSERT子句将其取回。
  • 直接将返回值写到#= operator参数中。
  • 全部在单个SQL命令中完成,通常最快。
  • 由于我不想为了提高速度而必须使用任何外部函数,所以我创建了一个使用hstore将记录插入表中的解决方案:

    #=

    比我想象的要长很多,但是它可以正常工作并且非常快速。

  • 1
    投票
    由于我不想为了提高速度而必须使用任何外部函数,所以我创建了一个使用hstore将记录插入表中的解决方案:

    0
    投票

    Erwin的解决方案很棒-代码中只有一个错误。这行:在哪里i.indrelid ='t':: regclass

    应为:在哪里i.indrelid = in_table_name :: regclass

    (对这个额外的答案很抱歉……信誉不足,无法发表评论!)

    © www.soinside.com 2019 - 2024. All rights reserved.