问题
Let's say I have a this search query like this:
SELECT COUNT(id), date(created_at)
FROM entries
WHERE date(created_at) >= date(current_date - interval '1 week')
GROUP BY date(created_at)
As you know then for example I get a result back like this:
count | date
2 | 15.01.2014
1 | 13.01.2014
9 | 09.01.2014
But I do not get the days of the week where no entries where created.
How can I get a search result that looks like this, including the days where no entries where created?
count | date
2 | 15.01.2014
0 | 14.01.2014
1 | 13.01.2014
0 | 12.01.2014
0 | 11.01.2014
0 | 10.01.2014
9 | 09.01.2014
回答1:
SELECT day, COALESCE(ct, 0) AS ct
FROM (SELECT now()::date - d AS day FROM generate_series (0, 6) d) d -- 6, not 7
LEFT JOIN (
SELECT created_at::date AS day, count(*) AS ct
FROM entries
WHERE created_at >= date_trunc('day', now()) - interval '6d'
GROUP BY 1
) e USING (day);
Use a sargable expression for your
WHERE
condition, so Postgres can use a plain index oncreated_at
. Far more important for performance than all the rest.To cover a week (including today), subtract 6 days from the start of "today", not 7.
Assuming that
id
is definedNOT NULL
,count(*)
is identical tocount(id)
here, but slightly faster.A CTE would be overkill here. It's slower and more verbose.
Aggregate first, join later. That's faster in this case.
now()
is the slightly shorter and faster Postgres implementation of the standard SQL CURRENT_TIMESTAMP (which you can use as well).
This should be the shortest and fastest query. Test with EXPLAIN ANALYZE
.
Related:
- Selecting sum and running balance for last 18 months with generate_series
- PostgreSQL: running count of rows for a query 'by minute'
回答2:
Try this query:
with a as (select current_date - n as dt from generate_series(0, 6) as t(n)),
b as (select count(id) cnt, date(created_at) created_at
from entries
where date(created_at) >= date(current_date - interval '1 week')
group by date(created_at))
select coalesce(b.cnt,0), a.dt
from a
left join b on (a.dt = b.created_at)
order by a.dt;
count
function will not generate 0 for non-existing rows. So you have to fill the rows for missing dates. With generate_series
and simple date arithmetic, you can generate rows for dates of some period (in this case, 1 week). Then you can outer join to generate the final result. coalesce
will substitute null
to 0
.
回答3:
You need to tell SQL to handle NULL. Return 0
if NULL
You can do this by COALESCE
http://www.postgresql.org/docs/devel/static/functions-conditional.html
回答4:
Use generate_series()
to create the dates you need and JOIN to this list of dates:
SELECT COUNT(id),
date(gdate)
FROM entries
RIGHT JOIN generate_series(current_date - interval '1 week', current_date, '1 day') g(gdate)
ON date(created_at) = date(gdate) AND date(created_at) >= date(current_date - interval '1 week')
GROUP BY
date(gdate)
ORDER BY
date(gdate) DESC;
来源:https://stackoverflow.com/questions/29362384/get-count-of-created-entries-for-each-day