使用 PostgreSQL 9.3 的动态数据透视查询
Dynamic pivot query using PostgreSQL 9.3
我有一个 table 命名为 Product
:
create table product (
ProductNumber varchar(10),
ProductName varchar(10),
SalesQuantity int,
Salescountry varchar(10)
);
示例值:
insert into product values
('P1', 'PenDrive', 50, 'US')
, ('P2', 'Mouse', 100, 'UK')
, ('P3', 'KeyBoard', 250, 'US')
, ('P1', 'PenDrive', 300, 'US')
, ('P2', 'Mouse', 450, 'UK')
, ('P5', 'Dvd', 50, 'UAE');
我想动态生成 Salescountry's
个名称并显示 SalesQuantity
在该国家/地区的销售总额。
预期结果:
ProductName US UK UAE
----------------------------
PenDrive 350 0 0
Mouse 0 550 0
KeyBoard 250 0 0
Dvd 0 0 50
我使用 SQL Server 2008 R2:
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX);
SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(SalesCountry)
FROM Product
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT ProductName, ' + @cols + ' from
(
select ProductName
, SalesQuantity as q
, Salescountry
from Product
) x
pivot
(
SUM(q)
for Salescountry in (' + @cols + ')
) p '
PRINT(@query);
execute(@query);
如何在 Postgres 中实现这一点?
SELECT *
FROM crosstab (
'SELECT ProductNumber, ProductName, Salescountry, SalesQuantity
FROM product
ORDER BY 1'
, $$SELECT unnest('{US,UK,UAE1}'::varchar[])$$
) AS ct (
"ProductNumber" varchar
, "ProductName" varchar
, "US" int
, "UK" int
, "UAE1" int);
详细解释:
- PostgreSQL Crosstab Query
- Pivot on Multiple Columns using Tablefunc
完全动态查询不同数量的不同 Salescountry
?
- Dynamic alternative to pivot with CASE and GROUP BY
虽然这是一个两步过程,但此方法将创建一个具有动态列的数据透视表,无需指定结果集,也无需创建临时表。
首先我们定义一个创建动态准备语句的函数:
CREATE OR REPLACE FUNCTION flowms.pivotcode_sql(
tablename character varying,
rowc character varying,
colc character varying,
cellc character varying,
celldatatype character varying)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
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
--tablename = REPLACE(text, ''', E'\"')
dynsql2 = 'prepare crosstab_stmt as select * from crosstab (
''select '||rowc||','||colc||','||cellc||' from '||replace(tablename, chr(39),E'\'\'')||' group by 1,2 order by 1,2'',
''select distinct '||colc||' from '||replace(tablename, chr(39),E'\'\'')||' order by 1''
)
as newtable (
'||rowc||' varchar,'||columnlist||'
);';
deallocate all;
execute dynsql2;
return dynsql2;
end
$BODY$;
您现在可以调用函数
select pivotcode_sql('tablename', 'rowfield', 'columnfield', 'sum(value)', 'integer');
这将创建准备好的语句。接下来就可以执行prepared statement了:
execute crosstab_stmt;
我刚刚改进了 Peter 的解决方案:
CREATE OR REPLACE FUNCTION export.pivotcode_sql(
tablename character varying,
rowc character varying,
colc character varying,
cellc character varying,
celldatatype character varying,
tblname character varying)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
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
--tablename = REPLACE(text, ''', E'\"')
dynsql2 = 'drop table if exists '|| tblname ||' ; create table ' || tblname || ' as select * from crosstab (
''select '||rowc||','||colc||','||cellc||' from '||replace(tablename, chr(39),E'\'\'')||' group by 1,2 order by 1,2'',
''select distinct '||colc||' from '||replace(tablename, chr(39),E'\'\'')||' order by 1''
)
as newtable (
'||rowc||' varchar,'||columnlist||'
);';
deallocate all;
execute dynsql2;
return dynsql2;
end
$BODY$;
ALTER FUNCTION export.pivotcode_sql(character varying, character varying, character varying, character varying, character varying, character varying)
OWNER TO postgres;
在使用中:
select EXPORT.pivotcode_sql('EXPORT.TERMINAL_REVENUE', 'terminal_no','period', 'sum(amount)', 'numeric(12,2)','faydin.test15');
它创建 table faydin.test15
:
select * from faydin.test15
希望它能像对我一样对你有所帮助!
我有一个 table 命名为 Product
:
create table product (
ProductNumber varchar(10),
ProductName varchar(10),
SalesQuantity int,
Salescountry varchar(10)
);
示例值:
insert into product values
('P1', 'PenDrive', 50, 'US')
, ('P2', 'Mouse', 100, 'UK')
, ('P3', 'KeyBoard', 250, 'US')
, ('P1', 'PenDrive', 300, 'US')
, ('P2', 'Mouse', 450, 'UK')
, ('P5', 'Dvd', 50, 'UAE');
我想动态生成 Salescountry's
个名称并显示 SalesQuantity
在该国家/地区的销售总额。
预期结果:
ProductName US UK UAE
----------------------------
PenDrive 350 0 0
Mouse 0 550 0
KeyBoard 250 0 0
Dvd 0 0 50
我使用 SQL Server 2008 R2:
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX);
SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(SalesCountry)
FROM Product
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT ProductName, ' + @cols + ' from
(
select ProductName
, SalesQuantity as q
, Salescountry
from Product
) x
pivot
(
SUM(q)
for Salescountry in (' + @cols + ')
) p '
PRINT(@query);
execute(@query);
如何在 Postgres 中实现这一点?
SELECT *
FROM crosstab (
'SELECT ProductNumber, ProductName, Salescountry, SalesQuantity
FROM product
ORDER BY 1'
, $$SELECT unnest('{US,UK,UAE1}'::varchar[])$$
) AS ct (
"ProductNumber" varchar
, "ProductName" varchar
, "US" int
, "UK" int
, "UAE1" int);
详细解释:
- PostgreSQL Crosstab Query
- Pivot on Multiple Columns using Tablefunc
完全动态查询不同数量的不同 Salescountry
?
- Dynamic alternative to pivot with CASE and GROUP BY
虽然这是一个两步过程,但此方法将创建一个具有动态列的数据透视表,无需指定结果集,也无需创建临时表。
首先我们定义一个创建动态准备语句的函数:
CREATE OR REPLACE FUNCTION flowms.pivotcode_sql(
tablename character varying,
rowc character varying,
colc character varying,
cellc character varying,
celldatatype character varying)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
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
--tablename = REPLACE(text, ''', E'\"')
dynsql2 = 'prepare crosstab_stmt as select * from crosstab (
''select '||rowc||','||colc||','||cellc||' from '||replace(tablename, chr(39),E'\'\'')||' group by 1,2 order by 1,2'',
''select distinct '||colc||' from '||replace(tablename, chr(39),E'\'\'')||' order by 1''
)
as newtable (
'||rowc||' varchar,'||columnlist||'
);';
deallocate all;
execute dynsql2;
return dynsql2;
end
$BODY$;
您现在可以调用函数
select pivotcode_sql('tablename', 'rowfield', 'columnfield', 'sum(value)', 'integer');
这将创建准备好的语句。接下来就可以执行prepared statement了:
execute crosstab_stmt;
我刚刚改进了 Peter 的解决方案:
CREATE OR REPLACE FUNCTION export.pivotcode_sql(
tablename character varying,
rowc character varying,
colc character varying,
cellc character varying,
celldatatype character varying,
tblname character varying)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
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
--tablename = REPLACE(text, ''', E'\"')
dynsql2 = 'drop table if exists '|| tblname ||' ; create table ' || tblname || ' as select * from crosstab (
''select '||rowc||','||colc||','||cellc||' from '||replace(tablename, chr(39),E'\'\'')||' group by 1,2 order by 1,2'',
''select distinct '||colc||' from '||replace(tablename, chr(39),E'\'\'')||' order by 1''
)
as newtable (
'||rowc||' varchar,'||columnlist||'
);';
deallocate all;
execute dynsql2;
return dynsql2;
end
$BODY$;
ALTER FUNCTION export.pivotcode_sql(character varying, character varying, character varying, character varying, character varying, character varying)
OWNER TO postgres;
在使用中:
select EXPORT.pivotcode_sql('EXPORT.TERMINAL_REVENUE', 'terminal_no','period', 'sum(amount)', 'numeric(12,2)','faydin.test15');
它创建 table faydin.test15
:
select * from faydin.test15
希望它能像对我一样对你有所帮助!