Bug #120988 Redundant DISTINCT in UNION, INTERSECT and EXCEPT branches is not eliminated
Submitted: 23 Jul 12:31 Modified: 3 Aug 6:39
Reporter: cl hl Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S2 (Serious)
Version:8.0.46, 8.4.10, 9.7.1 OS:Any
Assigned to: CPU Architecture:Any

[23 Jul 12:31] cl hl
Description:
`UNION`, `INTERSECT`, and `EXCEPT` use set semantics, so duplicates inside an
individual input branch cannot affect the final result. Nevertheless, MySQL
retains `DISTINCT` in both branches and performs temporary-table deduplication
before the set operation performs its own deduplication.

### Expected behaviour

Branch-level `DISTINCT` should be removed or absorbed into the set operation.
Queries with and without the branch-level `DISTINCT` should have equivalent
plans and comparable execution times.

### Actual behaviour

The plan contains a `Temporary table with deduplication` for each branch in
addition to `Union/Intersect/Except materialize with deduplication`.

Five alternating executions after warm-up produced:

| Operation | Without branch DISTINCT | With branch DISTINCT | Slowdown |
|---|---:|---:|---:|
| UNION | 54.44 ms | 86.00 ms | 1.58x |
| INTERSECT | 73.36 ms | 102.97 ms | 1.40x |
| EXCEPT | 74.64 ms | 103.12 ms | 1.38x |

How to repeat:
DROP DATABASE IF EXISTS mysql_set_branch_distinct;
CREATE DATABASE mysql_set_branch_distinct;
USE mysql_set_branch_distinct;

CREATE TABLE digits(d INT PRIMARY KEY);
INSERT INTO digits VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
CREATE TABLE lhs(id INT, v INT);
CREATE TABLE rhs(id INT, v INT);

INSERT INTO lhs
SELECT n, MOD(n,1000) FROM (
  SELECT a.d+b.d*10+c.d*100+d.d*1000+e.d*10000+1 AS n
  FROM digits a,digits b,digits c,digits d,digits e
) numbers;

INSERT INTO rhs
SELECT n, MOD(n,1000) FROM (
  SELECT a.d+b.d*10+c.d*100+d.d*1000+e.d*10000+50001 AS n
  FROM digits a,digits b,digits c,digits d,digits e
) numbers;

ANALYZE TABLE lhs,rhs;

EXPLAIN FORMAT=TREE SELECT COUNT(*) FROM
 ((SELECT DISTINCT id FROM lhs) UNION (SELECT DISTINCT id FROM rhs)) s;
EXPLAIN FORMAT=TREE SELECT COUNT(*) FROM
 ((SELECT DISTINCT id FROM lhs) INTERSECT (SELECT DISTINCT id FROM rhs)) s;
EXPLAIN FORMAT=TREE SELECT COUNT(*) FROM
 ((SELECT DISTINCT id FROM lhs) EXCEPT (SELECT DISTINCT id FROM rhs)) s;

-- Compare each statement above with the corresponding form below.
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;
```
[3 Aug 6:39] Chaithra Marsur Gopala Reddy
Hi cl hl, 

Thank you for the test case. Verified as described.