Joh*_*eck 7 postgresql dynamic-sql crosstab plpgsql
我在Postgres数据库中实现了这个功能:http://www.cureffi.org/2013/03/19/automatically-creating-pivot-table-column-names-in-postgresql/
这是功能:
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
$$;
Run Code Online (Sandbox Code Playgroud)
所以现在我可以调用这个函数:
select xtab('globalpayments','month','currency','(sum(total_fees)/sum(txn_amount)*100)::decimal(48,2)','text');
Run Code Online (Sandbox Code Playgroud)
返回(因为函数的返回类型是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 );
Run Code Online (Sandbox Code Playgroud)
如何才能使此函数不仅生成动态交叉表的代码,还执行结果?即,我手动复制/粘贴/执行的结果就是这个.但是我希望它在没有额外步骤的情况下执行:函数应该组装动态查询并执行它:
编辑1
这个函数很接近,但我需要它返回的不仅仅是第一个记录的第一列
摘自:有没有办法在PostgreSQL中的字符串值(如eval)中执行查询?
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
Run Code Online (Sandbox Code Playgroud)
用法: select * from eval($$select * from analytics limit 1$$)
但是它只返回第一条记录的第一列:
eval
----
2015
Run Code Online (Sandbox Code Playgroud)
当实际结果如下所示:
Year, Month, Date, TPV_USD
---- ----- ------ --------
2016, 3, 2016-03-31, 100000
Run Code Online (Sandbox Code Playgroud)
你要求的是不可能的.SQL是一种严格类型的语言.PostgreSQL函数需要声明一个返回类型(RETURNS ..)的时间创作.
有限的方法是使用多态函数.如果您可以在函数调用时提供返回类型.但是你的问题并不明显.
您可以使用匿名记录返回完全动态的结果.但是,您需要为每次调用提供列定义列表.你怎么知道返回的列?赶上22.
根据您的需要或可以使用的方法,有各种变通方法.由于您的所有数据列似乎共享相同的数据类型,我建议返回一个数组:text[].或者您可以返回类似hstore或的文档类型json.有关:
但是使用两个调用可能更简单:1:让Postgres构建查询.2:执行并检索返回的行.
在你的问题提出我不会从埃里克Minikel使用该功能在所有.通过恶意格式错误的标识符来防止SQL注入是不安全的.使用format()建立查询字符串,除非您运行的是过时的版本比9.1的Postgres年长.
更简洁,更清晰的实现可能如下所示:
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, _cat, _expr -- expr must be an aggregate expression!
, _tbl, _cat_list, _col_list, _type
);
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
与原始版本相同的函数调用.该功能crosstab()由tablefunc必须安装的附加模块提供.基本:
这会安全地处理列名和表名.注意对象标识符类型的使用regclass和regtype.也适用于模式限定名称.
然而,这是不是完全安全的,而你传递一个字符串作为表达式执行(_expr- cellc在原来的查询).这种输入本身对SQL注入不安全,不应该暴露给普通大众.
对于两个类别列表仅扫描表一次,并且应该更快一些.
仍然无法返回完全动态的行类型,因为这是完全不可能的.
| 归档时间: |
|
| 查看次数: |
4667 次 |
| 最近记录: |