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-000o7h-2B 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-00BUZ6-1t 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 1wmQsC-00Anzi-2S for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:56:08 +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 1wmQsA-00000000Z6q-1OI4 for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:56:08 +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=u7T5so2T5IhV0tCTwouXG9cFwacJz3lolB7GzQeTbt0=; b=akEjmQ/mQq1ObXGZKOONjGRKCt dtS0DfLrGymiM1n/4P954HDJym1xWKVa8PkVKGWWRhLQsZQMcnbEqCQ50TT1MHvvLr5FwsgPFlIxu G2pnTPFHVjKldh1ODenYL135RRLb5ODW3yrMl19IeKqgBgfn16+FddRnI3C50NtKTOn3iUWO6HCvf wwNJpVgn5G/uQ58rG6E9eTn/syaJ+4kEgKtbJOCkvp00HztXhEkZfVB8ohYV/Xs6wicTQT1hocRZd r89QqeBQcuxZLCp3IyvB9p3IcbDulgwPIjHJ2a4i7bT1dPX9HrxiMn3ZX43ceD/Cnq38qyoBHv2ko JJnjTuYQ==; 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 1wmQs8-001ksH-2e for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:56:04 +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 1wmQs7-000000026H3-24jZ for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:56:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19569: Redundant outer DISTINCT adds Sort and Unique above EXCEPT 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:55:39 +0000 Message-ID: <19569-2b3b7fb3edfbcba8@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: 19569 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 an `EXCEPT` result. Non-`ALL` `EXCEPT` already removes duplicates, so applying `DISTINCT` again is semantically redundant. PostgreSQL retains the additional operation. ### Expected Behaviour PostgreSQL should use the duplicate-free property of `EXCEPT` and eliminate the outer `DISTINCT`. The original and simplified forms should receive equivalent plans. ### Actual Behaviour The outer `DISTINCT` adds `Sort -> Unique` above `HashSetOp Except`. With two 500,000-row inputs and 250,000 result rows, five-run medians were 184.393 ms with the redundant operation and 181.401 ms without it, approximately 1.6% slower. This timing difference is small enough to overlap runtime variation, but the extra plan operators are deterministic. ## How to repeat ```sql DROP TABLE IF EXISTS except_distinct_lhs; DROP TABLE IF EXISTS except_distinct_rhs; CREATE TABLE except_distinct_lhs (v INTEGER NOT NULL); CREATE TABLE except_distinct_rhs (v INTEGER NOT NULL); INSERT INTO except_distinct_lhs SELECT g FROM generate_series(1, 500000) AS g; INSERT INTO except_distinct_rhs SELECT g FROM generate_series(250001, 750000) AS g; ANALYZE except_distinct_lhs; ANALYZE except_distinct_rhs; EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, TIMING OFF) SELECT DISTINCT v FROM ( SELECT v FROM except_distinct_lhs EXCEPT SELECT v FROM except_distinct_rhs ) AS set_result; EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, TIMING OFF) SELECT v FROM ( SELECT v FROM except_distinct_lhs EXCEPT SELECT v FROM except_distinct_rhs ) AS set_result; ``` Both queries return the same 250,000 rows. The characteristic plans are: ```text with DISTINCT: Unique -> Sort -> HashSetOp Except without: HashSetOp Except ```