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 1wmTre-000o7g-29 for pgsql-bugs@arkaria.postgresql.org; Wed, 22 Jul 2026 10:07:47 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wmTrd-00BUZ4-1s for pgsql-bugs@arkaria.postgresql.org; Wed, 22 Jul 2026 10:07:45 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wmQmT-00AnKP-05 for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:50:12 +0000 Received: from mahout.postgresql.org ([2001:4800:3e1:1::227]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wmQmN-00000000Z4r-2JIU for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:50:12 +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=E9sJXlZjyokVP89WMlNwtQqYt3/h78RW5q+aTho8QLM=; b=sKJ9LN6Cp723XJW+dvhtg7tdmL TcJ9wbK64EmEhSTXjJTCeOVBesnl1UrPgp0EqE1wYzQa8rsJBrq38nGuYEgO3rffe1JbkPmQrKylz 70IqCNrapvnBCAc/1xRMEjGicpUreDc1LnONlGrfEUcH3zyYGNODdJO39CeGhLFPHVBQwtW7pA6pP GUdxN0fiI8LjBPt45Iqyz0NZPvnD+q10ARhjQ7Uyn1MzQ5c8MSo3ow0ucuB9KB6iuUVK5f3trF7qA dxKLzwLo/C/E3GWlccOvfJ7Ej+eEY4R/3/Lm+vwlA+Npnpr4wu4Dw03m6ZE9B1jK+KgMSMAIi1jIQ /FO+11+g==; 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 1wmQmL-001kkc-0J for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:50:06 +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 1wmQmJ-0000000262W-2Yw2 for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:50:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19567: Redundant outer DISTINCT causes a second full aggregation above UNION 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: Wed, 22 Jul 2026 06:49:37 +0000 Message-ID: <19567-a99eb73efbfb32fe@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: 19567 Logged by: cl hl Email address: 2320415112@qq.com PostgreSQL version: 17.10 Operating system: Linux LAPTOP-2SQAVLB0 6.6.87.2-microsoft-standard- Description: =20 ## Description This issue concerns an outer `DISTINCT` applied to a non-`ALL` `UNION`. `UNION` already returns duplicate-free rows, so the outer operation cannot alter the result. PostgreSQL nevertheless executes a second full aggregation over the union output. ### Expected Behaviour PostgreSQL should propagate the uniqueness guarantee from `UNION` and remove the outer `DISTINCT`. Both SQL forms should require only the deduplication performed by `UNION` itself. ### Actual Behaviour The outer `DISTINCT` adds a second `HashAggregate`. With two 500,000-row inputs and 750,000 union rows, five-run median execution time increased from 236.234 ms to 420.470 ms. The redundant form is approximately 78.0% slower, or 1.78x the execution time. ## How to repeat ```sql DROP TABLE IF EXISTS union_distinct_lhs; DROP TABLE IF EXISTS union_distinct_rhs; CREATE TABLE union_distinct_lhs (v INTEGER NOT NULL); CREATE TABLE union_distinct_rhs (v INTEGER NOT NULL); INSERT INTO union_distinct_lhs SELECT g FROM generate_series(1, 500000) AS g; INSERT INTO union_distinct_rhs SELECT g FROM generate_series(250001, 750000) AS g; ANALYZE union_distinct_lhs; ANALYZE union_distinct_rhs; EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, TIMING OFF) SELECT DISTINCT v FROM ( SELECT v FROM union_distinct_lhs UNION SELECT v FROM union_distinct_rhs ) AS set_result; EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, TIMING OFF) SELECT v FROM ( SELECT v FROM union_distinct_lhs UNION SELECT v FROM union_distinct_rhs ) AS set_result; ``` Both queries return the same 750,000 rows. The characteristic plans are: ```text with DISTINCT: HashAggregate -> HashAggregate -> Append without: HashAggregate -> Append ```