SQL(Structured Query Language,结构化查询语言)是操作关系型数据库的标准语言。无论是数据查询、插入、更新还是删除,都离不开 SQL 的身影。本文将从最基础的查询语法开始,逐步深入到复杂的多表关联和分组统计,帮助你系统掌握 SQL 查询技巧,轻松应对各类数据提取需求。
一、SQL 查询基础:SELECT 语句的核心用法
SELECT语句是 SQL 中最常用的命令,用于从数据库表中提取数据。其基本语法结构为:
SELECT 列名 FROM 表名;
1.1 选择特定列
当你只需要表中的部分字段时,可以明确指定列名:
-- 只查询学生姓名
select name from student;
-- 同时查询姓名和年龄
select name, age from student;
这种方式的优势在于:
- 减少数据传输量,提高查询效率
- 避免不必要的字段干扰,结果更清晰
1.2 选择所有列
如果需要获取表中所有字段,可以使用通配符*:
select * from student;
注意:在生产环境中,不建议频繁使用*查询,原因如下:
- 可能返回不需要的字段,浪费资源
- 当表结构发生变化(如新增字段)时,查询结果会意外改变
- 无法明确知道返回的字段顺序,增加后续处理难度
二、条件筛选:WHERE 子句的灵活应用
WHERE子句用于过滤符合特定条件的记录,语法结构:
SELECT 列名 FROM 表名 WHERE 条件;
2.1 基本比较条件
SQL 支持多种比较运算符,用于构建筛选条件:
| 运算符 | 说明 | 示例 |
|---|---|---|
| = | 等于 | class=1 |
| > | 大于 | age>18 |
| < | 小于 | score<60 |
| >= | 大于等于 | age>=20 |
| <= | 小于等于 | height<=180 |
| != 或 <> | 不等于 | sex!='男' 或 sex<>'男' |
示例:
-- 查询1班的所有学生
select * from student where class=1;
-- 查询年龄不超过22岁且大于18岁的学生
select * from student where age<=22 and age>18;
2.2 逻辑运算符
当需要多个条件共同作用时,可使用逻辑运算符连接:
AND:所有条件必须同时满足OR:只要有一个条件满足即可NOT:否定后面的条件
示例:
-- 查询1班的女生
select * from student where class=1 and sex='女';
-- 查询ID为2、5或6的学生
select * from student where id=2 or id=5 or id=6;
2.3 范围查询
2.3.1 BETWEEN...AND
用于筛选字段值在指定范围内的记录(包含边界值):
-- 查询年龄在23到25岁之间的学生(包含23和25)
select * from student where age between 23 and 25;
-- 查询ID在3到7之间的学生
select * from student where id between 3 and 7;
注意:BETWEEN后的数值必须小于AND后的数值,否则会返回空结果。
2.3.2 IN 和 NOT IN
IN用于判断字段值是否在指定的集合中,NOT IN则相反:
-- 查询ID为2、5、6的学生(与OR方式等价但更简洁)
select * from student where id in(2,5,6);
-- 查询ID不是2、5、6的学生
select * from student where id not in(2,5,6);
适用场景:当筛选条件是固定的多个离散值时,IN比多个OR更高效且易读。
2.4 模糊查询:LIKE 运算符
LIKE用于实现字符串的模糊匹配,配合通配符使用:
%:匹配任意长度的字符串(包括 0 个字符)_:匹配恰好一个字符
示例:
-- 查询姓张的学生(张后面可以跟任意字符)
select * from student where name like '张%';
-- 查询名字中包含"张"的学生(张可以在任意位置)
select * from student where name like '%张%';
-- 查询姓张且名字只有两个字的学生(张+1个字符)
select * from student where name like '张_';
-- 查询第二个字是"张"的学生(任意1个字符+张+任意字符)
select * from student where name like '_张%';
注意:
- 模糊查询可能导致全表扫描,数据量大时效率较低
- 通配符
%放在开头(如%张)比放在结尾(如张%)效率更低 - 不同数据库对
LIKE的大小写敏感性处理不同(通常默认不区分)
2.5 空值判断:IS NULL 和 IS NOT NULL
在 SQL 中,NULL表示 "未知" 或 "不存在",不能用=或!=判断,必须使用IS NULL或IS NOT NULL:
-- 查询班级信息不为空的学生
select * from student where class is not null;
-- 查询班级信息为空的学生
select * from student where class is null;
为什么不能用=null?
NULL不是一个具体的值,而是表示 "没有值",所以逻辑上无法与任何值(包括自身)比较,NULL=NULL的结果永远是NULL(不成立)。
三、结果排序:ORDER BY 子句
ORDER BY用于对查询结果按指定字段排序,语法:
SELECT 列名 FROM 表名 [WHERE 条件] ORDER BY 字段名 [ASC|DESC];
ASC:升序排列(默认,可省略)DESC:降序排列
示例:
-- 按ID降序查询所有学生(从大到小)
select * from student order by id desc;
-- 先筛选男生,再按年龄降序排列
select * from student where sex='男' order by age desc;
-- 多字段排序:先按班级升序,再按年龄降序
select * from student order by class asc, age desc;
注意:
- 排序会增加查询开销,数据量大时需谨慎使用
- 若未指定排序方式,数据库不保证结果的顺序
- 对
NULL值的排序规则:通常NULL被视为小于任何非NULL值,升序时排在最前面
四、限制结果数量:LIMIT 子句(分页查询)
LIMIT用于限制返回的记录条数,常用于分页查询,语法:
-- 方式1:LIMIT 起始索引, 记录数(起始索引从0开始)
SELECT 列名 FROM 表名 LIMIT a, b;
-- 方式2:LIMIT 记录数 OFFSET 起始索引(与方式1等价,更易读)
SELECT 列名 FROM 表名 LIMIT b OFFSET a;
示例:
-- 从索引4开始,查询3条记录(即第5-7条)
select * from student limit 4,3;
-- 分页查询(每页3条)
-- 第一页(索引0-2)
select * from student limit 0,3;
-- 第二页(索引3-5)
select * from student limit 3,3;
-- 第三页(索引6-8)
select * from student limit 6,3;
-- 通用分页公式(page为页码,pageSize为每页条数)
select * from student limit (page-1)*pageSize, pageSize;
不同数据库的分页语法:
- MySQL/SQLite:使用
LIMIT - SQL Server:使用
TOP或OFFSET...FETCH - Oracle:使用
ROWNUM或FETCH FIRST...ROWS ONLY
子句组合顺序
当WHERE、ORDER BY、LIMIT同时出现时,必须遵循固定顺序:
-- 正确顺序:WHERE → ORDER BY → LIMIT
select * from student where class=1 order by age desc limit 0,2;
逻辑执行顺序:
- 先通过
WHERE筛选符合条件的记录 - 对筛选后的结果用
ORDER BY排序 - 最后用
LIMIT限制返回条数(如果先LIMIT再排序,会导致排序仅对部分数据生效)
五、聚合函数与分组查询
聚合函数用于对一组数据进行计算并返回单一结果,常与GROUP BY配合使用实现分组统计。
5.1 常用聚合函数
| 函数 | 说明 | 示例 |
|---|---|---|
| SUM (字段) | 计算字段值的总和 | sum(age) 计算所有学生年龄总和 |
| AVG (字段) | 计算字段值的平均值 | avg(score) 计算平均分 |
| MAX (字段) | 求字段的最大值 | max(height) 求最高身高 |
| MIN (字段) | 求字段的最小值 | min(weight) 求最低体重 |
| COUNT (表达式) | 统计记录数 | count(*) 统计所有记录数 |
示例:
-- 计算所有学生的年龄总和
select sum(age) from student;
-- 统计年龄不为空的学生数量(忽略NULL)
select count(age) from student;
-- 统计所有学生的总人数(包含NULL记录)
select count(*) from student;
COUNT 函数的区别:
count(*):统计所有记录数,包括NULLcount(字段):统计该字段非NULL的记录数count(1):与count(*)类似,效率可能更高(因数据库而异)
5.2 GROUP BY 分组查询
GROUP BY用于将记录按指定字段分组,然后对每个组应用聚合函数:
-- 按班级分组,计算每个班级的平均年龄
select avg(age), class from student group by class;
注意:SELECT子句中只能出现:
- 聚合函数(如
avg(age)) GROUP BY后指定的分组字段(如class)
否则会报错(部分数据库如 MySQL 有非标准扩展,需谨慎使用)。
5.3 HAVING 子句:筛选分组结果
WHERE用于筛选行,HAVING用于筛选分组后的结果(对聚合函数结果筛选):
-- 按班级分组,只显示1班的平均年龄
select avg(age), class from student group by class having class=1;
-- 先筛选男生,再按班级分组,只显示1班的平均年龄
select avg(age), class from student where sex='男' group by class having class=1;
WHERE与HAVING的区别:
WHERE在分组前筛选,不允许使用聚合函数HAVING在分组后筛选,允许使用聚合函数(如having avg(age)>20)
六、多表查询:关联查询详解
实际业务中,数据往往分散在多个表中(如学生表、班级表、课程表),需要通过关联查询获取完整信息。
6.1 表连接的基本概念
表连接基于两个表之间的关联字段(通常是外键),常见类型:
- 内连接(INNER JOIN):只返回两个表中匹配的记录
- 左外连接(LEFT JOIN):返回左表所有记录,右表匹配的记录,无匹配则为
NULL - 右外连接(RIGHT JOIN):返回右表所有记录,左表匹配的记录,无匹配则为
NULL
6.2 内连接(INNER JOIN)
内连接是最常用的连接方式,只保留两个表中关联字段匹配的记录:
-- 隐式内连接(使用逗号分隔表,WHERE指定关联条件)
select * from student, class where student.classid = class.id;
-- 显式内连接(使用JOIN...ON,更清晰推荐使用)
select * from student join class on student.classid = class.id;
显式内连接的优势:
- 关联条件与筛选条件分离,逻辑更清晰
- 支持多表连接时的复杂关联逻辑
6.3 外连接
6.3.1 左外连接(LEFT JOIN)
左外连接以左表为基准,返回左表所有记录,右表中匹配的记录;若右表无匹配,则显示NULL:
-- 查询所有学生及其班级信息(包括没有班级的学生)
select * from student left join class on student.classid = class.id;
6.3.2 右外连接(RIGHT JOIN)
右外连接以右表为基准,返回右表所有记录,左表中匹配的记录;若左表无匹配,则显示NULL:
-- 查询所有班级及其学生信息(包括没有学生的班级)
select * from student right join class on student.classid = class.id;
6.4 表别名与字段别名
当表名较长或多表连接时,可使用AS为表或字段起别名(AS可省略):
-- 为表起别名(简化书写)
select * from student as s, class c where s.classid = c.id;
-- 为字段起别名(使结果更易读)
select student.*, class.name as classname from student, class where student.classid = class.id;
注意:别名不能在WHERE子句中使用(因WHERE执行早于字段别名解析),但可在ORDER BY中使用。
七、数据库设计基础:表关系与外键
合理的表结构设计是高效查询的基础,常见的表关系包括:
7.1 一对一关系
两个表中的记录一一对应(如 "学生" 表和 "学生档案" 表)。
设计原则:通常合并为一张表(减少关联查询),必要时拆分(如敏感信息分离)。
7.2 一对多关系
一个表中的一条记录对应另一个表中的多条记录(如 "班级" 表和 "学生" 表)。
设计原则:在 "多" 的一方添加外键,指向 "一" 的一方的主键:
班级表(id, classname)
学生表(id, name, classid)-- classid是指向班级表id的外键
7.3 多对多关系
两个表中的记录相互对应多条(如 "学生" 表和 "课程" 表)。
设计原则:新增一张中间表,存储两个表的主键作为联合外键:
学生表(id, name)
课程表(id, coursename)
学生课程表(studentid, courseid)-- 双外键,分别指向学生表和课程表
7.4 外键约束的作用
- 保证数据一致性(如不能删除有学生的班级)
- 防止无效数据插入(如学生的 classid 必须存在于班级表中)
- 明确表之间的关系,提高可读性
创建表时定义外键示例:
-- 创建班级表
create table class(
id int primary key auto_increment,
name varchar(20) not null
) engine=InnoDB default charset=utf8mb4;
-- 创建学生表,classid为外键
create table student(
id int primary key auto_increment,
name varchar(20) not null,
classid int,
foreign key (classid) references class(id)
) engine=InnoDB default charset=utf8mb4;
注意:使用InnoDB引擎才支持外键约束,MyISAM引擎不支持。
八、实战技巧与性能优化
8.1 查询优化基本原则
- 只查询需要的字段:避免
SELECT *,减少数据传输 - 合理使用索引:在
WHERE、JOIN、ORDER BY涉及的字段上建立索引 - 减少模糊查询:
%开头的LIKE会导致全表扫描,尽量用其他方式替代 - 分页查询优化:大数据量分页时,使用
LIMIT配合索引字段排序 - 避免
SELECT DISTINCT:去重操作开销大,尽量在设计上保证数据唯一性
8.2 常见错误及解决方案
| 错误 | 原因 | 解决方案 |
|---|---|---|
| 数据类型不匹配 | 条件中使用的类型与字段类型不符 | 确保比较双方类型一致(如字符串加引号) |
NULL判断错误 | 用=判断NULL值 | 改用IS NULL或IS NOT NULL |
| 分组函数使用错误 | SELECT中包含非聚合、非分组字段 | 只保留聚合函数和GROUP BY字段 |
5083

被折叠的 条评论
为什么被折叠?



