SELF JOIN本质是同一张表自连接并需用AS起不同别名,否则报错;典型用途是查邮箱重复的原始记录对,须用ON t1.id != t2.id避免自匹配,并显式处理NULL;不可替代去重或删除操作。

SELF JOIN 本质是给同一张表起两个别名
SELF JOIN 不是特殊语法,就是普通 JOIN,但左右两边都是同一张表,必须用 AS 起不同别名,否则数据库无法区分字段来源。不加别名会报错,比如 ERROR: table name "users" specified more than once。
典型场景:查出邮箱重复的用户记录,且要显示所有重复行(不只是去重后的邮箱值)。
- 左表别名通常代表“基准行”,右表别名代表“对比行”
- JOIN 条件里必须包含
ON t1.id != t2.id,否则每条记录会和自己匹配,结果翻倍且无意义 - 业务条件(如
t1.email = t2.email)写在ON里,不是WHERE——避免因 NULL 导致漏匹配(多数数据库中NULL = NULL为 false)
检测重复时 WHERE 条件要避开 NULL 和自匹配
如果字段允许 NULL,直接 t1.email = t2.email 会跳过所有含 NULL 的行,因为 SQL 中 NULL 不等于任何值(包括另一个 NULL)。需要显式处理。
- 安全写法:
ON t1.email = t2.email OR (t1.email IS NULL AND t2.email IS NULL) - 必须加上
t1.id (或 <code>!=)防止 A-B 和 B-A 重复出现;用还能天然去重,结果更干净 - 如果主键不是
id,换成实际唯一标识字段,比如t1.user_id != t2.user_id
示例(查重复邮箱):
SELECT t1.id, t1.email, t2.id, t2.email FROM users AS t1 JOIN users AS t2 ON t1.email = t2.email AND t1.id < t2.id;
性能差?别直接在大表上跑 SELF JOIN
SELF JOIN 是 O(n²) 复杂度,10 万行表可能产生百亿级笛卡尔积中间结果,即使加了 id 剪枝,执行仍可能卡死或超时。
- 先用
GROUP BY + HAVING COUNT(*) > 1快速定位哪些值重复,再按需查详情 - 确保被 JOIN 的字段(如
email)有索引,否则全表扫描两次 - 某些场景下,用窗口函数更高效:
SELECT *, COUNT(*) OVER (PARTITION BY email) AS cnt FROM users,再过滤cnt > 1
别把 SELF JOIN 当去重工具用
SELF JOIN 适合“找出重复的原始记录对”,不是用来 DISTINCT 或 DELETE 的。想删重复行,直接 ROW_NUMBER() 更稳:
DELETE FROM users
WHERE id IN (
SELECT id FROM (
SELECT id, ROW_NUMBER() OVER (PARTITION BY email ORDER BY id) AS rn
FROM users
) t
WHERE rn > 1
);
SELF JOIN 的结果里,同一组重复数据可能出现多次组合(比如 3 条相同邮箱 → 输出 3 行:A-B、A-C、B-C),而实际排查时往往只关心“哪些 ID 属于重复组”,这点容易被忽略。

















