← Concepts

阅读中文版本

Anti-Join Optimization

An anti-join returns rows from the left input that have no match in the right, and optimizing it means avoiding the naive nested-loop that scans the right side once per left row.

database

Definition

An anti-join is the relational operation that returns left rows for which no matching right row exists, and its optimization replaces the O(N*M) nested loop with a hash or merge anti-join that builds a probe structure on the right side once, achieving O(N+M) for the common case.

Context and scope

Applied in NOT EXISTS queries, exception reports, and any "find what is missing" query where the naive plan scans the right table per left row.

Common misconceptions

  • NOT IN and NOT EXISTS are the same — NOT IN has NULL semantics that can produce empty results where NOT EXISTS would return rows; they are not interchangeable
  • An anti-join is always faster with an index on the right key — a hash anti-join can beat an indexed nested loop when the right table fits in memory

Anti-patterns

  • Writing NOT IN with a nullable right column and getting an empty result set from the three-valued logic
  • Forcing a nested-loop anti-join on a large right table, producing an O(N*M) plan the optimizer would have avoided

Related concepts

Authoritative source

https://www.postgresql.org/docs/current/explicit-joins.html

What it is

An anti-join is the relational operation that returns left rows for which no matching right row exists, and its optimization replaces the O(N*M) nested loop with a hash or merge anti-join that builds a probe structure on the right side once, achieving O(N+M) for the common case. Applied in NOT EXISTS queries, exception reports, and any “find what is missing” query where the naive plan scans the right table per left row. The data model and the access patterns are co-designed: a schema that is correct in the relational sense can be catastrophic for the queries the workload actually issues.

How it works

Indexing decisions follow the query shapes, not the column importance: a covering index turns a table scan into an index-only scan, but every index adds write amplification and storage cost. Partitioning aligns with the access dimension so a query touches one partition, not all. Transaction scope is minimized to hold locks briefly; long transactions are the root of contention and deadlock.

When to apply and pitfalls

Apply when data volume or query latency has a concrete business cost. The common pitfall is indexing for the current workload and ignoring the access patterns that growth will introduce — the index that helps today can serialize writes tomorrow. Another is treating the database as a black box, missing that the optimizer’s plan choice depends on statistics that drift as data changes.