在@Erwin Brandstetter和@Craig Ringer的协助下继续a previous case,我修改了我的代码,如下所示。请注意,我的函数myresult()
现在输出text
,而不是表(事实上,正如在前一种情况中指出的那样,输出表格没有意义对象,因为我们需要提前定义它的所有列,这基本上违背了整个目的):
CREATE OR REPLACE FUNCTION myresult(mytable text, myprefix text)
RETURNS text AS
$func$
DECLARE
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
RAISE NOTICE 'My additional text: %', myoneliner;
RETURN myoneliner;
END
$func$ LANGUAGE plpgsql;
呼叫:
select myresult('dkj_p_k27ac','enri');
运行上面的过程后,我得到一个文本字符串,基本上是一个查询。为了简单起见,我将其作为' oneliner-output'来引用它。
' oneline-output'看起来如下(我只是从我进入这里的一个输出单元复制/粘贴它):
"SELECT enrich_d_dkj_p_k27ac,enrich_lr_dkj_p_k27ac,enrich_r_dkj_p_k27ac FROM dkj_p_k27ac"
myresult()
输出的一部分。我没有添加它们。我现在明白了这个有问题的想法,即构建一个既可以创建' oneliner-output'并执行它。我能够复制/粘贴' oneliner-output'进入一个新的Postgres查询窗口并将其作为普通查询执行就好了,在我的数据输出窗口中接收所需的列和行。
但是,我希望自动执行此步骤,以避免复制/粘贴步骤。 Postgres中有没有办法使用text
函数收到的myresult()
输出(' oneliner-output')并执行它?是否可以创建第二个函数来接收myresult()
的输出并将其用于执行查询?
沿着这些方向,虽然我知道下面的脚本(在下面这里)工作并且实际上输出了所需的列和行:
-- DEALLOCATE stmt1; -- use this line after the first time 'stmt1' was created
prepare stmt1 as SELECT enrich_d_dkj_p_k27ac,enrich_lr_dkj_p_k27ac,enrich_r_dkj_p_k27ac FROM dkj_p_k27ac;
execute stmt1;
我在想,在做了正确的调整后,下面的脚本可能会起作用吗?不知道怎么回事。
prepare stmt1 as THE_OUTPUT_OF_myresult();
execute stmt1;
CREATE OR REPLACE FUNCTION show_mytable(ref refcursor) RETURNS refcursor AS $$
BEGIN
OPEN ref FOR SELECT enrich_d_dkj_p_k27ac,enrich_lr_dkj_p_k27ac,enrich_r_dkj_p_k27ac FROM dkj_p_k27ac; -- Open a cursor
RETURN ref; -- Return the cursor to the caller
END;
$$ LANGUAGE plpgsql;
呼叫:
BEGIN;
SELECT show_mytable('roy');
FETCH ALL IN "roy";
这个过程实际上可以工作并吐出所需的列和行,而且我必须再次提供精确的SELECT语句。
我基本上希望能够将其作为myresult()
函数的输出提供。像这样:
CREATE OR REPLACE FUNCTION show_mytable(ref refcursor) RETURNS refcursor AS $$
BEGIN
OPEN ref FOR myresult(); -- Open a cursor
RETURN ref; -- Return the cursor to the caller
END;
$$ LANGUAGE plpgsql;
呼叫:
BEGIN;
SELECT show_mytable('roy');
FETCH ALL IN "roy";
答案 0 :(得分:1)
使用PREPARE
的技巧不起作用,因为它不会像CREATE FUNCTION
那样使用*文本字符串*(值),而是使用有效的语句(码)。
要将数据转换为可执行的代码,您需要使用动态SQL,即plpgsql函数或EXECUTE
语句中的DO
。只要返回类型不依赖于第一个函数myresult()
的结果,这就没有问题。另外,你回到了我之前的答案中所描述的22:
关键部分是以某种方式声明返回类型(在这种情况下为行类型)。您可以为此目的创建TABLE
,TEMP TABLE
或TYPE
。或者您可以使用预准备语句或refcursor。
你一直很亲密。这个难题的缺失部分是使用动态SQL 准备生成的查询。
创建此功能 一次 。它是您的函数myresult()
的优化和安全版本:
CREATE OR REPLACE FUNCTION f_prep_query (_tbl regclass, _prefix text)
RETURNS void AS
$func$
BEGIN
IF EXISTS (SELECT 1 FROM pg_prepared_statements WHERE name = 'stmt_dyn') THEN
DEALLOCATE stmt_dyn;
END IF; -- you my or may not need this safety check
EXECUTE (
SELECT 'PREPARE stmt_dyn AS SELECT '
|| string_agg(quote_ident(attname), ',' ORDER BY attname)
|| ' FROM ' || _tbl
FROM pg_catalog.pg_attribute
WHERE attrelid = _tbl
AND attname LIKE _prefix || '%'
AND attnum > 0
AND NOT attisdropped
);
END
$func$ LANGUAGE plpgsql;
我使用regclass
作为表名参数_tbl
,使其对SQLi具有明确性和安全性。详细说明:
信息架构不包括系统目录的oid列,因此我切换到pg_catalog.pg_attribute
而不是information_schema.columns
。那也更快。这有利有弊:
如果已存在名为stmt_dyn
的预准备语句,PREPARE
将引发异常。如果这是可接受的,请取消对系统视图pg_prepared_statements
和以下DEALLOCATE
的检查
可以使用更复杂的算法来管理每个会话的多个预准备语句,或者将预准备语句的名称作为附加参数,甚至使用查询字符串的MD5哈希作为名称,但这超出了本问题的范围。 子>
请注意PREPARE
在事务的范围之外运行,一旦PREPARE
成功,准备好的语句将在会话的生命周期内存在。如果包装事务中止,PREPARE
不受影响。 ROLLBACK
无法删除预先准备好的语句。
两个查询,但只有一个调用服务器。而且非常有效。
SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;
对于大多数简单的用例来说,比创建临时表或游标并从中选择/获取(这可能是其他选项)更简单,更有效。
答案 1 :(得分:1)
我想我也找到了一个解决方案,使用refcursor 如果你可以通过它,我会很高兴,检查并告诉我你是否认为它是犹太洁食的'。坦率地说,我不太清楚我在这里提出了什么,因为我对语法并不熟悉。但是我能够使用我在网上找到的不同例子来合成这个。它似乎对我有用。如果你能为我和其他用户阐明这个解决方案,我会很高兴 - 并告诉你对它的看法。
首先让我们创建构造动态SELECT
语句的函数:
CREATE OR REPLACE FUNCTION myresult2()
RETURNS text AS
$func$
DECLARE
myoneliner text;
mytable text := 'dkj_p_k27ac';
myprefix text := 'enri';
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
-- RAISE NOTICE 'My additional text: %', myoneliner; -- for debugging
RETURN myoneliner;
END
$func$ LANGUAGE plpgsql;
现在,让我们创建第二个函数,它可以执行第一个函数myresult2()
的字符串TEXT输出:
CREATE OR REPLACE FUNCTION show_mytable(ref refcursor)
RETURNS refcursor AS
$func$
DECLARE
mydynamicstatment text := myresult2();
BEGIN
OPEN ref FOR EXECUTE mydynamicstatment;
RETURN ref; -- return cursor to the caller
END;
$func$ LANGUAGE plpgsql;
呼叫:
BEGIN;
SELECT show_mytable('roy');
FETCH ALL IN "roy";