I have a table named as Product
:
create table product (
ProductNumber varchar(10),
ProductName varchar(10),
SalesQuantity int,
Salescountry varchar(10)
);
Sample values:
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');
I want to generate the Salescountry's
names dynamically and show the sum of SalesQuantity
sale in that Country.
Expected result:
ProductName US UK UAE
----------------------------
PenDrive 350 0 0
Mouse 0 550 0
KeyBoard 250 0 0
Dvd 0 0 50
I did it using 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);
How to achieve this in Postgres?
In PostgreSQL, pivot tables are created with the help of the crosstab() function, which is part of the optional tablefunc module. To start using this function, you need to install the tablefunc module for a required database.
You can also create a dynamic pivot query, which uses a dynamic columns for pivot table, means you do not need to pass hard coded column names that you want to display in your pivot table. Dynamic pivot query will fetch a value for column names from table and creates a dynamic columns name list for pivot table.
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);
Detailed explanation:
Completely dynamic query for varying number of distinct Salescountry
?
While it's a two step process, this method will create a pivot with dynamic columns, without the need to specify the result set and without creating temp tables.
First we define a function that creates a dynamically prepared statement:
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$;
You can now call the function
select pivotcode_sql('tablename', 'rowfield', 'columnfield', 'sum(value)', 'integer');
which will create the prepared statement. Next you can execute the prepared statement:
execute crosstab_stmt;
I just improved Peter's solution:
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;
as in use :
select EXPORT.pivotcode_sql('EXPORT.TERMINAL_REVENUE', 'terminal_no','period', 'sum(amount)', 'numeric(12,2)','faydin.test15');
it creates the table faydin.test15
:
select * from faydin.test15
I hope it helps you as much as it did to me!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With