Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.84_2) (envelope-from ) id 1e8ljS-00018Q-KD for pgsql-performance@arkaria.postgresql.org; Sun, 29 Oct 2017 11:26:23 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.84_2) (envelope-from ) id 1e8ljS-0002dd-2m for pgsql-performance@arkaria.postgresql.org; Sun, 29 Oct 2017 11:26:22 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1e8lhh-0007rl-Kp for pgsql-performance@postgresql.org; Sun, 29 Oct 2017 11:24:33 +0000 Received: from mx.zeyos.com ([88.99.153.70]) by magus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1e8lha-0000bQ-Rj for pgsql-performance@postgresql.org; Sun, 29 Oct 2017 11:24:33 +0000 Received: from mx.zeyos.com (localhost [127.0.0.1]) by mx.zeyos.com (Postfix) with ESMTP id 8920B5FB05 for ; Sun, 29 Oct 2017 12:24:20 +0100 (CET) Authentication-Results: mx.zeyos.com (amavisd-new); dkim=pass reason="pass (just generated, assumed good)" header.d=zeyos.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=zeyos.com; h= content-transfer-encoding:content-type:content-type:mime-version :to:subject:subject:from:from:date:date; s=dkim; t=1509276260; x=1510140261; bh=JbbPSvZ7QDtkPf+BBDS50VUsVJ85ehgYHtyXn+DxLZU=; b= YTAp9iaxlQ/qul5Utu0Q7vmjIn6MK8UGqFIXcC/YC6JuTP1eGeCM9WzzWjB3tbmv 5hrScndnc25GGrfcDj9q6ufMKSSNylZ7cbuuX+v/fGI3DupW3V8+5MoTcMme3X2T 4HF16dBz5E9bRsMRrqZXv/Gf7I5qo6WyDRJAYKj6D/8= X-Virus-Scanned: Debian amavisd-new at mx.zeyos.com Received: from mx.zeyos.com ([127.0.0.1]) by mx.zeyos.com (mx.zeyos.com [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id PYvWRTwXVqjh for ; Sun, 29 Oct 2017 12:24:20 +0100 (CET) Received: from [81.171.8.203] (unknown [81.171.8.203]) by mx.zeyos.com (Postfix) with ESMTPSA id 3DB925FAFE; Sun, 29 Oct 2017 12:24:20 +0100 (CET) Date: Sun, 29 Oct 2017 12:24:20 +0100 From: Benjamin Coutu Subject: Cheaper subquery scan not considered unless offset 0 To: pgsql-performance@postgresql.org Cc: Tom Lane MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20171029112420.8920B5FB05@mx.zeyos.com> List-Archive: List-Help: List-ID: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-Mailing-List: pgsql-performance Precedence: bulk Sender: pgsql-performance-owner@postgresql.org Hello everyone, Please consider the following three semantically equivalent, but differentl= y written queries: Query A: SELECT * FROM items a INNER JOIN ( SELECT item, sum(amount) stock FROM stocktransactions GROUP BY item HAVIN= G sum(amount) >=3D 1 ) b ON b.item =3D a. "ID" Query B: SELECT * FROM items a INNER JOIN ( SELECT item, sum(amount) stock FROM stocktransactions GROUP BY item ) b ON b.item =3D a. "ID" WHERE b.stock >=3D 1 Query C: SELECT * FROM items a INNER JOIN ( SELECT item, sum(amount) stock FROM stocktransactions b GROUP BY item OFF= SET 0 ) b ON b.item =3D a. "ID" WHERE b.stock >=3D 1 FYI: stocktransactions.item and stocktransactions.amount have not null cons= traints and stocktransactions.item is a foreign key referencing items.ID, t= he primary key of items. Queries A + B generate the same plan and execute as follows: Merge Join (cost=3D34935.30..51701.59 rows=3D22285 width=3D344) (actual ti= me=3D463.824..659.553 rows=3D15521 loops=3D1) Merge Cond: (a."ID" =3D b.item) -> Index Scan using "PK_items_ID" on items a (cost=3D0.42..15592.23 row= s=3D336083 width=3D332) (actual time=3D0.012..153.899 rows=3D336064 loops= =3D1) -> Sort (cost=3D34934.87..34990.59 rows=3D22285 width=3D12) (actual tim= e=3D463.677..466.146 rows=3D15521 loops=3D1) Sort Key: b.item Sort Method: quicksort Memory: 1112kB -> Finalize HashAggregate (cost=3D32879.78..33102.62 rows=3D22285= width=3D12) (actual time=3D450.724..458.667 rows=3D15521 loops=3D1) Group Key: b.item Filter: (sum(b.amount) >=3D '1'::double precision) Rows Removed by Filter: 48277 -> Gather (cost=3D27865.65..32545.50 rows=3D44570 width=3D1= 2) (actual time=3D343.715..407.243 rows=3D162152 loops=3D1) Workers Planned: 2 Workers Launched: 2 -> Partial HashAggregate (cost=3D26865.65..27088.50 r= ows=3D22285 width=3D12) (actual time=3D336.416..348.105 rows=3D54051 loops= =3D3) Group Key: b.item -> Parallel Seq Scan on stocktransactions b (co= st=3D0.00..23281.60 rows=3D716810 width=3D12) (actual time=3D0.015..170.646= rows=3D579563 loops=3D3) Planning time: 0.277 ms Execution time: 661.342 ms Plan C though, thanks to the "offset optimization fence", executes the foll= owing, more efficient plan: Nested Loop (cost=3D32768.77..41146.56 rows=3D7428 width=3D344) (actual ti= me=3D456.611..525.395 rows=3D15521 loops=3D1 total=3D525.395) -> Subquery Scan on c (cost=3D32768.35..33269.76 rows=3D7428 width=3D12= ) (actual time=3D456.591..475.204 rows=3D15521 loops=3D1 total=3D475.204) Filter: (c.stock >=3D '1'::double precision) Rows Removed by Filter: 48277 -> Finalize HashAggregate (cost=3D32768.35..32991.20 rows=3D22285= width=3D12) (actual time=3D456.582..468.124 rows=3D63798 loops=3D1 total= =3D468.124) Group Key: b.item -> Gather (cost=3D27865.65..32545.50 rows=3D44570 width=3D1= 2) (actual time=3D348.479..415.463 rows=3D162085 loops=3D1 total=3D415.463) Workers Planned: 2 Workers Launched: 2 -> Partial HashAggregate (cost=3D26865.65..27088.50 r= ows=3D22285 width=3D12) (actual time=3D343.952..355.912 rows=3D54028 loops= =3D3 total=3D1067.736) Group Key: b.item -> Parallel Seq Scan on stocktransactions b (co= st=3D0.00..23281.60 rows=3D716810 width=3D12) (actual time=3D0.015..172.235= rows=3D579563 loops=3D3 total=3D516.705) -> Index Scan using "PK_items_ID" on items a (cost=3D0.42..1.05 rows=3D= 1 width=3D332) (actual time=3D0.003..0.003 rows=3D1 loops=3D15521 total=3D4= 6.563) Index Cond: ("ID" =3D c.item) Planning time: 0.223 ms Execution time: 526.203 ms I'm wondering, given that Query C's plan has lower overall costs than Query= A/B's, why wouldn't the planner choose to execute that plan for queries A+= B as well? It has lower projected startup cost as well as lower total cost so apparent= ly the optimzer does not consider such a plan with a subquery scan at all (= otherwise it would choose it based on the lower cost estimates, right?) unl= ess one forces it to via OFFSET 0. Though I wouldn't necessarily consider this a bug, it is an issue that one = has to explicitly work around with inadvisable optimization fences and it w= ould be great if this could be fixed. Thanks to the developer community for delivering this great product, I hope= this helps in enhancing it. Cheers, Benjamin --=20 Bejamin Coutu ben.coutu@zeyos.com ZeyOS, Inc. http://www.zeyos.com --=20 Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-performance