I have to display a table like this:
| Year | Month | Delivered | Not delivered | Not Received |
|---|---|---|---|---|
| 2021 | Jan | 10 | 86 | 75 |
| 2021 | Feb | 13 | 36 | 96 |
| 2021 | March | 49 | 7 | 61 |
| 2021 | Apr | 3 | 21 | 72 |
Using raw data generated by this query:
SELECT
year,
TO_CHAR( creation_date, 'Month') AS month,
marking,
COUNT(*) AS count
FROM invoices
GROUP BY 1,2,3
I have tried using crosstab() but I got error:
SELECT * FROM crosstab('
SELECT
year,
TO_CHAR( creation_date, ''Month'') AS month,
marking,
COUNT(*) AS count
FROM invoices
GROUP BY 1,2,3
') AS ct(year text, month text, marking text)
I would prefer to not manually type all marking values because they are a lot.
ERROR: invalid source data SQL statement DETAIL: The provided SQL must return 3 columns: rowid, category, and values.
1. Static solution with a limited list of
markingvalues :2. Full dynamic solution with a large list of
markingvalues :This proposal is an alternative solution to the
crosstabsolution as proposed in A and B.The proposed solution here just requires a dedicated
composite typewhich can be dynamically created and then it relies on thejsonbtype and standard functions :Starting from your query which counts the number of rows per year, month and
markingvalue :jsonb_object_aggfunction, the resulting rows are first aggregated by year and month intojsonbobjects whosejsonb keyscorrespond to themarkingvalues and whosejsonb valuescorrespond to the counts.jsonbobjects are then converted into records using thejsonb_populate_recordfunction and the dedicated composite type.First we dynamically create a
composite typewhich corresponds to the ordered list ofmarkingvalues :Then the expected result is provided by the following query :
Obviously, if the list of
markingvalues may vary in time, then you have to recall thecreate_composite_type()procedure just before executing the query. If you don't update thecomposite_type, the query will still work (no error !) but some old marking values may be obsolete (not used anymore), and some new marking values may be missing in the query result (not displayed as columns).See the full demo in dbfiddle.