从一题了解 CROSS JOIN 与 DATEDIFF:SQL 天气温度对比实战
在 SQL 学习中,JOIN 子查询和日期函数是高频考点,也是实际业务中常用的组合技能。今天我们就通过一道经典的天气温度对比题,吃透 CROSS JOIN(交叉连接)和 DATEDIFF 函数的用法,同时理解“自连接”的核心逻辑。
一、题目背景与需求
题目描述
现有一张天气表 Weather,结构如下:
| 列名 | 类型 | 说明 |
|---|---|---|
| id | int | 唯一标识(主键) |
| recordDate | date | 记录日期 |
| Temperature | int | 当日温度 |
要求查询出 所有温度比前一天高的日期对应的 id。
示例输入输出
假设表中数据如下:
| id | recordDate | Temperature |
|---|---|---|
| 1 | 2023-01-01 | 10 |
| 2 | 2023-01-02 | 20 |
| 3 | 2023-01-03 | 15 |
| 4 | 2023-01-04 | 25 |
期望输出:
| id |
|---|
| 2 |
| 4 |
解释:2023-01-02(20℃)比前一天(10℃)高,2023-01-04(25℃)比前一天(15℃)高,因此返回对应的 id 2 和 4。
二、核心知识点拆解
要解决这道题,需要掌握两个关键技能:CROSS JOIN 自连接 和 DATEDIFF 日期差计算。
1. 先搞懂:为什么要用 CROSS JOIN 自连接?
我们的需求是“对比当日与前一天的温度”,本质是 将表中的每条记录与它的“前一天记录”进行配对。
- 普通查询只能获取单条记录的信息,无法直接关联两条不同日期的记录;
- 自连接(将表与自身连接)是实现“同表不同行对比”的核心方法;
CROSS JOIN是交叉连接,会返回两张表所有记录的笛卡尔积(即表 A 每条记录与表 B 每条记录配对)。但我们通过ON条件筛选后,就能精准得到“当日-前一天”的配对组合。
这里的关键逻辑:
- 把
Weather表看成两张表:A 表(代表“当日”)和 B 表(代表“前一天”); - 我们需要 A 表的日期 = B 表的日期 + 1 天,这样就实现了“当日与前一天”的配对。
2. 再吃透:DATEDIFF 函数的用法
DATEDIFF 是 SQL 中用于计算两个日期之间差值的函数,语法因数据库(MySQL、SQL Server、Oracle)略有差异,但核心逻辑一致:
通用语法(以 MySQL 为例)
DATEDIFF(date1, date2)
- 功能:计算
date1减去date2的天数差; - 返回值:整数(正数表示 date1 晚于 date2,负数表示 date1 早于 date2,0 表示日期相同)。
本题中的应用
我们需要“A 表日期比 B 表日期晚 1 天”,因此条件为:
DATEDIFF(a.recordDate, b.recordDate) = 1
- 若
a.recordDate是 2023-01-02,b.recordDate是 2023-01-01,差值为 1,符合条件; - 其他配对(如差值为 2、-1)都会被过滤掉。
不同数据库的兼容说明
- SQL Server:
DATEDIFF(day, b.recordDate, a.recordDate) = 1(需要指定日期部分); - Oracle:没有
DATEDIFF,需用a.recordDate - b.recordDate = 1(Oracle 中日期直接相减得到天数差)。
三、完整解题步骤
步骤 1:使用 CROSS JOIN 自连接表
将 Weather 表自连接,分别命名为 a(当日)和 b(前一天):
SELECT a.id
FROM Weather AS a
CROSS JOIN Weather AS b
此时会生成笛卡尔积(比如 4 条记录的表会生成 4×4=16 条配对),大部分配对是无效的,需要筛选。
步骤 2:用 DATEDIFF 筛选“当日-前一天”配对
通过 ON 条件保留“a 的日期比 b 晚 1 天”的配对:
SELECT a.id
FROM Weather AS a
CROSS JOIN Weather AS b
ON DATEDIFF(a.recordDate, b.recordDate) = 1
此时配对结果已精准过滤,只保留“当日与前一天”的组合(如 (2,1)、(3,2)、(4,3))。
步骤 3:筛选温度更高的记录
添加 WHERE 条件,要求当日温度(a.Temperature)高于前一天温度(b.Temperature):
SELECT a.id
FROM Weather AS a
CROSS JOIN Weather AS b
ON DATEDIFF(a.recordDate, b.recordDate) = 1
WHERE a.Temperature > b.Temperature;
最终结果验证
代入示例数据,筛选后的有效配对为:
- (a.id=2, b.id=1):20℃ > 10℃ → 保留;
- (a.id=3, b.id=2):15℃ < 20℃ → 排除;
- (a.id=4, b.id=3):25℃ > 15℃ → 保留;
最终输出 id=2 和 4,与期望结果一致。
四、常见误区与优化
误区 1:混淆 DATEDIFF 的参数顺序
错误写法:DATEDIFF(b.recordDate, a.recordDate) = 1
此时要求 b 的日期比 a 晚 1 天(即 a 是前一天,b 是当日),与需求相反,会导致无结果或错误结果。
误区 2:忘记自连接的表别名
如果不使用 AS a 和 AS b,SQL 会无法区分两个表的同名字段(如 recordDate),直接报错。
优化:用 INNER JOIN 替代 CROSS JOIN
CROSS JOIN + ON 本质上等同于 INNER JOIN(内连接),因为 ON 条件会过滤掉无效配对。因此也可以写成更简洁的内连接形式(语义更清晰):
SELECT a.id
FROM Weather AS a
INNER JOIN Weather AS b
ON DATEDIFF(a.recordDate, b.recordDate) = 1
WHERE a.Temperature > b.Temperature;
两种写法结果完全一致,INNER JOIN 更符合“筛选有效连接”的语义,推荐日常使用。
五、总结
通过这道题,我们不仅学会了具体的解题方法,更理解了背后的核心逻辑:
- 同表不同行对比 → 用自连接(将表视为两张独立表);
- 日期差值计算 → 用DATEDIFF(根据数据库选择对应语法);
CROSS JOIN + ON等同于INNER JOIN,核心是通过条件筛选有效配对。
这类“对比相邻记录”的场景在实际业务中非常常见(如环比增长、连续登录判断等),掌握自连接和日期函数的组合用法,能轻松应对这类问题。建议大家多尝试修改条件(如查询温度比后一天高的记录、计算连续 2 天升温的 id),加深对知识点的理解~
