SQL 查询全攻略:从基础语法到高级应用(超详细手册)

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 NULLIS 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:使用TOPOFFSET...FETCH
  • Oracle:使用ROWNUMFETCH FIRST...ROWS ONLY

子句组合顺序

WHEREORDER BYLIMIT同时出现时,必须遵循固定顺序:

-- 正确顺序:WHERE → ORDER BY → LIMIT
select * from student where class=1 order by age desc limit 0,2;

逻辑执行顺序

  1. 先通过WHERE筛选符合条件的记录
  2. 对筛选后的结果用ORDER BY排序
  3. 最后用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(*):统计所有记录数,包括NULL
  • count(字段):统计该字段非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;

WHEREHAVING的区别

  • 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 查询优化基本原则

  1. 只查询需要的字段:避免SELECT *,减少数据传输
  2. 合理使用索引:在WHEREJOINORDER BY涉及的字段上建立索引
  3. 减少模糊查询%开头的LIKE会导致全表扫描,尽量用其他方式替代
  4. 分页查询优化:大数据量分页时,使用LIMIT配合索引字段排序
  5. 避免SELECT DISTINCT:去重操作开销大,尽量在设计上保证数据唯一性

8.2 常见错误及解决方案

错误原因解决方案
数据类型不匹配条件中使用的类型与字段类型不符确保比较双方类型一致(如字符串加引号)
NULL判断错误=判断NULL改用IS NULLIS NOT NULL
分组函数使用错误SELECT中包含非聚合、非分组字段只保留聚合函数和GROUP BY字段
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值