1

I have a large dataset with many different product. In a small scale it would look like this:

product month amount
AA 1 100
AA 1 150
AA 2 200
AA 2 120
BB 2 180
BB 2 220
CC 3 80

I want get the info in a different order. Write as many new columns as distinct values from the column "product", then fill it with the sum of the amount by month. It would look like this:

month AA BB CC
1 250 NA NA
2 320 400 NA
3 NA NA 80

The important thing is to PIVOT the table, that's the main problem I have. I saw similar questions but all have being solved with PIVOT function but I'm doing this with a Postgres database in DBeaver and it doesn't have the PIVOT function:

SELECT *
FROM (
    SELECT product, month, amount
    FROM ventas
) AS SourceTable
PIVOT (
    SUM(amount)
    FOR month IN ([1], [2], [3])
) AS PivotTable;

SQL Error [42601]: ERROR: syntax error at or near "PIVOT" Position: 95

I've tried different ways but no success. I can't write the names of all products in the query as there are too many!

Here is a test setup for your convenience:

CREATE TABLE ventas (
    product VARCHAR(50),
    month INT,
    amount INT
);

INSERT INTO ventas (product, month, amount) VALUES 
('AA', 1, 100),
('AA', 1, 150),
('AA', 2, 200),
('AA', 2, 120),
('BB', 2, 180),
('BB', 2, 220),
('CC', 3, 80),
4
  • 1
    postgres has no command PIVOT use CROSSTAB instead see stackoverflow.com/questions/20618323/… Commented Mar 13, 2024 at 23:51
  • Thanks nbk. It's a good info, but in this case, how can I solve the fact that there are many unique values for the column and I can't type all of them? Because in this example and all that I checked they are written, and I have to create more than 100 columns Commented Mar 13, 2024 at 23:56
  • For unknown number of columns you almost always have to use dynamic sql. That being said, why can't you have result having 12 months as columns x n products as rows? Commented Mar 14, 2024 at 0:10
  • Do you actually need 100 separate output columns? Else, an array (for same data types) or a document type like json / jsonb / hstore / xml is much easier to handle ... Commented Mar 14, 2024 at 2:21

1 Answer 1

1

If you actually need separate output columns, the only reasonable approach is a 2-step flow:

  1. Generate the query dynamically.
  2. Execute it.

If you are not familiar with the crosstab() function, read this first:

-- generate crosstab() query
SELECT format(
$f$  -- begin dynamic query string
SELECT * FROM crosstab(
   $q$
   SELECT month, product, sum(amount)
   FROM   ventas
   GROUP  BY 1, 2
   ORDER  BY 1, 2
   $q$
 , $c$VALUES (%s)$c$
   ) AS ct(month int, %s);
$f$  -- end dynamic query string
            , string_agg(quote_literal(sub.product), '), (')
            , string_agg(quote_ident  (sub.product), ' int, ') || ' int'
                 )
FROM  (SELECT DISTINCT product FROM ventas ORDER BY 1) sub;

This generates a query of the form:

SELECT * FROM crosstab(
   $q$
   SELECT month, product, sum(amount)
   FROM   ventas
   GROUP  BY 1, 2
   ORDER  BY 1, 2
   $q$
 , $c$VALUES ('AA'), ('BB'), ('CC')$c$
   ) AS ct(month int, "AA" int, "BB" int, "CC" int);

... which you then execute.

fiddle

Closely related, with more explanation and options:

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you so much Erwin! Appreciate the fiddle tool, got it!
But from what I understand, you always have to type the name of the columns of the product, and in my real case there are more than 100... any idea?
@FranciscoMS: No you don't type any product names here. Postgres does that for you, that's the point.
Thank you Erwin, understood all, thanks so much!!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.