Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wmwrs-00017j-2A for pgsql-bugs@arkaria.postgresql.org; Thu, 23 Jul 2026 17:05:56 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wmwqr-000GhV-2f for pgsql-bugs@arkaria.postgresql.org; Thu, 23 Jul 2026 17:04:54 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wmsxA-00GJqE-1t for pgsql-bugs@lists.postgresql.org; Thu, 23 Jul 2026 12:55:08 +0000 Received: from mahout.postgresql.org ([2001:4800:3e1:1::227]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wmsx7-00000001aIk-115P for pgsql-bugs@lists.postgresql.org; Thu, 23 Jul 2026 12:55:07 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=postgresql.org; s=20171124; h=Message-ID:Date:Reply-To:Cc:From:To:Subject: Content-Transfer-Encoding:MIME-Version:Content-Type:Sender:Content-ID: Content-Description:In-Reply-To:References; bh=jPwMx6voh9vGtPfGPKtxNmDk7pRQnohH77b0cF7qnew=; b=qtjsfHCwQcP3WVajS9C2Vxsl2n CcrK/vYt56az0xGNo1mHVcGvw747j7dfcgpRYNyl1+iFvwCVn5Ls/4x9QeFIxoQgNBZf3GUIm1Ddx f1fQz+SbhsuZNjmiqp+y/LO2DRSYbVa/8VrfuK8ld98+xd+58p7v1t6lIiXRTZ/bBKkikNfP+WUPr xyTfj3GICDQS4gdpSwufOA2+bvO3Xk6zoF1LFv2W72kS8+GSb0pikzQktFoilXjguW8UHINs30Sqz vkR7Ux/PhXn7MWUMBVR78Slg5skn+3yAJUNS3G0jBjM6XvSfjyacFA6n9B5Hsi+sGC4e4wtPmhcuH rXwFSHog==; Received: from wrigleys.postgresql.org ([2a02:16a8:dc51::60]) by mahout.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wmsx6-002N1r-1w for pgsql-bugs@lists.postgresql.org; Thu, 23 Jul 2026 12:55:05 +0000 Received: from localhost ([127.0.0.1] helo=wrigleys.postgresql.org) by wrigleys.postgresql.org with esmtp (Exim 4.98.2) (envelope-from ) id 1wmsx5-00000003kQt-1rq8 for pgsql-bugs@lists.postgresql.org; Thu, 23 Jul 2026 12:55:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19575: Enhancement for Redundant DISTINCT in set-operation branches To: pgsql-bugs@lists.postgresql.org From: PG Bug reporting form Cc: 2320415112@qq.com Reply-To: 2320415112@qq.com, pgsql-bugs@lists.postgresql.org Date: Thu, 23 Jul 2026 12:54:38 +0000 Message-ID: <19575-7ce4e703a249ba27@postgresql.org> X-Auto-Response-Suppress: All Auto-Submitted: auto-generated List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk The following bug has been logged on the website: Bug reference: 19575 Logged by: cl hl Email address: 2320415112@qq.com PostgreSQL version: 17.10 Operating system: ubuntu 22-04 Description: =20 ## Description PostgreSQL retains a `HashAggregate` in each input branch when `DISTINCT` is added below `UNION`, `INTERSECT`, or `EXCEPT`, even though the outer set operation already removes duplicates. ### Expected behaviour The planner should remove branch-level `DISTINCT` and let `HashAggregate` or `HashSetOp` at the set-operation level handle duplicate elimination. ### Actual behaviour For `UNION`, two branch `HashAggregate` nodes feed another set-level `HashAggregate`. For `INTERSECT` and `EXCEPT`, two branch `HashAggregate` nodes feed `HashSetOp`. | Operation | Without branch DISTINCT | With branch DISTINCT | Slowdown | |---|---:|---:|---:| | UNION | 32.48 ms | 62.13 ms | 1.91x | | INTERSECT | 24.49 ms | 50.26 ms | 2.05x | | EXCEPT | 22.90 ms | 49.67 ms | 2.17x | ## How to repeat ```sql DROP SCHEMA IF EXISTS pg_set_branch_distinct CASCADE; CREATE SCHEMA pg_set_branch_distinct; SET search_path TO pg_set_branch_distinct; CREATE TABLE lhs(id INT, v INT); CREATE TABLE rhs(id INT, v INT); INSERT INTO lhs SELECT n,n%1000 FROM generate_series(1,100000) AS n; INSERT INTO rhs SELECT n+50000,n%1000 FROM generate_series(1,100000) AS n; VACUUM ANALYZE; EXPLAIN (ANALYZE, COSTS OFF) SELECT COUNT(*) FROM ((SELECT DISTINCT id FROM lhs) UNION (SELECT DISTINCT id FROM rhs)) s; EXPLAIN (ANALYZE, COSTS OFF) SELECT COUNT(*) FROM ((SELECT DISTINCT id FROM lhs) INTERSECT (SELECT DISTINCT id FROM rhs)) s; EXPLAIN (ANALYZE, COSTS OFF) SELECT COUNT(*) FROM ((SELECT DISTINCT id FROM lhs) EXCEPT (SELECT DISTINCT id FROM rhs)) s; SELECT COUNT(*) FROM ((SELECT id FROM lhs) UNION (SELECT id FROM rhs)) s; SELECT COUNT(*) FROM ((SELECT DISTINCT id FROM lhs) UNION (SELECT DISTINCT id FROM rhs)) s; SELECT COUNT(*) FROM ((SELECT id FROM lhs) INTERSECT (SELECT id FROM rhs)) s; SELECT COUNT(*) FROM ((SELECT DISTINCT id FROM lhs) INTERSECT (SELECT DISTINCT id FROM rhs)) s; SELECT COUNT(*) FROM ((SELECT id FROM lhs) EXCEPT (SELECT id FROM rhs)) s; SELECT COUNT(*) FROM ((SELECT DISTINCT id FROM lhs) EXCEPT (SELECT DISTINCT id FROM rhs)) s; ```