【问题标题】:Return dynamic table with unknown columns from PL/pgSQL function从 PL/pgSQL 函数返回具有未知列的动态表
【发布时间】:2014-07-18 17:51:54
【问题描述】:

如果infowindow 字段存在,我需要创建一个检查给定表的函数。如果存在,则该函数必须返回 select * from table,但如果不存在,则必须返回一个额外的 id 字段:

CREATE OR REPLACE FUNCTION getxo_ocx_cincu_preparar_infowindow(
                                              guretabla character varying)
  RETURNS TABLE AS
$BODY$ 
DECLARE
    tabla ALIAS FOR $1;

BEGIN

IF  EXISTS (SELECT 1
   FROM   pg_namespace n
   JOIN   pg_class     c ON c.relnamespace = n.oid
   JOIN   pg_attribute a ON a.attrelid = c.oid 
   WHERE  n.nspname = current_schema()  -- default to current schema
   AND    c.relname = tabla
   AND    a.attname = 'infowindow'
   AND    NOT a.attisdropped)
THEN
    RETURN QUERY EXECUTE 'SELECT * from ' ||tabla ;
ELSE
    RETURN QUERY EXECUTE 'SELECT *, ID:' || id::text ||' as infowindow
                                   from ' ||tabla ;
END IF;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

如果我使用RETURNS SETOF RECORDS,当我对函数进行选择时,我需要指定我不知道的列。如果我使用RETURNS TABLE,我也需要指定字段,所以我不知道该怎么做。

【问题讨论】:

  • 您忘记提及您的 Postgres 版本。

标签: postgresql polymorphism plpgsql dynamic-sql return-type


【解决方案1】:

这很难解决,因为 SQL 要求在调用时知道返回类型
此外,plpgsql 函数需要有一个定义明确的返回类型

如果您选择返回匿名记录,您将得到您所定义的:匿名记录。 Postgres 不知道里面是什么。因此,需要一个列定义列表来分解类型。

有多种解决方法,具体取决于具体要求。如果您有任何方法知道返回类型在调用时,我建议多态类型如本答案最后一章所述(“各种完整的表类型”):
Refactor a PL/pgSQL function to return the output of various SELECT queries

但这不包括在函数内部在运行时向返回类型添加另一列。那是不可能的。我会重新考虑整个方法

至于您当前的方法,我能想到的最接近的方法是一个临时表 (or a cursor),您可以在 单个事务中的 第二次调用 中查询该表强>。

您的代码中有几个其他问题。请参阅下面的注释。

概念证明

CREATE OR REPLACE FUNCTION f_tbl_plus_infowindow (_tbl regclass) -- regclass!
  RETURNS void AS  -- no direct return type
$func$
DECLARE
   -- appending _tmp for temp table
   _tmp text := quote_ident(_tbl::text || '_tmp');
BEGIN

-- Create temp table only for duration of transaction
EXECUTE format(
   'CREATE TEMP TABLE %s ON COMMIT DROP AS TABLE %s LIMIT 0', _tmp, _tbl);

IF EXISTS (
   SELECT 1
   FROM   pg_attribute a
   WHERE  a.attrelid = _tbl
   AND    a.attname  = 'infowindow'
   AND    a.attisdropped = FALSE)
THEN
   EXECUTE format('INSERT INTO %s SELECT * FROM %s', _tmp, _tbl);
ELSE
  -- This is assuming a NOT NULL column named "id"!
   EXECUTE format($x$
      ALTER  TABLE %1$s ADD COLUMN infowindow text;
      INSERT INTO %1$s
      SELECT *, 'ID: ' || id::text
      FROM   %2$s $x$
     ,_tmp, _tbl);
END IF;

END
$func$ LANGUAGE plpgsql;

调用必须在单个事务中。您可能需要启动显式事务,具体取决于您的客户。

BEGIN;
SELECT f_tbl_plus_infowindow ('tbl');
SELECT * FROM tbl_tmp;  -- do something with the returned rows
ROLLBACK;               -- or COMMIT, does not matter here

SQL Fiddle.

或者,您可以让临时表在会话期间有效。不过要小心重复调用的命名冲突。

注意事项

【讨论】:

    猜你喜欢
    • 2012-08-05
    • 2012-08-16
    • 2013-06-19
    • 2018-02-10
    • 2013-08-07
    • 1970-01-01
    • 2018-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多