mysql中cross join于普通join的区别
今天在写力扣的时候发现一道题,需要笛卡尔积做多表查询,发现cross join和普通join之间其实是一个东西,可以随意转换。
题目
题目背景(简化版)
假设我们有两张表:
- 学生表(Students):存着学生的编号和姓名,比如
(1, '张三'), (2, '李四')
。 - 考试表(Exams):存着考试的编号和科目,比如
(101, '数学'), (102, '英语'), (103, '物理')
。
先说Cross Join:暴力配对所有可能
核心逻辑:把学生表和考试表的每一行都强行“配对”,生成所有可能的组合,就像做排列组合题。
比如:
- 学生表有2个学生,考试表有3个考试,
CROSS JOIN
后会得到 2×3=6条记录,每条记录都是“某个学生+某个考试”的组合,不管这个学生有没有参加过这个考试。
SQL示例(对应题目可能的需求:列出所有学生可能参加的考试组合):
SELECT s.student_name, e.subject_name
FROM Students s CROSS JOIN Exams e;
结果大概长这样:
student_name | subject_name |
---|---|
张三 | 数学 |
张三 | 英语 |
张三 | 物理 |
李四 | 数学 |
李四 | 英语 |
李四 | 物理 |
口语化理解:就像老师让全班同学和所有科目“组队”,不管你有没有选这门课,先把所有可能的“人+科目”组合列出来,纯凑数!
再聊普通Join(比如Inner Join):只留符合条件的配对
核心逻辑:必须按条件筛选,只保留两表中“有关系”的记录。比如,只有当学生真正参加了某场考试时,才会被加入结果。
假设我们有第三张表:学生考试记录表(StudentExams),记录谁参加了哪场考试,比如 (1, 101), (1, 102), (2, 101)
(张三考了数学和英语,李四考了数学)。
SQL示例(查“参加过考试的学生和科目”):
SELECT s.student_name, e.subject_name
FROM Students s
INNER JOIN StudentExams se ON s.student_id = se.student_id
INNER JOIN Exams e ON se.exam_id = e.exam_id;
结果大概长这样:
student_name | subject_name |
---|---|
张三 | 数学 |
张三 | 英语 |
李四 | 数学 |
口语化理解:现在老师只让“真正参加了考试的人+科目”组队,没参加的不显示。比如李四没考英语和物理,所以这两个组合就被筛掉了。
两者的灵魂区别对比
场景 | Cross Join | 普通Join(如Inner Join) |
---|---|---|
目的 | 生成所有可能的组合(不管有没有关系) | 只取“有关系”的组合(按条件筛选) |
结果数量 | 学生数 × 考试数(比如2×3=6) | 取决于实际参加考试的记录数(可能更少) |
类比 | 全班同学和所有科目强行组队,先列全再筛选 | 只让“报了名的同学+科目”组队,精准匹配 |
在本题中的应用 | 题目可能需要统计“每个学生对所有科目的考试情况”,即使没参加也显示(用Cross Join) | 题目可能需要统计“实际参加过的考试”(用Inner Join) |
为什么这题可能用到Cross Join?
假设题目要求:列出每个学生对所有科目的考试次数,没参加过的科目次数记为0。这时候就需要先用Cross Join
生成“所有学生+所有科目”的组合,再通过分组统计次数,没参加过的自然就是0啦~
比如:
- 用
Cross Join
得到所有组合(张三+数学,张三+英语,张三+物理,李四+数学,李四+英语,李四+物理)。 - 再左连接考试记录表,没参加过的考试记录为
NULL
,统计时NULL
会被计为0。
在SQL中,JOIN
(连接)和CROSS JOIN
(交叉连接)都是用于组合多张表数据的操作,但它们的逻辑和结果有显著区别:
1. CROSS JOIN(交叉连接)
- 核心逻辑:生成两张表的笛卡尔积(Cartesian product),即左表的每一行与右表的每一行都强制组合成一条新记录。
- 特点:
- 不需要任何连接条件(
ON
或WHERE
),如果添加条件会改变其性质。 - 结果集行数 = 左表行数 × 右表行数,数据量可能非常大(需谨慎使用)。
- 不需要任何连接条件(
- 语法:
-- 显式写法 SELECT * FROM 表A CROSS JOIN 表B;-- 隐式写法(等价,用逗号分隔表) SELECT * FROM 表A, 表B;
- 示例:
表A(2行)和表B(3行)交叉连接后产生 2×3=6 行,包含所有可能的组合。
2. JOIN(普通连接,如INNER JOIN、LEFT JOIN等)
- 核心逻辑:根据指定的连接条件(
ON
子句)筛选出符合条件的记录组合,只保留匹配的行。 - 常见类型:
INNER JOIN
(内连接):只保留两表中完全匹配条件的记录。LEFT JOIN
(左连接):保留左表所有记录,右表匹配不到则补NULL
。RIGHT JOIN
(右连接):保留右表所有记录,左表匹配不到则补NULL
。
- 特点:
- 必须有连接条件(
ON
子句),否则会退化为CROSS JOIN
。 - 结果集行数由匹配条件决定,通常远小于笛卡尔积。
- 必须有连接条件(
- 语法:
-- 内连接示例(只保留匹配的行) SELECT * FROM 表A INNER JOIN 表B ON 表A.id = 表B.a_id;-- 左连接示例(保留左表所有行) SELECT * FROM 表A LEFT JOIN 表B ON 表A.id = 表B.a_id;
关键区别总结
维度 | CROSS JOIN | 普通JOIN(如INNER JOIN) |
---|---|---|
连接条件 | 不需要,强制全组合 | 必须有ON 条件,筛选匹配记录 |
结果集性质 | 笛卡尔积(所有可能组合) | 符合条件的匹配记录 |
行数 | 左表行数 × 右表行数 | 取决于匹配条件,通常较少 |
适用场景 | 需全量组合的特殊场景(如生成测试数据) | 需关联两表字段的常规查询 |
补充说明
- 普通
JOIN
如果省略ON
条件,会自动变成CROSS JOIN
(不推荐,易出错)。 CROSS JOIN
加WHERE
条件后,逻辑上等价于INNER JOIN
(但建议直接用INNER JOIN
更清晰)。
例如,以下两种写法结果相同,但后者更规范:
-- 不推荐:CROSS JOIN + WHERE 等价于 INNER JOIN
SELECT * FROM 表A CROSS JOIN 表B WHERE 表A.id = 表B.a_id;-- 推荐:显式内连接
SELECT * FROM 表A INNER JOIN 表B ON 表A.id = 表B.a_id;