Skip to content

SELECT DISTINCT returns duplicate rows when a unique key is downgraded by a join #23626

Description

@alamb

Describe the bug

SELECT DISTINCT can return duplicate rows when its input has a functional dependency whose determinant columns cover all output columns, but whose uniqueness was lost earlier in the plan.

To Reproduce

Run the following in datafusion-cli:

CREATE TABLE users_with_pk (id INT, name VARCHAR, PRIMARY KEY (id)) AS VALUES
  (1, 'alice'),
  (2, 'bob');

CREATE TABLE user_orders (user_id INT, amount INT) AS VALUES
  (1, 10),
  (1, 20),
  (2, 30);

SELECT DISTINCT u.id
FROM users_with_pk u
LEFT JOIN user_orders o ON u.id = o.user_id
ORDER BY u.id;

Actual output — id = 1 is repeated once per matching order:

+----+
| id |
+----+
| 1  |
| 1  |
| 2  |
+----+

EXPLAIN shows the DISTINCT has been dropped entirely — there is no Aggregate (or any other deduplication) in the plan:

logical_plan
01)Projection: u.id
02)--Left Join: u.id = o.user_id
03)----SubqueryAlias: u
04)------TableScan: users_with_pk projection=[id]
05)----SubqueryAlias: o
06)------TableScan: user_orders projection=[user_id]

Expected behavior

The query should return each u.id once:

+----+
| id |
+----+
| 1  |
| 2  |
+----+

and the plan should retain an Aggregate (or equivalent deduplication) for the DISTINCT.

Additional context

The faulty check is in datafusion/optimizer/src/replace_distinct_aggregate.rs (ReplaceDistinctWithAggregate::rewrite): it only verifies that dep.source_indices covers all fields, but a Dependency::Multi dependence merely means equal determinant keys imply equal rows — the key may still occur in multiple rows, so the input is not necessarily duplicate-free. The removal is only sound for Dependency::Single dependencies.

Fixed by #23548.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions