【问题标题】:How to execute a string result of a stored procedure in postgres如何在postgres中执行存储过程的字符串结果
【发布时间】:2015-03-04 16:52:11
【问题描述】:

我创建了以下存储过程,它基本上接收一个表名和一个前缀。然后,该函数查找共享此前缀的所有列,并作为输出返回“选择”查询命令(“myoneliner”)。 如下:

CREATE OR REPLACE FUNCTION mytext (mytable text, myprefix text)
RETURNS text AS $myoneliner$
declare
    myoneliner text;
BEGIN
   SELECT 'SELECT ' || substr(cols,2,length(cols)-2) ||' FROM '||mytable 
   INTO myoneliner  
     FROM (
        SELECT array(
           SELECT DISTINCT quote_ident(column_name::text)
           FROM   information_schema.columns
           WHERE  table_name = mytable
           AND    column_name LIKE myprefix||'%'
           order by quote_ident             
      )::text cols 
     ) sub;
   RETURN myoneliner;
END;
$myoneliner$ LANGUAGE plpgsql;

呼叫:

select mytext('dkj_p_k27ac','enri');

由于运行了这个存储过程和跟随它的“选择”,我在“数据输出”窗口中得到了以下输出(都在一个单元格中,名为“mytext text”):

'SELECT enrich_d_dkj_p_k27ac,enrich_lr_dkj_p_k27ac,enrich_r_dkj_p_k27ac
 FROM dkj_p_k27ac'

我希望基本上能够将收到的输出命令行作为输出并执行它。换句话说,我希望能够并执行我的存储过程的输出。 我该怎么做?

我尝试了以下方法:

CREATE OR REPLACE FUNCTION mytext (mytable text, myprefix text)
RETURNS SETOF RECORD AS $$
declare
        smalltext text;
    myoneliner text;
BEGIN
   SELECT 'SELECT ' || substr(cols,2,length(cols)-2) ||' FROM '||mytable 
   INTO myoneliner  
     FROM (
        SELECT array(
           SELECT DISTINCT quote_ident(column_name::text)
           FROM   information_schema.columns
           WHERE  table_name = mytable
           AND    column_name LIKE myprefix||'%'
           order by quote_ident             
      )::text cols 
     ) sub;

   smalltext=lower(myoneliner);
   raise notice '%','my additional text '||smalltext;
   RETURN QUERY EXECUTE smalltext;
END;
$$ LANGUAGE plpgsql;

调用函数:

SELECT * from mytext('dkj_p_k27ac','enri');

但是我收到以下错误消息,请您告知我应该更改哪些内容才能执行?:

ERROR:  a column definition list is required for functions returning "record"
LINE 26: SELECT * from mytext('dkj_p_k27ac','enri');

********** Error **********

ERROR: a column definition list is required for functions returning "record"
SQL state: 42601
Character: 728

【问题讨论】:

  • RETURN QUERY EXECUTE。但是,您的过程必须声明为RETURNING SETOF RECORD,然后调用者必须枚举调用中的列,这使得它没有用处。或者,您可以打开一个游标和RETURNS REFCURSOR,然后调用者可以从游标FETCH - 但在这种情况下,您不能将结果用作子查询等的一部分,只能将它们流式传输到客户端应用程序。
  • 这看起来像是inner platform effect 的一个实例。我质疑设计。改用hstorejson 列怎么样?
  • 您需要以select * from my_function(...)调用该过程
  • @Roy 这就是我要说的——你必须识别查询中的列,所以它不会给你带来太多好处。我说过“然后调用者将不得不枚举调用中的列”。您正在尝试做一些系统确实不擅长或专门设计的事情。
  • @Roy 有关信息,请参阅 the manual setof record

标签: postgresql stored-procedures plpgsql dynamic-sql


【解决方案1】:

您的第一个问题是通过使用动态 SQL 和 EXECUTE 解决的,就像 Craig 建议的那样。 但兔子洞更深:

CREATE OR REPLACE FUNCTION myresult(mytable text, myprefix text)
  RETURNS SETOF RECORD AS
$func$
DECLARE
   smalltext  text;
   myoneliner text;
BEGIN
   SELECT INTO myoneliner  
          'SELECT '
        || string_agg(quote_ident(column_name::text), ',' ORDER BY column_name)
        || ' FROM ' || quote_ident(mytable)
   FROM   information_schema.columns
   WHERE  table_name = mytable
   AND    column_name LIKE myprefix||'%'
   AND    table_schema = 'public';  -- schema name; might be another param

   smalltext := lower(myoneliner);  -- nonsense
   RAISE NOTICE 'My additional text: %', myoneliner;

   RETURN QUERY EXECUTE myoneliner;
END
$func$ LANGUAGE plpgsql;

要点

  • 不要将整个语句转换为小写。列名可能用大写字母双引号,在这种情况下区分大小写(没有双关语)。

  • information_schema.columns 的查询中不需要DISTINCT每个表的列名是唯一的

  • 确实需要指定架构,但是(或使用其他方式来挑选一个架构),或者您可能会在多个模式中混合来自多个同名表的列名,从而导致废话。

  • 您必须清理动态代码中的所有标识符 - 包括表名:quote_ident(mytable)。请注意,函数的文本参数区分大小写! information_schema.columns 上的查询也需要这样做。

  • 我解开了您的整个构造,以使用 string_agg() 而不是数组构造函数来构建列名列表。相关答案:

  • The assignment operator in plpgsql is :=.

  • RAISE NOTICE 的简化语法。

无法解决的核心问题

所有这些仍然不能解决您的主要问题:SQL 要求定义要返回的列。您可以通过返回您尝试过的匿名记录来规避这种情况。但这只是推迟了不可避免的事情。现在您必须在调用时提供一个列定义列表,就像您的错误消息告诉您的那样。但是您只是不知道要返回哪些列。第 22 条。

你的电话像这样工作:

SELECT *
FROM   myresult('dkj_p_k27ac','enri') AS f (
  enrich_d_dkj_p_k27ac text  -- replace with actual column types
, enrich_lr_dkj_p_k27ac text
, enrich_r_dkj_p_k27ac text);

但是您不知道返回列的编号、名称(可选)和数据类型,在函数创建时甚至在调用时都不知道。在单个调用中不可能做到这一点。您需要对数据库两个单独的查询

可以使用多态类型的函数动态返回任何给定表的所有列,因为有是整个表的定义明确的类型。这个相关答案的最后一章:

【讨论】:

  • 基本上没有什么要求 SQL 客户端事先知道列;对于大多数客户,它们可以在结果集信息之前发送。但是,PostgreSQL 的设计目前不允许查询结果类型在计划期间未知且仅在执行时确定的查询,因此 PostgreSQL 无法做到这一点。
  • 另外,虽然需要两个单独的查询,但这并不一定意味着在 DB API 的两个函数调用或两个往返的意义上的两个调用。您可以在复合语句中执行类似SELECT prepare_cursor('cursorname'); FETCH ALL FROM "cursorname"; 的内容。大多数客户端驱动程序都会处理这个问题。
  • @CraigRinger:同样,您可以在动态语句中使用CREATE TEMP TABLE foo AS ...;,在下一个查询中使用SELECT * FROM foo;。可以是到服务器的一次往返,但可以是两个单独的查询。正如建议的那样,我澄清了“查询”,因为“呼叫”太模糊了。
  • 嗨@Erwin Brandstetter,非常感谢您的回复。非常翔实和有教育意义。我在我的问题中添加了一个新部分 - 请参阅上面的“我的第二次尝试”。我基本上想知道我是否可以创建第二个函数来接收 myresult() 函数(基本上是 SQL 查询行)的 TEXT 输出,并能够使用它来执行查询。我将衷心感谢您的帮助。这背后的动机是避免我目前在收到 myresult() 的输出后执行的复制/粘贴步骤。非常感谢!
  • 请恢复您对问题的编辑并改用新问题。这些信息是面向公众的,他们很难理解。您始终可以链接到这个以获取上下文。
猜你喜欢
  • 1970-01-01
  • 2014-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多