Note the slightly modified schema of my test in the fiddle.
Using actual primary keys and proper column names instead of id
.
Also, you seem to be operating with dates exclusively. So I suggest to convert your timestamp
columns to date
.
Items 1 and 2
SELECT k.keyword_id
, k.name
, pr.project_id
, COALESCE(min(pr.position), 0) AS pos
, COALESCE(pr.created_at, now()::date) AS created_at
FROM keyword k
LEFT JOIN project_report pr USING (keyword_id)
GROUP BY k.keyword_id, pr.project_id, pr.created_at
ORDER BY keyword_id, created_at
;
- In Postgres 9.1 or later the pk column covers the whole table in
GROUP BY
.
- Use
COALESCE
to replace possible NULL
values.
A guess at item 3
WITH cte AS (
SELECT k.keyword_id
, k.name
, pr.project_id
, COALESCE(min(pr.position), 0) AS pos
, COALESCE(pr.created_at, now()::date) AS created_at
FROM keyword k
LEFT JOIN project_report pr USING (keyword_id)
GROUP BY k.keyword_id, pr.project_id, pr.created_at
)
, x AS (
SELECT DISTINCT ON (keyword_id, project_id) *
FROM cte
ORDER BY keyword_id, project_id, created_at DESC
)
SELECT x.*
, COALESCE(y.pos, 0) AS yesterday_pos
, COALESCE(w.pos, 0) AS week_pos
, COALESCE(m.pos, 0) AS month_pos
FROM x
LEFT JOIN cte y ON y.keyword_id = x.keyword_id
AND y.project_id = x.project_id
AND y.created_at = x.created_at - interval '1 day'
LEFT JOIN cte w ON w.keyword_id = x.keyword_id
AND w.project_id = x.project_id
AND w.created_at = x.created_at - interval '1 week'
LEFT JOIN cte m ON m.keyword_id = x.keyword_id
AND m.project_id = x.project_id
AND m.created_at = x.created_at - interval '1 month'
;
Explain
- In CTE
cte
produce daily aggregates per (k.keyword_id, pr.project_id)
.
- In CTE
x
pick the latest day per (k.keyword_id, pr.project_id)
.
- In the outer query
LEFT JOIN
the latest day x
to cte
multiple times to retrieve past values from the same (k.keyword_id, pr.project_id)
for 1 day / week / month earlier.
SQL Fiddle.
Best Answer
The total number of rows per
UserID
is easy, you just need to useCOUNT(*)
. As for the other column, then, assumingName
cannot be null, you need to count distinctName
values and compare the result to 1. To explain: if all names are identical,COUNT(DISTINCT Name))
will return 1, otherwise it will return a different number. Thus, by comparing the result to 1 you will determine whether all names are unique or not.This is how I would implement it in SQL:
Feel free to play with this solution at dbfiddle.uk.