Hi all,

  tl;dr - How can I speed up my count-distinct query?

  I apologize in advance if this question has been asked already.  I'm
finding the mailing list hard to navigate.  I'm trying to speed up a query
that will find a count of distinct emails with in a table using Postgres
9.3.3.  The name of the table is participants.  Our domain is set up such
that duplicate emails are allowed so long as a particular corresponding
column value is unique.


*TABLE participants*

*  id serial NOT NULL (primary key)*

*  email character varying(255)*

*  (other columns omitted)*



I have the following index defined:

*index_participants_on_email ON participants USING btree (email COLLATE
pg_catalog."default");*

The query I'm trying to run is select count(distinct email) from
participants.  I've also tried the *group by* equivalent.  *On a table size
of 2 million rows, the query takes about 1 minute to return.*  This is way
too long.  After running analyze, I see that the index is being ignored and
a full table scan is performed.

So, I tried running the following after dropping the index:
create index email_idx on participants(email) where email=email;
set enable_bitmapscan = false;
set seq_page_cost = 0.1;
set random_page_cost = 0.2;
create index email_idx_2 on participants(email);
cluster participants using email_idx_2;

With these settings in place, if I run *select count(distinct email) from
participants* I get

"Aggregate  (cost=29586.20..29586.21 rows=1 width=18) (actual
time=54243.643..54243.644 rows=1 loops=1)"
"  ->  Seq Scan on participants  (cost=0.00..24586.18 rows=2000008
width=18) (actual time=0.030..550.296 rows=2000008 loops=1)"
*"Total runtime: 54243.669 ms"*

When I run the following, I get MUCH better results
*select count(1) from (select email from participants where email=email
group by email) x;*

"Aggregate  (cost=1856.36..1856.37 rows=1 width=0) (actual
time=1393.573..1393.573 rows=1 loops=1)"
"  Output: count(1)"
"  ->  Group  (cost=0.43..1731.36 rows=10000 width=18) (actual
time=0.052..1205.977 rows=2000008 loops=1)"
"        Output: participants.email"
"        ->  Index Only Scan using email_idx on public.participants
 (cost=0.43..1706.36 rows=10000 width=18) (actual time=0.050..625.248
rows=2000008 loops=1)"
"              Output: participants.email"
"              Heap Fetches: 2000008"
*"Total runtime: 1393.599 ms"*

This query has a weird where clause (email=email) because I'm trying to
force the analyzer's hand to use the index.

*I'm concerned about setting the enable_bitmapscan and seq_page_cost values
because I'm not yet sure what the consequences are.  Can anyone enlighten
me on the recommended way to speed up this query?*

 Thanks

Reply via email to