For more predictable performance (and to avoid having high variation of rows per page), I would lean to storing this data in a related table - especially if it is only populated a small percentage of the time, and especially if it is only retrieved in some of the queries. The rows where this value is NULL
do contribute to space overhead, but this is minimal. More important will be how one page might only fit two rows and the next page can fit 500 rows - this can really impact statistics and you might be better off splitting this out so it is stored separately and doesn't impact all of your operations on the core table.
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
GETDATE()
returns the current system timestamp as aDATETIME
value (date and time to the milliseconds). That's obviously different than just theDATE
value string you provided of'01/06/2023'
. YourTxn_Date
probably truly doesn't have any values with the same milliseconds as the instant in time when you ran the query withGETDATE()
.To make your query work with
GETDATE()
, you need to cast it to just aDATE
data type like so: