【问题标题】:plpgsql function: Return rows from a view created from random tableplpgsql 函数:从随机表创建的视图中返回行
【发布时间】:2014-03-13 18:05:57
【问题描述】:

我想创建一个从未知表创建的视图中返回行的函数:

    CREATE OR REPLACE FUNCTION tt_query(text, timestamp without time zone)
      RETURNS SETOF record AS
    $$
    DECLARE

    orig_name ALIAS FOR $1;
    data_tt ALIAS FOR $2;

    BEGIN

    [...]

    EXECUTE 'create OR REPLACE TEMP view temp as 
    select * 
    from  '
    ||orig_name 
    ||' where trigger_changed >'
    ||quote_literal(data_tt)
    ||' ORDER BY trigger_changed DESC';

    [...]--other work on view temp

    --NOW I WANT RETURN THE ROW OF view temp
    END;
    $$
    LANGUAGE plpgsql VOLATILE

好的,我认为(在你的帮助下):

表:

create table t(a integer, b text);

功能:

CREATE OR REPLACE FUNCTION f()
  RETURNS SETOF record AS
$$
BEGIN

RETURN QUERY EXECUTE 'SELECT * FROM t';

END;
  $$
  LANGUAGE plpgsql VOLATILE

类型:

CREATE TYPE y AS (
    a int,
    b text
);

现在有可能吗?:

select * from f() as y;

在我的例子中,y name 是一个变量,我在另一个函数中创建它

【问题讨论】:

  • RETURN QUERY EXECUTE 'SELECT * FROM temp' 怎么样?
  • 当我选择 tt_query(val1, val2) 我有: 错误:la funzione che restituisce insiemi è chiamata in un contesto che non può accettare un insieme SQL state: 0A000
  • 在会话中运行 SET lc_messages = 'C' 以在会话期间获取默认英文消息。
  • 抱歉:错误:在不能接受集合的上下文中调用集合值函数
  • 如果我写它就可以工作: select * from tt_query(val1, val2) t(int a, int c, ... etc);但我想在这种模式下写我的查询:-(

标签: postgresql record plpgsql


【解决方案1】:

它可以像这样工作:

CREATE OR REPLACE FUNCTION tt_query(orig_name regclass, data_tt timestamp)
  RETURNS SETOF record AS
$func$
BEGIN

EXECUTE 'CREATE OR REPLACE TEMP VIEW tmp as 
select * 
from  '
|| orig_name 
|| ' where trigger_changed >'
|| quote_literal(data_tt)
|| ' ORDER BY trigger_changed DESC';

-- other work on view tmp

-- return the rows of view temp
RETURN QUERY
SELECT * FROM tmp;

END
$func$  LANGUAGE plpgsql;
  • 注意使用object identifier type regclass 来自动避免SQL 注入。

  • 如果没有必要,不要使用过时的语法var ALIAS for $1。改为声明参数名称。

  • 即使允许,我也不会使用关键字temp 作为标识符。改用tmp

  • 使用RETURN QUERY 返回一组记录。这甚至可以是没有EXECUTE 的静态调用。但是,您返回的是匿名记录,而 Postgres 每次调用都需要一个列定义列表

SELECT * FROM tt_query('tbl_name', '2014-02-15 12:00')
AS f(col1 int, col2 text, ...);

这相当笨拙。

更好的解决方案

如果您知道返回类型(即使表名发生变化,列列表也可能共享相同的类型),请在创建时声明它。考虑这个相关问题:
PostgreSQL: ERROR: 42601: a column definition list is required for functions returning "record"

如果返回类型因提供的表名而不同,仍然有更好的解决方案。由于您使用SELECT * FROM tbl 创建视图,因此您可以利用表格本身的众所周知的类型作为polymorphic 参数:

CREATE OR REPLACE FUNCTION tt_query(orig_name anyelement, data_tt timestamp)
  RETURNS SETOF anyelement AS
$func$
BEGIN

EXECUTE format('CREATE OR REPLACE TEMP VIEW tmp AS
   SELECT * FROM  %s
   WHERE  trigger_changed > %L
   ORDER  BY trigger_changed DESC'
  ,pg_typeof(orig_name)
  ,data_tt);

-- other work on view tmp

-- return the rows of view tmp
RETURN QUERY
SELECT * FROM tmp;

END
$func$  LANGUAGE plpgsql;

简化调用:

SELECT * FROM tt_query(NULL::tbl_name, '2014-02-15 12:00');

还使用format() 进行安全且简单的字符串连接。

此相关答案中的更多详细信息:
Refactor a PL/pgSQL function to return the output of various SELECT queries

【讨论】:

  • @user3297525:好吧,祝你好运!如果您发现了什么,请务必报告。恐怕您会发现这对于具有不同列的不同表来说非常简单。
  • @ErwinBrandstetter 请您告知我是否可以安全地假设临时视图将仅存在于 BEGIN END 块中并且不会与同一函数的其他调用竞争?文档说“每次会话”,但我不是 100% 确定。谢谢。
  • @Pocketsand:相信文档。临时视图会在当前 session 结束时自动删除,而不是 transaction。因此,这很可能会导致同一会话中的多个调用出现问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-25
相关资源
最近更新 更多