X-Original-To: pgsql-performance-postgresql.org@localhost.postgresql.org Received: from localhost (av.hub.org [200.46.204.144]) by svr1.postgresql.org (Postfix) with ESMTP id 12F7CD8E45 for ; Wed, 14 Sep 2005 06:15:27 -0300 (ADT) Received: from svr1.postgresql.org ([200.46.204.71]) by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) with ESMTP id 47009-09 for ; Wed, 14 Sep 2005 09:15:22 +0000 (GMT) Received: from mail.metronet.co.uk (mail.metronet.co.uk [213.162.97.75]) by svr1.postgresql.org (Postfix) with ESMTP id F077BD6F0C for ; Wed, 14 Sep 2005 06:15:19 -0300 (ADT) Received: from mainbox.archonet.com (84-51-143-99.archon037.adsl.metronet.co.uk [84.51.143.99]) by smtp.metronet.co.uk (MetroNet Mail) with ESMTP id 0A7724186A2; Wed, 14 Sep 2005 10:15:13 +0100 (BST) Received: from localhost (localhost.localdomain [127.0.0.1]) by mainbox.archonet.com (Postfix) with ESMTP id 11A2015F1D; Wed, 14 Sep 2005 10:14:03 +0100 (BST) Received: from mainbox.archonet.com ([127.0.0.1]) by localhost (mainbox [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 16461-03; Wed, 14 Sep 2005 10:13:58 +0100 (BST) Received: from [192.168.1.17] (client17.office.archonet.com [192.168.1.17]) by mainbox.archonet.com (Postfix) with ESMTP id 9877E15F1C; Wed, 14 Sep 2005 10:13:58 +0100 (BST) Message-ID: <4327E9D6.5020203@archonet.com> Date: Wed, 14 Sep 2005 10:13:58 +0100 From: Richard Huxton User-Agent: Mozilla Thunderbird 1.0.2 (X11/20050317) X-Accept-Language: en-us, en MIME-Version: 1.0 To: Chris Kratz Cc: pgsql-performance@postgresql.org Subject: Re: Help with performance on current status column References: <200509131708.26638.chris.kratz@vistashare.com> In-Reply-To: <200509131708.26638.chris.kratz@vistashare.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-Virus-Scanned: by amavisd-new at hub.org X-Spam-Status: No, hits=0.057 required=5 tests=[AWL=0.007, FORGED_RCVD_HELO=0.05] X-Spam-Level: X-Archive-Number: 200509/185 X-Sequence-Number: 14493 Chris Kratz wrote: > Hello All, > > We are struggling with a specific query that is killing us. When doing > explain analyze on the entire query, we *seem* to be getting killed by the > estimated number of rows on a case statement calculation. > > I've included a snippet from the explain analyze of the much larger query. The > line in question, (cost=0.00..106.52 rows=1 width=16) (actual > time=0.048..67.728 rows=4725 loops=1) shows that it returned 4700 rows > instead of 1 which when coupled with a later join causes the statement to run > over 3 minutes.[1] > > It seems that it thinks that the scan on role_id is going to return 1 row, but > in reality returns 4725 rows. The case statement causing the problem uses > todays date to see if a particular row is still active. Here is a test case > showing how far off the estimate is from the reality. [2] > [2] A much simpler statement triggers the incorrect row counts here. > > explain analyze > select * > from roles rol > where > > CASE > WHEN rol.role_id IS NULL > THEN NULL > WHEN rol."begin" IS NOT NULL and rol."end" IS NOT NULL > THEN > CASE WHEN TIMESTAMP 'now'>=rol."begin" and TIMESTAMP > 'now'<=rol."end" > THEN 'Active' > ELSE 'Inactive' END > WHEN rol."begin" IS NOT NULL > THEN > CASE WHEN TIMESTAMP 'now'>=rol."begin" > THEN 'Active' > ELSE 'Inactive' END > WHEN rol."end" IS NOT NULL > THEN > CASE WHEN TIMESTAMP 'now'<=rol."end" > THEN 'Active' > ELSE 'Inactive' END > ELSE 'Active' > END = 'Active' Aside #1 - I'm not entirely clear how role_id can be null since you seemed to be joining against it in the real query. Aside #2 - You're probably better off with CURRENT_DATE since begin/end seem to be dates, rather than TIMESTAMP 'now' - and in any case you wanted "timestamp with time zone" OK, I think the root of your problem is your use of null to mean "not ended" or "not started" (whatever 'not started' means). PostgreSQL has the handy timestamptz value "infinity", but only for timestamps and not for dates. I'd probably cheat a little and use an end date of '9999-12-31' or similar to simulate "infinity". Then your test is simply: WHERE ... AND (rol.begin <= CURRENT_DATE AND rol.end >= CURRENT_DATE) That should estimate simply enough. -- Richard Huxton Archonet Ltd