I have a table with data:
create Table ProductSales
(
Productname varchar(50),
Year int,
Sales int
);
Insert into ProductSales
values ('A', 2017, 100),
('A', 2018, 150),
('A', 2019, 300),
('A', 2020, 500),
('A', 2021, 450),
('A', 2022, 675),
('B', 2017, null),
('B', 2019, 1120),
('B', 2020, 750),
('B', 2021, 1500),
('B', 2022, 1980);
Productname | Year | Sales |
---|---|---|
A | 2017 | 100 |
A | 2018 | 150 |
A | 2019 | 300 |
A | 2020 | 500 |
A | 2021 | 450 |
A | 2022 | 675 |
B | 2017 | null |
B | 2019 | 1120 |
B | 2020 | 750 |
B | 2021 | 1500 |
B | 2022 | 1980 |
And I want to get the result like this:
Productname | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 |
---|---|---|---|---|---|---|
A | 100 | 150 | 300 | 500 | 450 | 675 |
B | null | null | 1120 | 750 | 1500 | 1980 |
Is it possible to make it with postgresql? I tried to use crosstab()
but failed.
Besides, it’s possible to have separate table with need years, e.g.:
|Year|
|----|
|2017|
|2018|
|2019|
|2020|
Does it change something?
2
Answers
By using your data in DBFIDDLE
The working query using
crosstab
can be written as :This gives me expected output :
You can’t have a dynamic number of columns in a single SQL query.
If you use two, the first one can determine your target structure, the second one use it. You can have dynamic SQL construct and
prepare
a statement for you to only call anexecute
onThe list isn’t exhaustive. None of this is pretty, all is error-prone. You can keep pushing how "dynamic" it gets until you run out of patience (or prescription meds): demo extending Tushar’s
And then, each time you want to use it, you need to repeat your two steps:
I went with prepared statements because they are at least session-specific – other users can share the
first_step_procedure()
but theirsecond_step_statement
generated by it won’t be interfering.If you want to maintain a table(view) based on that ProductSales, you can push this further with a trigger that keeps redefining the dependent object: demo
Keep in mind that static references to ProductSalesYearly that’s being constantly redefined, keep breaking – even though the name remains the same, it’s a new object. It also keeps firing on all traffic on ProductSales. You could mitigate that to an extent by constructing "migrations", altering the table adding/removing columns each time, instead of dropping and reconstructing.