What does NATURAL JOIN do automatically, and why is it risky to use in production?
NATURAL JOIN matches rows on every column the two tables have in common, with no ON clause at all. It finds the shared column names, compares those columns for equality, and joins on all of them.
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name VARCHAR(120) NOT NULL,
region_id INTEGER NOT NULL
);
CREATE TABLE regions (
id INTEGER PRIMARY KEY,
label VARCHAR(60) NOT NULL
);
INSERT INTO customers (id, name, region_id) VALUES
(1, 'Ada Lovelace', 1),
(2, 'Grace Hopper', 2);
INSERT INTO regions (id, label) VALUES
(1, 'EMEA'),
(2, 'Americas');
-- The shared column is id, so this joins on customers.id = regions.id.
SELECT * FROM customers NATURAL JOIN regions ORDER BY id;Two rows come back, each with the id, name, region_id and label columns. The join found the shared column id and matched on it, with no join condition written at all.
The risk is that the shared column set is not under your control. The moment somebody adds a column called status to both tables, NATURAL JOIN silently starts requiring equality on status too — and rows that used to match stop matching, with no error and no change to the query text. The join gets stricter every time the schema evolves, which is the opposite of what a reader expects.
The second risk is ambiguity. If the tables share two columns, NATURAL JOIN requires both to be equal. If they share a column that happens to have the same name but a different meaning — an id that is a customer id on one side and a region id on the other, but both called id — the join compares the wrong things and quietly returns nonsense.
The third risk is that the output column names become ambiguous. With NATURAL JOIN, the joined columns appear once in the result rather than twice, and you cannot tell from the output which table each remaining column came from. Every query built on top of that view then needs an alias.
The rule is: use NATURAL JOIN in a quick exploration, never in application SQL. An explicit ON clause says what the join means and fails loudly when the schema changes.