Received: from localhost (unknown [200.46.204.184]) by postgresql.org (Postfix) with ESMTP id 2F2E29F949A for ; Thu, 1 Nov 2007 11:20:56 -0300 (ADT) Received: from postgresql.org ([200.46.204.71]) by localhost (mx1.hub.org [200.46.204.184]) (amavisd-maia, port 10024) with ESMTP id 98150-03 for ; Thu, 1 Nov 2007 11:20:26 -0300 (ADT) X-Greylist: from auto-whitelisted by SQLgrey-1.7.5 Received: from melon.pingpong.net (melon.pingpong.net [195.178.174.161]) by postgresql.org (Postfix) with ESMTP id 5DF249F9588 for ; Thu, 1 Nov 2007 11:20:16 -0300 (ADT) Received: from localhost (localhost.pingpong.net [127.0.0.1]) by melon.pingpong.net (Postfix) with ESMTP id F18AA509CB; Thu, 1 Nov 2007 15:20:19 +0100 (CET) Received: from melon.pingpong.net ([127.0.0.1]) by localhost (melon.pingpong.net [127.0.0.1]) (amavisd-new, port 10024) with LMTP id 75319-01-14; Thu, 1 Nov 2007 15:20:19 +0100 (CET) Received: from [192.168.1.187] (unknown [213.136.40.204]) by melon.pingpong.net (Postfix) with ESMTP id AB9F35091E; Thu, 1 Nov 2007 15:20:19 +0100 (CET) Date: Thu, 01 Nov 2007 15:20:14 +0100 From: Palle Girgensohn To: Tom Lane cc: pgsql-performance@postgresql.org Subject: Re: select max(field) from table much faster with a group by clause? Message-ID: <00DC41259D75AF3300AAF9D2@rambutan.pingpong.net> In-Reply-To: <4305.1193924619@sss.pgh.pa.us> References: <4305.1193924619@sss.pgh.pa.us> X-Mailer: Mulberry/4.0.8 (Linux/x86) MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Content-Disposition: inline X-Virus-Scanned: by amavisd-new at pingpong.net X-Virus-Scanned: Maia Mailguard 1.0.1 X-Archive-Number: 200711/13 X-Sequence-Number: 27428 --On torsdag, november 01, 2007 09.43.39 -0400 Tom Lane wrote: > Palle Girgensohn writes: >> When running >> select max("when") from login where userid='userid' >> it takes a second or two, but when adding "group by userid" the planner >> decides on using another plan, and it gets *much* faster. See example >> below. > > It's only faster for cases where there are few or no rows for the > particular userid ... Well, no, not really. See below. OTH, it sometimes a bit slower. Seems to depend on how far away from the estimated number of rows you get? Weird? >> Number of tuples per user varies from zero to a couple of thousands. > > The planner is using an intermediate estimate of 406 rows. You might be > well advised to increase the statistics target for login.userid --- with > luck that would help it to choose the right plan type for both common > and uncommon userids. I'll try that, thanks! -- pp=# SELECT max("when") FROM login WHERE userid='kudo' group by userid; max ------------------------------- 2007-01-04 15:31:46.863325+01 (1 row) Time: 6,194 ms pp=# SELECT max("when") FROM login WHERE userid='kudo' ; max ------------------------------- 2007-01-04 15:31:46.863325+01 (1 row) Time: 992,391 ms pp=# SELECT max("when") FROM login WHERE userid='kudo' ; max ------------------------------- 2007-01-04 15:31:46.863325+01 (1 row) Time: 779,582 ms pp=# SELECT max("when") FROM login WHERE userid='kudo' ; max ------------------------------- 2007-01-04 15:31:46.863325+01 (1 row) Time: 818,667 ms pp=# SELECT max("when") FROM login WHERE userid='kudo' ; max ------------------------------- 2007-01-04 15:31:46.863325+01 (1 row) Time: 640,242 ms pp=# SELECT max("when") FROM login WHERE userid='kudo' group by userid; max ------------------------------- 2007-01-04 15:31:46.863325+01 (1 row) Time: 18,384 ms pp=# SELECT count(*) FROM login WHERE userid='kudo' group by userid; count ------- 1998 (1 row) Time: 12,762 ms pp=# explain analyze SELECT max("when") FROM login WHERE userid='kudo' group by userid; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------- GroupAggregate (cost=0.00..648.44 rows=1 width=25) (actual time=24.700..24.703 rows=1 loops=1) -> Index Scan using login_userid_idx on "login" (cost=0.00..646.40 rows=406 width=25) (actual time=0.140..16.931 rows=1998 loops=1) Index Cond: (userid = 'kudo'::text) Total runtime: 24.779 ms (4 rows) Time: 25,633 ms pp=# explain analyze SELECT max("when") FROM login WHERE userid='kudo' ; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------ Result (cost=323.93..323.94 rows=1 width=0) (actual time=1400.994..1400.997 rows=1 loops=1) InitPlan -> Limit (cost=0.00..323.93 rows=1 width=8) (actual time=1400.975..1400.979 rows=1 loops=1) -> Index Scan Backward using login_when_idx on "login" (cost=0.00..131515.87 rows=406 width=8) (actual time=1400.968..1400.968 rows=1 loops=1) Filter: (("when" IS NOT NULL) AND (userid = 'kudo'::text)) Total runtime: 1401.057 ms (6 rows) Time: 1401,881 ms pp=# SELECT userid, count("when") FROM login WHERE userid in ('girgen' , 'kudo') group by userid; userid | count --------+------- kudo | 1998 girgen | 1120 (2 rows) pp=# explain analyze SELECT max("when") FROM login WHERE userid='girgen' group by userid; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------- GroupAggregate (cost=0.00..648.44 rows=1 width=25) (actual time=25.137..25.141 rows=1 loops=1) -> Index Scan using login_userid_idx on "login" (cost=0.00..646.40 rows=406 width=25) (actual time=0.121..20.712 rows=1120 loops=1) Index Cond: (userid = 'girgen'::text) Total runtime: 25.209 ms (4 rows) Time: 25,986 ms pp=# explain analyze SELECT max("when") FROM login WHERE userid='girgen' ; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------ Result (cost=323.93..323.94 rows=1 width=0) (actual time=6.695..6.698 rows=1 loops=1) InitPlan -> Limit (cost=0.00..323.93 rows=1 width=8) (actual time=6.669..6.675 rows=1 loops=1) -> Index Scan Backward using login_when_idx on "login" (cost=0.00..131515.87 rows=406 width=8) (actual time=6.660..6.660 rows=1 loops=1) Filter: (("when" IS NOT NULL) AND (userid = 'girgen'::text)) Total runtime: 6.785 ms (6 rows) Time: 7,776 ms