Solution 1. A simple request with an aggregate.
The easiest and fastest way to get the expected result. In the client program, it is easy to analyze the sales column.
select item, string_agg(coalesce(sales, 0)::text, ',') sales from ( select distinct item_id item, doy from generate_series (1, 10) doy -- change 10 to given n cross join entry_daily ) sub left join entry_daily on item_id = item and day_of_year = doy group by 1 order by 1; item | sales ------+---------------------- A1 | 20,0,0,0,0,0,9,0,0,0 A2 | 11,0,0,0,0,0,0,0,0,0 (2 rows)
Solution 2. Dynamically created view.
Based on solution 1 with array_agg() instead of string_agg() . The function creates a view with a given number of columns.
create or replace function create_items_view(view_name text, days int) returns void language plpgsql as $$ declare list text; begin select string_agg(format('s[%s] "%s"', i::text, i::text), ',') into list from generate_series(1, days) i; execute(format($f$ drop view if exists %s; create view %s as select item, %s from ( select item, array_agg(coalesce(sales, 0)) s from ( select distinct item_id item, doy from generate_series (1, %s) doy cross join entry_daily ) sub left join entry_daily on item_id = item and day_of_year = doy group by 1 order by 1 ) q $f$, view_name, view_name, list, days) ); end $$;
Using:
select create_items_view('items_view_10', 10); select * from items_view_10; item | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 ------+----+---+---+---+---+---+---+---+---+---- A1 | 20 | 0 | 0 | 0 | 0 | 0 | 9 | 0 | 0 | 0 A2 | 11 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 (2 rows)
Solution 3. Crosstab.
Easy to use, but very inconvenient with a large number of columns due to the need to determine the format of the row.
create extension if not exists tablefunc; select * from crosstab ( 'select item_id, day_of_year, sales from entry_daily order by 1', 'select i from generate_series (1, 10) i' ) as ct (item_id text, "1" int, "2" int, "3" int, "4" int, "5" int, "6" int, "7" int, "8" int, "9" int, "10" int); item_id | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10