I am trying to create a pivot table in Postgres from the following table
named product_info
:
CREATE TABLE product_info (
ID varchar(10),
date VARCHAR(20) NOT NULL,
product VARCHAR(20) NOT NULL,
product_cost integer
);
INSERT INTO product_info(ID, date, product, product_cost)
VALUES
('1', 'AUG-23','Laptop',100),
('1', 'AUG-23','Phone',80),
('1', 'AUG-23','Keypad',50),
('1', 'SEP-23','Laptop',200),
('1', 'SEP-23','Phone',100),
('1', 'SEP-23','Ipad',150),
('1', 'SEP-23','Keypad',80),
('2', 'AUG-23','Laptop',100),
('2', 'AUG-23','Phone',90),
('2', 'AUG-23','Keypad',70),
('2', 'SEP-23','Laptop',120),
('2', 'SEP-23','Phone',30),
('2', 'SEP-23','Ipad',80),
('2', 'SEP-23','Keypad',40);
Tried below PIVOT Query
SELECT * FROM crosstab(
'SELECT date, id,product, sum(product_cost)
FROM product_info
GROUP BY 1, 2,3
ORDER BY 1, 2,3'
,
'SELECT DISTINCT product FROM product_info ORDER BY 1'
) as cte(
date text,
id text,
"Ipad" numeric,
"Keypad" numeric,
"Laptop" numeric,
"Phone" numeric
)
RETURNED RESULT
date | id | Ipad | Keypad | Laptop | Phone |
---|---|---|---|---|---|
AUG-23 | 1 | NULL | 70 | 100 | 90 |
SEP-23 | 1 | 80 | 40 | 120 | 30 |
I get a single row per month, with id = '1'
and values for id = '2'
.
I expected one row per ID.
EXPECTED RESULT
id | date | Ipad | Keypad | Laptop | Phone |
---|---|---|---|---|---|
1 | AUG-23 | NULL | 50 | 100 | 80 |
2 | AUG-23 | NULL | 70 | 100 | 90 |
1 | SEP-23 | 150 | 80 | 200 | 100 |
2 | SEP-23 | 80 | 40 | 120 | 30 |
2
Answers
As you can use only obe row with crosstab, you can do something like this.
But simpler is a normal aggregation like the second query
fiddle
Proper
crosstab()
callQuoting the manual for
crosstab()
:Your query does not produce data in the expected form:
This query does:
Your "row name" is a composite of
(date, id)
. (Simply concatenating could produce ambiguous results.) Generate a single-columnrow_name
with the window functiondense_rank()
as demonstrated.date
andid
can be passed as "extra" columns.Also, a
SELECT
query that generates category names dynamically rarely makes sense, since the static (!) column definition list has to match.The query is based off an actual
date
column. Don’t store date information astext
, which does not sort properly.This
crosstab()
call works:fiddle
See:
Plain SQL
Typically,
crosstab()
is the fastest option. But for only few products, and since this case requires aggregation and a window function anyway, plain SQL may be even faster.fiddle
See: