So one thing that SQL can't do is give you an arbitrary number of columns. If you think about executing a query as two steps, the first step sets up the memory for the query by reading how many columns to expect, and the second actually fills it. If your (hypothetical) query were to have an arbitrary number of columns, it wouldn't be able to tell how many columns it needed until runtime.
That being said, there are a few ways to do it if you know ahead of time how many columns you need. In postgresql, you're going to want to use the crosstab()
function. Crosstab functions (kinda) like a pivot table in Excel. I'm not entirely familiar with it, so I'm working off this documentation page. Try the following:
SELECT *
FROM crosstab(
'select Id,ProductName,value
from base_table
order by 1',
'select distinct productName
from base_table
order by 1')
as ct(id, Product1, Product2, Product3);
Note: This is only a best-guess. It's likely not perfect for your purposes.