Reputation: 44094
This is my query:
SELECT
i::date AS day,
(SELECT COUNT(*) FROM genericevent WHERE event = 'chat_message' AND eventDate::date = i::date AND extra1 = 'public') AS message_public_total,
(SELECT COUNT(*) FROM genericevent WHERE event = 'chat_message' AND eventDate::date = i::date AND extra1 = 'public' AND extra2 = 'clean') AS message_public_clean
FROM generate_series('2013-08-01', '2013-08-27', INTERVAL '1 day') i
I have an index which I as a human consider fully usable for this query (in fact it should result in an index-only scan):
CREATE INDEX idx__genericevent__event__extra1__date
ON genericevent
USING btree
(event COLLATE pg_catalog."default", extra1 COLLATE pg_catalog."default", eventDate);
However, as EXPLAIN
ed, PostgreSQL doesn't deem it so. It uses event
and extra1
from this index, but not eventDate
(see the Index Cond
lines):
"Function Scan on generate_series i (cost=0.00..145219698.17 rows=1000 width=8)"
" SubPlan 1"
" -> Aggregate (cost=72274.87..72274.88 rows=1 width=0)"
" -> Bitmap Heap Scan on genericevent (cost=11367.74..72271.51 rows=1345 width=0)"
" Recheck Cond: (((event)::text = 'chat_message'::text) AND ((extra1)::text = 'public'::text))"
" Filter: ((eventDate)::date = (i.i)::date)"
" -> Bitmap Index Scan on idx__genericevent__event__extra1__date (cost=0.00..11367.40 rows=269012 width=0)"
" Index Cond: (((event)::text = 'chat_message'::text) AND ((extra1)::text = 'public'::text))"
" SubPlan 2"
" -> Aggregate (cost=72944.79..72944.80 rows=1 width=0)"
" -> Bitmap Heap Scan on genericevent (cost=11367.50..72943.80 rows=396 width=0)"
" Recheck Cond: (((event)::text = 'chat_message'::text) AND ((extra1)::text = 'public'::text))"
" Filter: (((extra2)::text = 'clean'::text) AND ((eventDate)::date = (i.i)::date))"
" -> Bitmap Index Scan on idx__genericevent__event__extra1__date (cost=0.00..11367.40 rows=269012 width=0)"
" Index Cond: (((event)::text = 'chat_message'::text) AND ((extra1)::text = 'public'::text))"
I think it may have to do something by the eventDate::date
cast. How can I change the query or the index to improve performance?
For completeness, here's the table:
CREATE TABLE genericevent
(
id bigint NOT NULL,
eventDate timestamp with time zone NOT NULL,
event character varying(50) NOT NULL,
extra1 character varying(100),
extra2 character varying(100),
CONSTRAINT genericevent_pkey PRIMARY KEY (id)
)
Upvotes: 5
Views: 3616
Reputation: 125214
This equivalent query does one only scan in instead of two like in yours.
select
i::date as day,
count(*) as message_public_total,
count(extra2 = 'clean' or null) as message_public_clean
from
genericevent
right join
generate_series(
'2013-08-01', '2013-08-27', interval '1 day'
) i on eventdate::date = i::date
where
event = 'chat_message'
and extra1 = 'public'
group by 1
Then the index would be
create index idx on genericevent (
eventDate::date,
event,
extra1
)
I placed the date first as I guess it has the highest cardinality.
Upvotes: 0
Reputation: 78423
You need to use timestamps for it to work, rather than dates.
On paper, you could change the index to an expression so it is date truncated to the specified date. But this won't work if the time stamp has a time zone, since it's then volatile due to the theoretical potential for the server's timezone to change.
In practice, you'd need to change the equality clause to an equivalent inequality, e.g. something like:
eventDate >= i and eventDate < i + interval '1 day'
But before proceeding with rewriting the query, note that you could simply add the appropriate where clauses to Clodoaldo Neto's query:
select
i::date as day,
count(*) as message_public_total,
count(extra2 = 'clean' or null) as message_public_clean
from
genericevent
right join
generate_series(
'2013-08-01', '2013-08-27', interval '1 day'
) i on eventdate::date = i::date
where
event = 'chat_message'
and extra1 = 'public'
and eventDate >= '2013-08-01'
and eventDate < '2013-08-27' + interval '1 day'
group by 1
Or:
select
i::date as day,
count(*) as message_public_total,
count(extra2 = 'clean' or null) as message_public_clean
from
genericevent
right join
generate_series(
'2013-08-01', '2013-08-27', interval '1 day'
) i on eventdate >= i and eventDate < i + interval '1 day'
where
event = 'chat_message'
and extra1 = 'public'
-- and eventDate >= '2013-08-01'
-- and eventDate < '2013-08-27' + interval '1 day'
group by 1
Upvotes: 2