← scratchpad

Why my LEFT JOIN duplicated rows

I needed to filter machines by the GPU cards they have. Machines are in one table, GPUs in another, one row per card.

SELECT node.* FROM node
JOIN node_gpu ON node_gpu.node_twin_id = node.twin_id
WHERE node_gpu.device = '...';

I first wrote this as a LEFT JOIN, but a WHERE on the right table throws away the unmatched rows anyway, so it behaves like an inner join. Plain JOIN says what it does.

It worked until a machine had two cards. Then it came back twice, because a one-to-many join multiplies the “one” side.

DISTINCT on node.* looks fine since I never return GPU columns. It fails if node has a json column (not jsonb), because json has no equality operator.

So I tried DISTINCT ON (node.twin_id) and got this:

ERROR: SELECT DISTINCT ON expressions must match initial ORDER BY expressions

The response is ordered so the caller’s rented machines come first:

ORDER BY (rented_by = $1) DESC NULLS LAST, node.node_id

DISTINCT ON wants its column to lead the ORDER BY, and that order had to stay first.

A subquery with plain SELECT DISTINCT node_twin_id works, since the duplicates never reach the outer query. EXISTS is cleaner:

SELECT node.* FROM node
WHERE EXISTS (
  SELECT 1 FROM node_gpu
  WHERE node_gpu.node_twin_id = node.twin_id
    AND node_gpu.device = '...'
)
ORDER BY (rented_by = $1) DESC NULLS LAST, node.node_id;

It’s a semi-join, so duplicates can’t happen and the ORDER BY stays free.

TIL: when a join only filters, use EXISTS.