SQL Join 速查表
可搜尋、可列印的 SQL join 參考:inner、left、right、full、反向與半 join、自我 join、ON 與 WHERE 及 LATERAL。免費。
Basic join types
9SELECT * FROM a INNER JOIN b ON a.id = b.a_id;
Only rows with a match in BOTH tables
SELECT * FROM a LEFT JOIN b ON a.id = b.a_id;
All left rows, NULLs where no match
SELECT * FROM a RIGHT JOIN b ON a.id = b.a_id;
All right rows, NULLs where no match
SELECT * FROM a FULL OUTER JOIN b ON a.id = b.a_id;
All rows from both, NULLs if unmatched
JOIN = INNER JOIN
Plain JOIN defaults to INNER
LEFT OUTER JOIN = LEFT JOIN
The OUTER keyword is optional
LEFT JOIN ... UNION ... RIGHT JOIN ...
Emulate FULL JOIN in MySQL (none native)
ON a.id = b.a_id AND a.type = b.type
Compound (multi-column) join condition
ON a.qty BETWEEN p.min_qty AND p.max_qty
Non-equi join: match on a range
Anti-joins (no match)
8LEFT JOIN b ON a.id = b.a_id WHERE b.a_id IS NULL
Left rows with NO match on the right
WHERE NOT EXISTS (SELECT 1 FROM b WHERE b.a_id = a.id)
Anti-join; safe with NULLs
WHERE a.id NOT IN (SELECT a_id FROM b)
One NULL in the list = empty result!
WHERE a.id NOT IN (SELECT a_id FROM b WHERE a_id IS NOT NULL)
NOT IN made NULL-safe
RIGHT JOIN a ... WHERE a.id IS NULL
Right rows with no match on the left
SELECT id FROM a EXCEPT SELECT a_id FROM b;
Set difference (Postgres, MySQL 8.0.31+)
users without orders = LEFT JOIN + IS NULL
The classic anti-join use case
Prefer NOT EXISTS over NOT IN
Same plan, none of the NULL traps
Semi-joins (has a match)
8WHERE EXISTS (SELECT 1 FROM b WHERE b.a_id = a.id)
Left rows having a match; never duplicates
WHERE a.id IN (SELECT a_id FROM b)
Same intent; fine for short value lists
JOIN b ... GROUP BY a.id
JOIN duplicates 1-to-many; dedupe needed
SELECT DISTINCT a.* FROM a JOIN b ON ...
Semi-join via JOIN; EXISTS is cleaner
EXISTS stops at the first match
Often faster than joining everything
EXISTS (SELECT 1 ...) vs (SELECT * ...)
Selected columns are ignored; 1 is idiomatic
IN: uncorrelated / EXISTS: correlated
Optimizers usually rewrite them the same
users WITH orders = EXISTS subquery
The classic semi-join use case
Self joins
8FROM emp e JOIN emp m ON e.manager_id = m.id
Each employee paired with their manager
FROM emp e LEFT JOIN emp m ON e.manager_id = m.id
Keeps top-level rows (manager is NULL)
SELECT e.name, m.name AS manager FROM ...
Aliases are REQUIRED to tell copies apart
ON a.id < b.id
Unique pairs: no self-match, no mirrors
ON a.id <> b.id
All ordered pairs except self-matches
ON b.day = a.day + INTERVAL 1 DAY
Adjacent rows (gaps and islands)
ON a.city = b.city AND a.id <> b.id
Other rows sharing the same value
WITH RECURSIVE tree AS (... UNION ALL ...)
Whole hierarchy: recursive CTE, not a join
Cross & multi-table joins
8SELECT * FROM sizes CROSS JOIN colors;
Every combination: m x n rows, no ON
FROM sizes, colors
Implicit cross join; avoid this syntax
FROM users u JOIN orders o ON ... JOIN items i ON ...
Chain joins; each needs its own ON
JOIN dates d CROSS JOIN users u LEFT JOIN ...
Calendar x users grid, then fill data
u LEFT JOIN o ON ... LEFT JOIN i ON i.o_id = o.id
Keep LEFT down the chain to keep all users
u LEFT JOIN o ... INNER JOIN i ON i.o_id = o.id
INNER after LEFT drops the NULL rows!
FROM a JOIN (b LEFT JOIN c ON ...) ON ...
Parentheses control join grouping
Written order is logical, not execution order
The optimizer reorders inner joins freely
ON vs WHERE (the gotcha)
8LEFT JOIN b ON a.id = b.a_id AND b.status = 1
Filters RIGHT side; all left rows kept
LEFT JOIN b ON a.id = b.a_id WHERE b.status = 1
NULL fails the test: LEFT becomes INNER!
WHERE b.a_id IS NULL OR b.status = 1
Keep unmatched rows despite a WHERE filter
LEFT JOIN b ON ... AND a.type = 'x'
Left-side filter in ON does NOT drop rows
WHERE a.type = 'x'
Filter the LEFT table in WHERE instead
INNER JOIN: ON and WHERE are equivalent
The trap only exists for outer joins
COALESCE(b.total, 0)
Replace outer-join NULLs with a default
COUNT(b.id) vs COUNT(*)
COUNT(col) skips NULLs: 0 for no match
USING, NATURAL & set ops
9JOIN b USING (user_id)
Equi-join on a same-named column
USING outputs ONE shared column
ON keeps both a.user_id and b.user_id
NATURAL JOIN b
Auto-joins ALL same-named columns; fragile
Avoid NATURAL JOIN in production
A new column silently changes the join
SELECT ... UNION SELECT ...
Stack rows vertically, duplicates removed
UNION ALL
Keep duplicates; much faster than UNION
JOIN adds columns, UNION adds rows
Widen vs lengthen the result set
INTERSECT
Rows present in both result sets
UNION needs same column count and types
Names come from the first SELECT
LATERAL & performance
9CROSS JOIN LATERAL (SELECT ... WHERE b.a_id = a.id LIMIT 3) t
Per-row subquery: top-N per group
LEFT JOIN LATERAL (...) t ON TRUE
Keep left rows whose subquery is empty
CROSS APPLY / OUTER APPLY
SQL Server spelling of LATERAL joins
LATERAL sees columns of preceding tables
A plain subquery in FROM cannot
CREATE INDEX idx ON orders (user_id);
Index the FK side of every frequent join
EXPLAIN SELECT ...;
Check join order, type, and index usage
Filter early: join the smallest set first
Less rows carried through each join
ON DATE(a.created_at) = b.day
Function on a column kills index use
Join on same-type, same-collation columns
Implicit casts also disable indexes
沒有條目符合「:q」。
關於 SQL Join 速查表
這份 SQL join 速查表只做一件事,並且做得透徹:說明所有合併資料表的方式。八個段落涵蓋基本 join 類型、找出沒有對應資料列的反向 join、找出確實有對應資料列的半 join、自我 join、交叉與多資料表 join、經典的 ON 與 WHERE 陷阱、USING、NATURAL 與集合運算,以及附有效能說明的 LATERAL join。
SQL 結果最容易悄悄出錯的地方就在 join:在 WHERE 子句中篩選的 LEFT JOIN 會默默變成 INNER JOIN,而含有 NULL 的 NOT IN 則什麼都不會回傳。這些陷阱在這裡各有獨立的列,旁邊附上應該改用的正確寫法。
與本群組的每份速查表一樣,它免費且在用戶端執行:用搜尋框即時篩選列、用固定的目錄在各段落間跳轉、一鍵複製任何陳述式,並可列印頁面作為桌邊參考。
如何使用 SQL Join 速查表
- 開啟速查表並檢視各段落,從「基本 join 類型」到「LATERAL 與效能」。
- 搜尋 LEFT JOIN、EXISTS 或 NOT IN 等關鍵字,即時篩選每一列。
- 在除錯回傳資料列過少的 LEFT JOIN 之前,先閱讀「ON 與 WHERE」段落。
- 點選陳述式或其複製圖示,即可將 SQL 複製到剪貼簿。
- 使用列印取得完整 join 參考的紙本。
常見問題
inner、left、right 與 full outer join,加上反向 join、半 join、自我 join、交叉 join、多資料表 join、USING 與 NATURAL join、UNION 系列集合運算,以及 LATERAL join。
ON 中的條件會在外部 join 保留未匹配資料列之前,先篩選被 join 的資料表;相同的條件放在 WHERE 則在之後才執行,並丟棄 NULL 資料列,使 LEFT JOIN 變成 INNER JOIN。速查表中有專門討論這一點的段落。
反向 join 段落展示了 LEFT JOIN … WHERE right.id IS NULL 與 NOT EXISTS 兩種寫法,並說明子查詢可能回傳 NULL 時 NOT IN 為什麼會出問題。
適用。語法是標準 SQL,可在 MySQL、PostgreSQL、SQL Server 與 SQLite 中使用,少數特定資料庫引擎的注意事項會直接標註在該列。
是的,完全免費,在瀏覽器中執行,無需登入。
熱門搜尋
sql join 速查表
sql join 類型
inner join 與 left join 差異
sql anti join
sql full outer join
self join 範例
join 中 on 與 where 差異
需要協助?
使用此工具時遇到問題?請告訴我們的團隊。