1. MySQL查询基础入门
作为一名数据库开发人员,我经常需要处理各种数据查询需求。MySQL作为最流行的关系型数据库之一,其查询功能强大而灵活。今天我想分享一些MySQL基础查询操作的实战经验,这些技巧在我多年的工作中被反复验证过。
MySQL查询的核心是SELECT语句,它就像是我们获取数据的"望远镜"。通过这个"望远镜",我们可以精确地定位到需要的数据,也可以广泛地扫描整个数据库。下面我将从最基本的查询开始,逐步深入到更复杂的操作。
提示:在实际开发中,建议始终使用多行写法来编写SQL语句,这样不仅可读性更好,也便于后续的维护和调试。
2. SELECT子句详解
2.1 基础列查询
最基本的SELECT查询就是指定要获取的列。例如,我们需要从Student表中查询学号、姓名和班级:
sql复制SELECT 学号, 姓名, 班级
FROM Student;
这种明确指定列名的方式是最佳实践,相比使用SELECT *有以下优势:
- 减少不必要的数据传输,提高查询效率
- 代码更清晰,明确知道获取了哪些数据
- 当表结构变更时,不会意外获取到不需要的列
2.2 去重查询
在实际业务中,我们经常需要获取不重复的值。比如查询公司所有的职位类型:
sql复制-- 这样会返回所有员工的职位,包含重复
SELECT job FROM emp;
-- 使用DISTINCT去重,只返回不同的职位类型
SELECT DISTINCT job FROM emp;
值得注意的是,DISTINCT作用于所有选择的列。如果查询多列,只有当所有列的值都相同时才会被认为是重复的。
2.3 列表达式与计算字段
MySQL允许我们在SELECT子句中使用表达式进行计算。例如查询图书价格的九折:
sql复制SELECT book_ID, name, price, price*0.9 AS discount_price
FROM Book;
这里使用了AS关键字为计算列创建别名,使结果更易读。实际上,AS关键字是可选的,但我建议始终使用它,以提高代码清晰度。
2.4 别名使用技巧
别名不仅适用于列,也可以用于表。这在多表查询时特别有用:
sql复制-- 列别名
SELECT name AS 学生姓名, age AS 学生年龄 FROM student;
-- 表别名
SELECT s.name, s.age
FROM student AS s;
在复杂查询中,合理的别名可以显著提高SQL的可读性。我习惯使用简洁但有意义的别名,通常取表名的首字母或缩写。
3. WHERE子句过滤数据
3.1 基础比较运算
WHERE子句是SQL的过滤器,它允许我们指定条件来筛选数据。基本的比较运算符包括:
=等于!=或<>不等于>大于<小于>=大于等于<=小于等于
sql复制-- 查询成绩大于90分的学生
SELECT * FROM Student WHERE Score > 90;
-- 查询不是张三的学生
SELECT * FROM student WHERE name != '张三';
注意:在MySQL中,字符串比较默认不区分大小写。如果需要区分大小写,可以使用
BINARY关键字或设置相应的字符集和排序规则。
3.2 特殊比较运算符
3.2.1 BETWEEN...AND...范围查询
这个操作符用于查询值在某个范围内的记录:
sql复制-- 查询成绩在60到90分之间的学生
SELECT * FROM Student
WHERE Score BETWEEN 60 AND 90;
等价于:
sql复制SELECT * FROM Student
WHERE Score >= 60 AND Score <= 90;
虽然两种写法结果相同,但BETWEEN的写法更简洁直观。需要注意的是,BETWEEN是包含边界值的。
3.2.2 IN集合查询
当我们需要匹配多个特定值时,IN操作符非常有用:
sql复制-- 查找特定出版社的图书
SELECT * FROM Book
WHERE publish IN ('机械工业出版社','清华大学出版社','高等教育出版社');
IN操作符比多个OR条件更简洁,执行效率也通常更高。对于NOT IN,要注意NULL值的处理,因为任何与NULL的比较结果都是未知的。
3.2.3 LIKE模糊查询
LIKE用于模式匹配,支持两个通配符:
%匹配任意数量字符(包括零个字符)_匹配单个字符
sql复制-- 查询姓张的学生
SELECT * FROM Reader WHERE name LIKE '张%';
-- 查询名字中包含"军"的学生
SELECT * FROM student WHERE name LIKE '%军%';
-- 查询名字第二个字是"宇"的员工
SELECT ename FROM emp WHERE ename LIKE "_宇%";
模糊查询虽然方便,但要注意它通常无法使用索引,在大数据量时性能较差。如果可能,尽量使用更精确的查询条件。
3.2.4 NULL值处理
NULL在数据库中表示缺失或未知的值,不能用普通的比较运算符来判断:
sql复制-- 查询性别为NULL的学生
SELECT * FROM Student WHERE sex IS NULL;
-- 查询出生日期不为NULL的学生
SELECT name FROM Student WHERE birthdate IS NOT NULL;
常见的错误是使用= NULL来判断,这不会返回任何结果,因为NULL与任何值的比较(包括NULL本身)结果都是未知的。
3.3 多重条件组合
我们可以使用AND、OR和NOT来组合多个条件:
sql复制-- 查询1992年以后出生的女学生
SELECT name FROM Student
WHERE birthdate >= '1992-1-1' AND sex='女';
-- 查询部门在10或20且工资在3000到5000之间的员工
SELECT 姓名,部门,工资
FROM emp
WHERE 部门 IN (10,20) AND 工资 BETWEEN 3000 AND 5000;
当条件复杂时,使用括号明确优先级是个好习惯:
sql复制SELECT * FROM Employee
WHERE (department = 'IT' OR department = 'HR')
AND salary > 5000;
4. 结果排序与限制
4.1 ORDER BY排序
ORDER BY子句允许我们对结果集进行排序:
sql复制-- 按出生日期升序排列
SELECT * FROM Student ORDER BY birthdate ASC;
-- 按价格降序排列,同价格按出版社升序排列
SELECT * FROM Book ORDER BY price DESC, publish ASC;
排序规则:
- ASC:升序(默认)
- DESC:降序
- 可以指定多个排序条件,优先级从左到右
- NULL值在升序时排在最前面,降序时排在最后面
4.2 LIMIT限制结果集
LIMIT用于限制返回的记录数量,常用于分页查询:
sql复制-- 查询前5条记录
SELECT * FROM student LIMIT 5;
-- 从第6条开始查询5条记录(第二页)
SELECT * FROM student LIMIT 5, 5;
分页查询的通用公式:
sql复制SELECT * FROM table
LIMIT (page_number - 1) * page_size, page_size;
对于TOPN查询,通常先排序再限制:
sql复制-- 查询成绩最高的5名学生
SELECT sname, score
FROM stu
ORDER BY score DESC
LIMIT 5;
性能提示:在大数据量分页时,越到后面的页数性能越差。对于深度分页,可以考虑使用"记住上次看到的位置"技术来优化。
5. 高级查询技巧
5.1 UNION合并结果集
UNION操作符用于合并两个或多个SELECT语句的结果集:
sql复制-- 合并两个查询结果,去除重复
SELECT * FROM Book WHERE publish='清华大学出版社'
UNION
SELECT * FROM Book WHERE price<25;
-- 使用UNION ALL保留所有记录(包括重复)
SELECT * FROM Book WHERE publish='清华大学出版社'
UNION ALL
SELECT * FROM Book WHERE price<25;
使用UNION时要注意:
- 每个SELECT语句必须有相同数量的列
- 列的数据类型必须兼容
- 结果集的列名取自第一个SELECT语句
- 默认去除重复行,使用UNION ALL可以保留重复行
5.2 分页实现最佳实践
在实际应用中,分页通常需要知道总记录数和总页数。完整的后端分页实现通常包括:
- 查询总记录数:
sql复制SELECT COUNT(*) AS totalCount FROM student;
- 计算总页数(后端代码):
java复制int totalPage = (totalCount + pageSize - 1) / pageSize;
- 查询当前页数据:
sql复制SELECT * FROM student
ORDER BY id
LIMIT (currentPage-1)*pageSize, pageSize;
对于大型表,这种分页方式在翻到后面几页时会变慢,因为MySQL需要扫描并跳过前面的所有记录。优化方法包括:
- 使用覆盖索引
- 记录上一页的最后一条记录的ID,使用WHERE id > last_id LIMIT pageSize
- 使用延迟关联
6. 实战经验与常见问题
6.1 性能优化建议
- **避免SELECT ***:只查询需要的列,减少数据传输量
- 合理使用索引:为WHERE、JOIN、ORDER BY涉及的列创建适当索引
- 注意LIKE查询:前导通配符(如
%abc)无法使用索引 - 小心OR条件:多个OR条件可能导致索引失效,考虑使用UNION ALL替代
- LIMIT分页优化:深度分页考虑使用基于游标的方法
6.2 常见错误排查
-
NULL值问题:
- 使用
= NULL而不是IS NULL - 在NOT IN子查询中包含NULL值
- 使用
-
数据类型不匹配:
- 字符串比较时忘记加引号
- 日期格式不正确
-
隐式类型转换:
- 比较不同数据类型的列可能导致索引失效
-
字符集问题:
- 不同字符集的列进行比较或连接时出错
6.3 实用技巧
- 快速查看表结构:
sql复制DESCRIBE 表名;
-- 或
SHOW CREATE TABLE 表名;
- 测试查询性能:
sql复制EXPLAIN SELECT * FROM student WHERE name LIKE '张%';
-
格式化SQL:使用工具或IDE的格式化功能保持SQL可读性
-
注释使用:复杂查询添加注释说明业务逻辑
sql复制-- 查询活跃用户:过去30天有登录且完成至少1单
SELECT user_id FROM user_activity
WHERE last_login > DATE_SUB(NOW(), INTERVAL 30 DAY)
AND completed_orders > 0;
掌握这些基础查询操作是成为SQL高手的必经之路。在实际项目中,我建议先从简单查询开始,逐步增加复杂度,并始终关注查询性能和结果准确性。