本文能帮你解决什么问题?
假设你是外卖平台的数据工程师,老板突然要:
传统写法需要写多层子查询,而用分析函数只需一个SQL!
-- 骑手移动平均配送时长
SELECT rider_id, order_time,
AVG(delivery_time) OVER (
PARTITION BY rider_id
ORDER BY order_time
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS avg_3_orders
FROM delivery_records;
-- 商圈商家销售额排名
SELECT mall_id, shop_name, sales,
RANK() OVER (PARTITION BY mall_id ORDER BY sales DESC) AS top3
FROM shop_sales;
功能 | 传统写法 | 分析函数写法 | 优势对比 |
---|---|---|---|
计算部门平均 | 需要GROUP BY + JOIN回原表 | 直接OVER计算 | 保留原始明细数据 |
累计销售额 | 多重自连接 | 一个SUM+窗口帧搞定 | 执行速度快10倍+ |
数据对比 | 复杂子查询 | LAG/LEAD一行搞定 | 代码量减少80% |
-- 建表语句
CREATE TABLE class (
student_id INT PRIMARY KEY,
name VARCHAR(20),
math_score INT
);
-- 插入数据
INSERT INTO class VALUES
(1, '小明', 90),
(2, '小红', 90),
(3, '小刚', 85);
SELECT name, math_score,
ROW_NUMBER() OVER (ORDER BY math_score DESC) as row_num
FROM class;
结果:
姓名 | 数学成绩 | 排名 |
---|---|---|
小明 | 90 | 1 |
小红 | 90 | 2 |
小刚 | 85 | 3 |
SELECT name, math_score,
RANK() OVER (ORDER BY math_score DESC) as rank
FROM class;
结果:
姓名 | 数学成绩 | 排名 |
---|---|---|
小明 | 90 | 1 |
小红 | 90 | 1 |
小刚 | 85 | 3 |
SELECT name, math_score,
DENSE_RANK() OVER (ORDER BY math_score DESC) as dense_rank
FROM class;
结果:
姓名 | 数学成绩 | 排名 |
---|---|---|
小明 | 90 | 1 |
小红 | 90 | 1 |
小刚 | 85 | 2 |
避坑指南:
DENSE_RANK
ROW_NUMBER
-- 查看股价波动
SELECT
trade_date,
closing_price,
LAG(closing_price, 1) OVER (ORDER BY trade_date) as 昨日收盘价,
closing_price - LAG(closing_price, 1) OVER (ORDER BY trade_date) as 涨跌幅
FROM stock;
-- 计算7日平均新增
SELECT
report_date,
new_cases,
AVG(new_cases) OVER (
ORDER BY report_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) as 7日移动平均
FROM covid_data;
慢查询(3.2秒):
SELECT user_id,
(SELECT AVG(score) FROM logs WHERE user_id = l.user_id)
FROM logs l;
优化后(0.4秒):
SELECT user_id,
AVG(score) OVER (PARTITION BY user_id)
FROM logs;
需求:计算每个员工的薪资在部门内的占比
表结构:employees(dept, name, salary)
需求:找出连续3天登录的用户(提示:用日期差值法)
-- 基础题答案
SELECT
dept, name, salary,
salary / SUM(salary) OVER (PARTITION BY dept) AS ratio
FROM employees;
-- 进阶题答案
WITH login_series AS (
SELECT
user_id,
login_date - ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) AS grp
FROM user_logins
)
SELECT user_id
FROM login_series
GROUP BY user_id, grp
HAVING COUNT(*) >= 3;
阶段 | 学习重点 | 推荐资源 |
---|---|---|
新手 | 基础语法 | 《SQL必知必会》 |
进阶 | 复杂查询 | LeetCode数据库题库 |
高手 | 执行计划优化 | 《高性能MySQL》第6章 |
下期预告:《SQL优化之分析需求》
互动话题:你在学习SQL时遇到过哪些坑?欢迎评论区留言讨论!
️温馨提示:我是[随缘而动,随遇而安], 一个喜欢用生活案例讲技术的开发者。如果觉得有帮助,点赞关注不迷路