这是功能:
create or replace function xtab (tablename varchar,rowc varchar,colc varchar,cellc varchar,celldatatype varchar) returns varchar language plpgsql as $$ declare dynsql1 varchar; dynsql2 varchar; columnlist varchar; begin -- 1. retrieve list of column names. dynsql1 = 'select string_agg(distinct '||colc||'||'' '||celldatatype||''','','' order by '||colc||'||'' '||celldatatype||''') from '||tablename||';'; execute dynsql1 into columnlist; -- 2. set up the crosstab query dynsql2 = 'select * from crosstab ( ''select '||rowc||','||colc||','||cellc||' from '||tablename||' group by 1,2 order by 1,2'',''select distinct '||colc||' from '||tablename||' order by 1'' ) as ct ( '||rowc||' varchar,'||columnlist||' );'; return dynsql2; end $$;
select xtab('globalpayments','month','currency','(sum(total_fees)/sum(txn_amount)*100)::decimal(48,2)','text');
返回(因为函数的返回类型是varchar):
select * from crosstab ( 'select month,currency,(sum(total_fees)/sum(txn_amount)*100)::decimal(48,2) from globalpayments group by 1,2 order by 1,2','select distinct currency from globalpayments order by 1' ) as ct ( month varchar,CAD text,EUR text,GBP text,USD text );
如何才能使此函数不仅生成动态交叉表的代码,还执行结果?即,我手动复制/粘贴/执行的结果就是这个.但是我希望它在没有额外步骤的情况下执行:函数应该组装动态查询并执行它:
编辑1
这个函数很接近,但我需要它返回的不仅仅是第一个记录的第一列
摘自:Are there any way to execute a query inside the string value (like eval) in PostgreSQL?
create or replace function eval( sql text ) returns text as $$ declare as_txt text; begin if sql is null then return null ; end if ; execute sql into as_txt ; return as_txt ; end; $$language plpgsql
用法:select * from eval($$select * from analytics limit 1 $$)
但是它只返回第一条记录的第一列:
eval ---- 2015
当实际结果如下所示:
Year,Month,Date,TPV_USD ---- ----- ------ -------- 2016,3,2016-03-31,100000
有限的方法是使用多态函数.如果您可以在函数调用时提供返回类型.但是你的问题并不明显.
> Refactor a PL/pgSQL function to return the output of various SELECT queries
您可以使用匿名记录返回完全动态的结果.但是,您需要为每次调用提供列定义列表.你怎么知道返回的列?赶上22.
根据您的需要或可以使用的方法,有各种变通方法.由于您的所有数据列似乎共享相同的数据类型,我建议返回一个数组:text [].或者您可以返回类似hstore或json的文档类型.有关:
> Dynamic alternative to pivot with CASE and GROUP BY
> Dynamically convert hstore keys into columns for an unknown set of keys
但是使用两个调用可能更简单:1:让Postgres构建查询. 2:执行并检索返回的行.
> Selecting multiple max() values using a single SQL statement
我根本不会使用Eric Minikel的功能.通过恶意格式错误的标识符来防止sql注入是不安全的.除非您运行的是比Postgres 9.1更早的过时版本,否则请使用format()
构建查询字符串.
更简洁,更清晰的实现可能如下所示:
CREATE OR REPLACE FUNCTION xtab(_tbl regclass,_row text,_cat text,_expr text -- still vulnerable to sql injection!,_type regtype) RETURNS text AS $func$ DECLARE _cat_list text; _col_list text; BEGIN -- generate categories for xtab param and col definition list EXECUTE format( $$SELECT string_agg(quote_literal(x.cat),'),('),string_agg(quote_ident (x.cat),%L) FROM (SELECT DISTINCT %I AS cat FROM %s ORDER BY 1) x$$,' ' || _type || ',',_cat,_tbl) INTO _cat_list,_col_list; -- generate query string RETURN format( 'SELECT * FROM crosstab( $q$SELECT %I,%I,%s FROM %I GROUP BY 1,2 -- only works if the 3rd column is an aggregate expression ORDER BY 1,2$q$,$c$VALUES (%5$s)$c$ ) ct(%1$I text,%6$s %7$s)',_row,_expr -- expr must be an aggregate expression!,_tbl,_cat_list,_col_list,_type ); END $func$LANGUAGE plpgsql;
与原始版本相同的函数调用.函数crosstab()由必须安装的附加模块tablefunc
提供.基本:
这会安全地处理列名和表名.注意使用对象标识符类型regclass和regtype.也适用于模式限定名称.
> Table name as a PostgreSQL function parameter
但是,当您传递要作为表达式执行的字符串(原始查询中的_expr – cellc)时,它并不完全安全.这种输入本身对sql注入不安全,不应该暴露给普通大众.
> SQL injection in Postgres functions vs prepared queries
对于两个类别列表仅扫描表一次,并且应该更快一些.
仍然无法返回完全动态的行类型,因为这是完全不可能的.