笛卡儿积就是「每一行跟对方每一行都配一次」:12 个学生 × 3 个班 = 36 行。
JOIN 后面不写 ON,等价于 CROSS JOIN,都是笛卡儿积。
所以「少写一个 on」不报错,只让结果从 12 行变成 36 行。
行数按乘法涨:两张 1000 行的表是 100 万行,三张就是 10 亿行。
数据库不拦你,只会慢慢跑。
前两篇都是「按条件配对」,这一篇讲「不配对」——听起来像错误,其实是个正经的运算。 它 99% 的出场是事故(漏写条件),剩下 1% 是「我就要全集」(算组合、造日历)。 这一篇一半讲怎么防它,一半讲什么时候该用。
一、笛卡儿积是什么:m × n 行
FROM students, classes 不写条件,给 36 行。
12 × 3,每一行学生跟每一行班级都配了一次,不管有没有关系。
看前六行就明白了:张伟配一班、二班、三班,然后王芳配一班、二班、三班。
这叫「笛卡儿积」,也叫「交叉连接」。
它的数学定义就是「所有可能的组合」,
所以行数一定是两张表行数的乘积——这一点可以用来快速判断「是不是连错了」。
二、漏写配对条件,就是笛卡儿积
JOIN 后面不写 ON,等价于 CROSS JOIN,都是 36 行。
所以「少写一个 on」不会报错,只会让结果从 12 行变成 36 行。
这是多表查询最常见的事故:
报表数字突然大了三倍,代码看着没问题——就是少了个 on。
而且它特别难发现:数字变大不像报错那样刺眼,往往要等业务方来问。
排查办法很简单:
先 count(*) 一下,行数对不上就是连错了。 更稳的办法是「连完再 count 一次主表」,
两次数字必须一样——不一样就说明配对丢了或者炸了。
三、行数按乘法涨,很容易炸
两张 1000 行的表做笛卡儿积是 100 万行;三张就是 10 亿行。
数据库不会拦你,它只会慢慢跑,跑到磁盘满或者超时。
最危险的不是「表大」,是「表不大但连了四张」:
每张 100 行,四张就是 1 亿行。 四张各 100 行的表看着都小,组合起来能压死服务器。
所以「多表查询前先数行数」是个好习惯:
12 × 3 还能算,1000 × 1000 × 1000 就该先想清楚条件了。
写完 SQL 先 EXPLAIN 看一眼也行,第 12 章会讲。
四、少数情况下,笛卡儿积真的有用
一是算组合:「所有班级 × 所有科目」做一张完整的成绩表骨架, 再用左连接把实际成绩填进去,没考的留 NULL。 这种「先造全集、再补数据」的手法在报表里很常见。 二是造日历:「所有日期 × 所有门店」,再左连接实际销量, 就能得到「某天某店销量为 0」的行——不用它的话,没销量的那天根本不会出现在结果里。 这两种用法的共同点是「我就要全集」,而不是「我忘了写条件」。 写的时候加一句注释说明意图,以后没人会以为是 bug。 这一句注释能省掉未来某个人的半小时。
五、写笛卡儿积的两种姿势
CROSS JOIN 显式写出来,意图最清楚;
逗号写法 FROM a, b 更短,但和「忘了写 where」长得一模一样,容易误读。
所以真要笛卡儿积,就用 CROSS JOIN。
这样任何人扫一眼就知道「这是故意的」,不用去数 where 里少了什么。
六、动手 Demo:36 行是怎么来的,以及少写 on 的后果
Demo 四段:先 count 一下笛卡儿积的行数、看前六行长什么样;
再证明「JOIN 不写 ON」和「CROSS JOIN」都是 36 行;然后算行数怎么涨,最后讲它什么时候真有用。
第三段那个「36 行」是这一篇的核心数字——记住它,以后看到行数变成乘积就知道是漏了条件。
import _q46 as Q
c = Q.con()
# 笛卡儿积:12 x 3 = 36 行
Q.show(c, "SELECT COUNT(*) AS n FROM students, classes")
Q.show(c, "SELECT s.name AS 学生, c.name AS 班级 "
"FROM students s, classes c LIMIT 6")
# 漏写 on 就是笛卡儿积;cross join 也是
Q.show(c, "SELECT COUNT(*) AS n FROM students JOIN classes")
Q.show(c, "SELECT COUNT(*) AS n FROM students CROSS JOIN classes")
实跑输出:
--------------------------------------------------------------------
第五十六课:笛卡儿积——不配对,直接全配
--------------------------------------------------------------------
笛卡儿积是什么:m × n 行
SQL> SELECT COUNT(*) AS n FROM students, classes
n
---
36
(1 行)
**12 × 3 = 36 行。** **每一行学生跟每一行班级都配了一次,不管有没有关系。**
SQL> SELECT s.name AS 学生, c.name AS 班级 FROM students s, classes c LIMIT 6
学生 班级
--------------
张伟 一班
张伟 二班
张伟 三班
王芳 一班
王芳 二班
王芳 三班
(6 行)
**前六行都是张伟**,配了一班、二班、三班,然后又来王芳配三个班。
**这叫「笛卡儿积」,也叫「交叉连接」。**
漏写配对条件,就是笛卡儿积
SQL> SELECT COUNT(*) AS n FROM students JOIN classes
n
---
36
(1 行)
SQL> SELECT COUNT(*) AS n FROM students CROSS JOIN classes
n
---
36
(1 行)
**`JOIN` 后面不写 `ON`,等价于 `CROSS JOIN`,都是 36 行。**
**所以「少写一个 `on`」不会报错,只会让结果从 12 行变成 36 行。**
**这是多表查询最常见的事故**:
**报表数字突然大了三倍,代码看着没问题——就是少了个 `on`。**
**排查办法很简单:先 `count(*)` 一下,行数对不上就是连错了。**
行数按乘法涨,很容易炸
**两张 1000 行的表做笛卡儿积是 100 万行;三张就是 10 亿行。**
**数据库不会拦你,它只会慢慢跑,跑到磁盘满或者超时。**
**所以「多表查询前先数行数」是个好习惯**:
**12 × 3 还能算,1000 × 1000 × 1000 就该先想清楚条件了。**
少数情况下,笛卡儿积真的有用
**一是算组合**:**「所有班级 × 所有科目」做一张完整的成绩表骨架**,
**再用左连接把实际成绩填进去,没考的留 NULL。**
**这种「先造全集、再补数据」的手法在报表里很常见。**
**二是造日历**:**「所有日期 × 所有门店」,再左连接实际销量**,
**就能得到「某天某店销量为 0」的行**——**不用它的话,没销量的那天根本不会出现在结果里。**
**这两种用法的共同点是「我就要全集」**,
**而不是「我忘了写条件」。** **写的时候加一句注释说明意图,以后没人会以为是 bug。**
写笛卡儿积的两种姿势
**`CROSS JOIN` 显式写出来**,意图最清楚;
**逗号写法 `FROM a, b`** 更短,但和「忘了写 where」长得一样,**容易误读。**
**所以真要笛卡儿积,就用 `CROSS JOIN`。**
**这样任何人扫一眼就知道「这是故意的」,不用去数 where 里少了什么。**
第一段 36 行:12 × 3。
第二段前六行:张伟配三个班、王芳配三个班。
第三段和第四段都是 36 行:JOIN 不写 ON 与 CROSS JOIN 完全等价。
后三段没有 SQL,讲行数怎么炸、什么时候真该用笛卡儿积,以及为什么用 CROSS JOIN 而不用逗号。
30 秒实验
SELECT COUNT(*) FROM students JOIN classes给 36 而不是 12,最可能的原因是什么?- 两张 500 行的表做笛卡儿积是多少行?三张呢?
- 「找出所有班级 × 所有科目、没考的留 NULL」,为什么要先笛卡儿积再左连接?
常见误区
JOIN忘了写ON。 不报错,行数变成乘积,报表数字凭空变大,很难第一时间发现。- 以为表小就没事。 四张各 100 行的表连起来是 1 亿行,小表也能把服务器压垮。
- 真要笛卡儿积却用逗号写法。 和「忘了写
where」长得一样,以后维护的人会当成 bug 去「修」。
自测三问(点开对答案)
1. 两表笛卡儿积的行数怎么算? —— 两张表行数的乘积。12 个学生 × 3 个班 = 36 行。 2. `JOIN` 不写 `ON` 会怎样? —— 不报错,等价于 `CROSS JOIN`,结果是两张表的笛卡儿积。 3. 什么场景下笛卡儿积是对的? —— 需要「全集」的时候:算所有组合、造日期乘门店的日历骨架,再接左连接补数据。最后一种连法最绕:10.6 自连接——一张表跟自己连。
- ☐ (本篇完,下一篇 10.6 自连接)