最近刷了牛客网的 SQL 快速入门题,内容主要覆盖基础查询、条件筛选、聚合统计、多表连接、子查询、常用函数以及窗口函数。题目看似很多,但核心知识点比较集中。掌握下面这些语法,基本可以应对大部分入门和常见面试题。
说明:你提供的语雀页面目前需要登录,无法读取其中的"五十题"具体内容,下面的总结主要根据牛客公开的"SQL 快速入门"题单整理。
一、基础查询
最基本的查询语句由 SELECT 和 FROM 组成。
SELECT *
FROM user_profile;
查询指定列:
SELECT device_id, gender, age
FROM user_profile;
* 表示查询全部列,但在实际开发中更推荐写出需要的字段,避免返回无用数据。
使用 DISTINCT 去重:
SELECT DISTINCT university
FROM user_profile;
限制返回数量:
SELECT *
FROM user_profile
LIMIT 5;
LIMIT 5 表示只返回前 5 行,也可以配合偏移量实现分页:
SELECT *
FROM user_profile
LIMIT 10, 20;
表示跳过前 10 行,返回接下来的 20 行。
使用 AS 修改列名:
SELECT
device_id AS 用户编号,
age AS 年龄
FROM user_profile;
AS 也可以给表设置别名:
SELECT u.device_id, u.age
FROM user_profile AS u;
二、条件查询与排序
使用 WHERE 对数据进行筛选:
SELECT *
FROM user_profile
WHERE university = '北京大学';
常见比较运算符有:
= 等于
<> 或 != 不等于
> 大于
< 小于
>= 大于等于
<= 小于等于
例如查询年龄大于 24 岁的用户:
SELECT *
FROM user_profile
WHERE age > 24;
查询年龄在 20 到 30 岁之间:
SELECT *
FROM user_profile
WHERE age BETWEEN 20 AND 30;
BETWEEN 包含两个边界值。
多个条件可以使用 AND 和 OR:
SELECT *
FROM user_profile
WHERE age >= 20
AND age <= 25
AND gender = '男';
使用 IN 查询多个指定值:
SELECT *
FROM user_profile
WHERE university IN ('北京大学', '复旦大学');
排除指定值:
SELECT *
FROM user_profile
WHERE university NOT IN ('复旦大学');
判断空值时不能使用 = NULL,而应该使用 IS NULL 或 IS NOT NULL:
SELECT *
FROM user_profile
WHERE age IS NULL;
SELECT *
FROM user_profile
WHERE age IS NOT NULL;
模糊查询使用 LIKE:
SELECT *
FROM user_profile
WHERE university LIKE '%北京%';
其中:
% 代表任意长度的字符串
_ 代表一个字符
查询结果可以使用 ORDER BY 排序:
SELECT *
FROM user_profile
ORDER BY age;
默认是升序排列,也可以明确写出 ASC:
ORDER BY age ASC;
降序排列使用 DESC:
SELECT *
FROM user_profile
ORDER BY age DESC;
多列排序时,先按照第一列排序,第一列相同时再按照第二列排序:
SELECT *
FROM user_profile
ORDER BY university ASC, age DESC;
三、聚合函数与分组查询
SQL 中常见的聚合函数包括:
COUNT() 统计数量
SUM() 求和
AVG() 平均值
MAX() 最大值
MIN() 最小值
统计用户数量:
SELECT COUNT(*) AS user_count
FROM user_profile;
计算男性用户的平均 GPA:
SELECT AVG(gpa) AS avg_gpa
FROM user_profile
WHERE gender = '男';
按照学校进行分组统计:
SELECT
university,
COUNT(*) AS user_count
FROM user_profile
GROUP BY university;
GROUP BY 用于将相同字段值的数据划分到同一组。
统计每个学校的平均 GPA,并按照平均值降序排列:
SELECT
university,
AVG(gpa) AS avg_gpa
FROM user_profile
GROUP BY university
ORDER BY avg_gpa DESC;
如果要对分组后的结果进行筛选,需要使用 HAVING,而不是 WHERE:
SELECT
university,
COUNT(*) AS user_count
FROM user_profile
GROUP BY university
HAVING COUNT(*) > 10;
两者的区别是:
WHERE:分组前筛选原始数据
HAVING:分组后筛选统计结果
例如,查询年龄大于 20 岁且用户数量超过 5 人的学校:
SELECT
university,
COUNT(*) AS user_count
FROM user_profile
WHERE age > 20
GROUP BY university
HAVING COUNT(*) > 5;
四、多表查询与连接
实际项目中的数据通常存储在多张表中。例如:
user_profile 用户信息表
question_practice_detail 刷题记录表
question_detail 题目信息表
使用 JOIN 可以连接多张表。
查询用户及其刷题记录:
SELECT
u.device_id,
u.university,
q.question_id,
q.result
FROM user_profile AS u
JOIN question_practice_detail AS q
ON u.device_id = q.device_id;
JOIN 默认表示内连接,只保留两张表中能够匹配的数据。
左连接使用 LEFT JOIN:
SELECT
u.device_id,
q.question_id
FROM user_profile AS u
LEFT JOIN question_practice_detail AS q
ON u.device_id = q.device_id;
左连接会保留左表的全部数据,即使右表中没有对应记录,也会显示为 NULL。
常见连接类型:
INNER JOIN:只保留匹配成功的数据
LEFT JOIN:保留左表全部数据
RIGHT JOIN:保留右表全部数据
统计每个学校答过题的用户数量:
SELECT
u.university,
COUNT(DISTINCT q.device_id) AS user_count
FROM user_profile AS u
JOIN question_practice_detail AS q
ON u.device_id = q.device_id
GROUP BY u.university;
这里使用 COUNT(DISTINCT device_id),避免同一用户刷多道题时被重复统计。
五、子查询与组合查询
子查询就是将一个查询结果作为另一个查询的条件。
查询 GPA 高于平均 GPA 的用户:
SELECT *
FROM user_profile
WHERE gpa > (
SELECT AVG(gpa)
FROM user_profile
);
子查询也可以放在 FROM 后面,作为临时表:
SELECT university, avg_gpa
FROM (
SELECT
university,
AVG(gpa) AS avg_gpa
FROM user_profile
GROUP BY university
) AS t
WHERE avg_gpa > 3.0;
UNION 可以合并两个查询结果,并自动去重:
SELECT device_id
FROM user_profile
WHERE university = '山东大学'
UNION
SELECT device_id
FROM user_profile
WHERE gender = '男';
如果不需要去重,可以使用 UNION ALL,性能通常更好:
SELECT device_id
FROM user_profile
WHERE university = '山东大学'
UNION ALL
SELECT device_id
FROM user_profile
WHERE gender = '男';
六、常用条件函数
CASE WHEN 是 SQL 中非常重要的条件表达式。
统计 25 岁以上和以下的用户数量:
SELECT
SUM(CASE WHEN age >= 25 THEN 1 ELSE 0 END) AS over_25,
SUM(CASE WHEN age < 25 THEN 1 ELSE 0 END) AS under_25
FROM user_profile;
也可以将年龄划分为不同区间:
SELECT
device_id,
age,
CASE
WHEN age < 20 THEN '20岁以下'
WHEN age BETWEEN 20 AND 25 THEN '20-25岁'
ELSE '25岁以上'
END AS age_group
FROM user_profile;
常见的空值处理函数是 IFNULL():
SELECT
device_id,
IFNULL(gpa, 0) AS gpa
FROM user_profile;
如果 GPA 为空,就显示为 0。
七、日期函数
日期题在 SQL 面试中出现频率很高。常用函数包括:
YEAR(date) 获取年份
MONTH(date) 获取月份
DAY(date) 获取日期
DATE(date) 提取日期部分
DATEDIFF(a, b) 计算日期差
统计 8 月每天的刷题数量:
SELECT
DATE(date) AS practice_day,
COUNT(*) AS question_count
FROM question_practice_detail
WHERE MONTH(date) = 8
GROUP BY DATE(date)
ORDER BY practice_day;
也可以使用日期范围筛选:
WHERE date >= '2021-08-01'
AND date < '2021-09-01'
这种写法比对日期使用 LIKE 更准确,也更容易使用索引。
八、字符串函数与正则表达式
常见字符串函数包括:
LENGTH() 字符串长度
SUBSTRING() 截取字符串
LEFT() 从左侧截取
RIGHT() 从右侧截取
CONCAT() 拼接字符串
LOWER() 转小写
UPPER() 转大写
例如从 URL 中提取用户名:
SELECT
SUBSTRING_INDEX(
SUBSTRING_INDEX(blog_url, '/', -1),
'.',
1
) AS username
FROM user_profile;
SUBSTRING_INDEX(str, delimiter, count) 可以按照指定分隔符截取字符串。
正则表达式使用 REGEXP:
SELECT *
FROM user_profile
WHERE phone REGEXP '^1[3-9][0-9]{9}$';
这个表达式用于匹配 11 位手机号码,且第一位为 1,第二位为 3 到 9。
九、窗口函数
窗口函数可以在不改变原始行数的情况下进行分组统计,常用于排名、组内比较和累计计算。
查找每个学校 GPA 最低的用户:
SELECT *
FROM (
SELECT
device_id,
university,
gpa,
ROW_NUMBER() OVER (
PARTITION BY university
ORDER BY gpa
) AS rn
FROM user_profile
) AS t
WHERE rn = 1;
PARTITION BY university 表示按照学校分组,ORDER BY gpa 表示组内按照 GPA 升序排列。
常见排名函数:
ROW_NUMBER():每行都有唯一排名
RANK():并列时排名相同,后面的排名会跳过
DENSE_RANK():并列时排名相同,但后续排名不跳过
计算累计利润:
SELECT
date,
profit,
SUM(profit) OVER (
ORDER BY date
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW
) AS total_profit
FROM daily_profit;
计算每个用户相对于前一天的数据,可以使用 LAG():
SELECT
device_id,
date,
LAG(date) OVER (
PARTITION BY device_id
ORDER BY date
) AS previous_date
FROM question_practice_detail;
十、刷题时容易出错的地方
第一,空值不能使用 = NULL 判断,必须使用 IS NULL。
第二,聚合函数和普通字段一起查询时,普通字段通常需要出现在 GROUP BY 中。
第三,WHERE 不能直接筛选聚合结果,聚合结果需要使用 HAVING。
第四,多表连接时要确认连接条件,否则容易产生笛卡尔积,导致统计数量严重偏大。
第五,统计用户数量时,要根据题意判断是否需要 DISTINCT。
第六,日期筛选尽量使用范围条件,例如:
WHERE date >= '2021-08-01'
AND date < '2021-09-01'
第七,窗口函数不能直接写在 WHERE 中,通常需要先放在子查询中,再在外层筛选。
总结
牛客 SQL 快速入门题主要围绕以下知识点展开:
SELECT、WHERE、ORDER BY、LIMIT
DISTINCT、LIKE、IN、BETWEEN
IS NULL、聚合函数、GROUP BY、HAVING
JOIN、LEFT JOIN、子查询、UNION
CASE WHEN、IFNULL、日期函数、字符串函数
REGEXP、窗口函数、排名、累计统计
刷题时可以按照"先筛选,再分组,后排序"的思路分析:
FROM/JOIN
→ WHERE
→ GROUP BY
→ HAVING
→ SELECT
→ ORDER BY
→ LIMIT
掌握这些常用语法后,再结合具体业务理解题目要求,SQL 查询题基本就能形成稳定的解题思路。真正需要重点练习的,不是死记每道题的答案,而是判断题目到底属于筛选、分组、连接、子查询还是窗口函数问题。