我需要创建一个函数来检查给定表中是否存在 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
,我也需要指定字段,所以我不知道该怎么做。
最佳答案
这很难解决,因为 SQL 要求在调用时知道返回类型。
此外,plpgsql 函数需要有一个明确定义的返回类型。
如果您选择返回匿名记录,您将得到您定义的:匿名记录。 Postgres 不知道里面有什么。因此,需要列定义列表来分解类型。
有多种解决方法,具体取决于具体要求。如果您有办法在调用时知道返回类型,我建议使用多态类型,如本答案最后一章(“各种完整的表类型”)中所述:
但这不包括在函数运行时在函数内向返回类型添加另一列。那是不可能的。我会重新考虑整个方法。
至于您当前的方法,我能想到的最接近的是一个临时表 ( or a cursor ),您可以在单个事务的第二次调用中查询强>.
您的代码中还有一些其他问题。请参阅下面的注释。
概念验证
CREATE OR REPLACE FUNCTION f_tbl_plus_infowindow (_tbl regclass) -- parameter name
RETURNS void -- no direct return type
LANGUAGE plpgsql AS
$func$
DECLARE
_tmp text := quote_ident(_tbl::text || '_tmp'); -- apending _tmp for temp table
BEGIN
-- Create temp table only for duration of transaction
EXECUTE format(
'CREATE TEMP TABLE %s ON COMMIT DROP AS
SELECT * FROM %s LIMIT 0'
,_tmp, _tbl
);
IF EXISTS (
SELECT 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($$
ALTER TABLE %1$s ADD COLUMN infowindow text;
INSERT INTO %1$s
SELECT *, 'ID: ' || id::text
FROM %2$s$$
,_tmp, _tbl
);
END IF;
END
$func$;
调用必须在单个事务中。根据您的客户,您可能必须启动显式事务。
BEGIN;
SELECT f_tbl_plus_infowindow ('tbl');
SELECT * FROM tbl_tmp; -- do something with the returned rows
ROLLBACK; -- or COMMIT, does not matter here
db<> fiddle here
<子> Old sqlfiddle
或者,您可以让临时表在 session 期间一直存在。不过,请注意重复调用的命名冲突。
注意事项
使用参数名称代替 outdated ALIAS
command .
要实际“默认”到当前模式,请使用我显示的更简单的查询。使用 regclass
自动完成技巧。详情:
此外,这还可以避免原始代码中非标准(或恶意格式错误)表名的语法错误和可能的SQL 注入(inject)。
您的 ELSE
中的代码子句根本不起作用。
TABLE tbl;
基本上是 SELECT * FROM tbl;
的缩写.
关于postgresql - 从 PL/pgSQL 函数返回具有未知列的动态表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23929707/