I suggest a radically new approach with crosstab()
from the additional module tablefunc. You need to install it once per database. Detailed instructions:
SELECT k.name, keyword_id, project_id, now()::date AS the_date
, t AS today, y As yesterday, w AS week, m AS month
FROM crosstab(
$$
SELECT rn
, pk.keyword_id
, pk.project_id
, d.created_at
, COALESCE(pr.pos, 0) AS pos
FROM (SELECT *, row_number() OVER () AS rn FROM project_keyword) pk
CROSS JOIN (
VALUES
(now()::date)
, (now()::date - 1)
, (now()::date - 7)
,((now() - interval '1 month')::date)
) d(created_at)
LEFT JOIN (
SELECT keyword_id
, project_id
, created_at::date AS created_at
, min(position) AS pos
FROM project_report
GROUP BY keyword_id, project_id, created_at::date
) pr USING (keyword_id, project_id, created_at)
ORDER BY pk.rn, d.created_at
$$
,$$
VALUES
(now()::date)
, (now()::date - 1)
, (now()::date - 7)
, ((now() - interval '1 month')::date)
$$
) ct (rn int, keyword_id int, project_id int
, t int, y int, w int, m int)
JOIN keyword k USING (keyword_id);
Returns:
name keyword_id project_id the_date today yesterday week month
Cheese 1 1 2014-07-11 1 1 1 0
Monitor 2 1 2014-07-11 2 2 2 0
Apple 3 1 2014-07-11 9 9 9 0
Apple1 4 1 2014-07-11 0 0 0 0
Iphone 5 1 2014-07-11 0 33 34 0
Explain
We need a single unique column per group for crosstab()
. Since you don't have that per (keyword_id int, project_id)
I am adding a surrogate key with row_number()
to project_keyword
.
JOIN
that to a set of dates you are interested in (today, yesterday etc.). One row per date.
LEFT JOIN
to project_reports
to attach the position for each date if available. Default to 0 with COALESCE
.
Cross-tabulate to get a single row with one position column per given date.
Finally JOIN
to keyword
just to add the name
. Add the current date and remove rn
from the result while being at it.
Aside: "name" is not a good name. I'd use something descriptive instead.
Best Answer
What you need to do is:
string_agg
them together withUNION ALL
as the joining string.UNION ALL
is important, not justUNION
, asUNION
may force a sort-and-de-duplicate operation, which is slow.I will use the
format
function and its%I
and%L
format-specififers instead ofquote_ident
andquote_literal
respectively, because it's massively more readable that way.I have no idea what the stray
ROM
in:is. It doesn't make sense. I'm ignoring it. I assume it's probably an editing error, the end of an accidentally remaining
FROM
.So, first, take a query that produces a list of queries for each table:
then wrap it in an aggregate (
string_agg
) to combine the strings, prefix theCREATE VIEW
and store the result in your query variable and execute it:BTW, needing to do things like this often means you have a bit of a database design issue. You should not need tons of little tables with the same schema like this. It usually means you're doing this:
when you should usually be doing this: