1. 单表查询基础:SELECT语句的三段论结构
单表查询是SQL语言中最基础也最常用的操作,其核心在于SELECT语句的三段论结构:结果、位置、条件。这种结构化的查询方式让数据检索变得清晰可控。
SELECT语句的基本框架可以拆解为三个关键部分:
- 结果部分:决定查询输出哪些列或计算值
- 位置部分:指定从哪个表获取数据
- 条件部分:定义筛选数据的规则
1.1 基础查询语法解析
最简单的SELECT语句只需要指定结果和位置两部分:
sql复制SELECT Sno, Sname FROM Student;
这条语句从Student表中检索学号(Sno)和姓名(Sname)两列数据。星号(*)是特殊通配符,表示选择所有列:
sql复制SELECT * FROM Student;
注意:生产环境中应避免使用SELECT *,明确列出所需字段能提高查询效率并减少网络传输量。
1.2 SELECT子句的灵活运用
SELECT后面不仅可以跟列名,还可以包含:
- 算术表达式
- 字符串常量
- 函数调用
计算学生年龄的示例:
sql复制SELECT Sname, (EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM Sbirthdate))
FROM Student;
字符串常量需要用英文双引号包裹:
sql复制SELECT Sname, "Date of birth:", Sbirthdate FROM Student;
DISTINCT关键字用于消除重复行:
sql复制SELECT DISTINCT Sno FROM SC;
实操心得:DISTINCT会对查询结果进行排序去重,在大数据量时可能影响性能。如果确定数据本身无重复,可以省略DISTINCT提升效率。
2. 条件筛选:WHERE子句深度解析
WHERE子句是SQL查询的过滤条件核心,支持多种条件表达式和运算符组合。
2.1 比较运算符的应用
基本比较运算符包括:=, <, >, >=, <=, !=, <>, !>, !< 等,还可以用NOT进行逻辑否定:
sql复制SELECT Sname FROM Student WHERE Smajor='计算机';
字符串常量在WHERE子句中需要用单引号包裹(与SELECT中的双引号不同):
sql复制SELECT Sname FROM Student WHERE Sage > 20;
2.2 范围查询与集合查询
BETWEEN...AND...用于范围查询:
sql复制SELECT Sname FROM Student WHERE Sage BETWEEN 20 AND 30;
IN运算符检查值是否在指定集合内:
sql复制SELECT Sname FROM Student WHERE Smajor IN ('计算机','信息安全');
性能提示:对于固定值集合,IN通常比多个OR条件效率更高。但对于子查询结果集,需要评估执行计划。
2.3 高级字符串匹配
LIKE运算符支持通配符匹配:
- % 匹配任意长度字符串(包括空串)
- _ 匹配单个字符
查找学号以'220631'开头的学生:
sql复制SELECT * FROM Student WHERE Sno LIKE '220631%';
ESCAPE指定转义字符,使通配符失去特殊含义:
sql复制SELECT * FROM Course WHERE Cname LIKE 'DB\\_Design' ESCAPE '\';
2.4 空值处理与多条件组合
空值检查必须使用IS NULL,不能用等号:
sql复制SELECT * FROM SC WHERE Grade IS NULL;
AND和OR组合多个条件,注意优先级(AND高于OR):
sql复制SELECT * FROM Student WHERE Smajor = '计算机' AND Sage >= 20;
最佳实践:复杂条件建议使用括号明确优先级,避免依赖默认规则。
3. 结果排序与分页:ORDER BY和LIMIT
3.1 排序规则详解
ORDER BY支持多列排序,每列可指定ASC(升序,默认)或DESC(降序):
sql复制SELECT * FROM SC ORDER BY Cno, Grade DESC;
注意:NULL值在排序时被视为比任何非NULL值都大。
3.2 分页查询实现
LIMIT子句通常与ORDER BY配合使用,实现分页:
sql复制SELECT Sno, AVG(Grade)
FROM SC
GROUP BY Sno
ORDER BY AVG(Grade) DESC
LIMIT 5 OFFSET 2;
这个查询会:
- 计算每个学生的平均成绩
- 按平均成绩降序排序
- 跳过前2条记录
- 返回接下来的5条记录
分页优化:大数据量分页时,使用WHERE条件配合LIMIT比单纯OFFSET更高效。例如记录最后显示的ID,下页查询用WHERE id > last_id LIMIT 5。
4. 聚合计算与分组统计
4.1 聚合函数大全
SQL提供以下聚合函数:
- COUNT(*) - 统计行数
- COUNT(列名) - 统计非NULL值
- SUM() - 求和
- AVG() - 平均值
- MAX()/MIN() - 最大/最小值
统计选修不同课程的数量:
sql复制SELECT COUNT(DISTINCT Cno) FROM SC;
4.2 分组统计实战
GROUP BY将结果集按指定列分组:
sql复制SELECT Cno, COUNT(Sno) FROM SC GROUP BY Cno;
重要规则:SELECT中的非聚合列必须出现在GROUP BY中。
HAVING对分组结果进行过滤:
sql复制SELECT Sno, AVG(Grade)
FROM SC
GROUP BY Sno
HAVING AVG(Grade) >= 90;
常见错误是将HAVING条件误写在WHERE中:
sql复制-- 错误写法!
SELECT Sno, AVG(Grade)
FROM SC
WHERE AVG(Grade) >= 90 -- 聚合函数不能用于WHERE
GROUP BY Sno;
4.3 WHERE与HAVING的执行顺序
SQL执行逻辑顺序:
- FROM 确定数据源
- WHERE 过滤行
- GROUP BY 分组
- HAVING 过滤组
- SELECT 选择列
- ORDER BY 排序
- LIMIT 分页
理解这个顺序能避免很多查询设计错误。
5. 实战经验与性能优化
5.1 索引使用建议
- WHERE和ORDER BY中的列应考虑建立索引
- 避免在索引列上使用函数或计算
- 多列条件考虑复合索引
5.2 查询优化技巧
- 只查询需要的列,避免SELECT *
- 大表关联时先过滤再JOIN
- 分页查询使用WHERE条件而非大OFFSET
- 复杂查询考虑使用临时表或CTE
5.3 常见错误排查
- 字符串引号混淆:SELECT用双引号,WHERE用单引号
- NULL值判断必须用IS NULL,不能用= NULL
- GROUP BY后SELECT只能包含分组列或聚合函数
- HAVING中不能使用非聚合列条件
我在实际项目中遇到一个典型案例:一个应该返回100条记录的查询超时。分析发现是因为在WHERE中对索引列使用了函数调用,导致索引失效。改为直接列比较后,查询时间从15秒降到0.1秒。
对于大数据量表,EXPLAIN命令是分析查询性能的利器。它能显示执行计划,帮助发现全表扫描等性能瓶颈。建议对核心查询都进行EXPLAIN分析。