SELECT*FROM users WHERE age >=18; SELECT*FROM users WHERE username ='alice'; SELECT*FROM users WHERE age <>25; -- 不等于
4.1.2 逻辑运算符(AND / OR / NOT)
SELECT*FROM users WHERE age BETWEEN20AND30AND city ='Beijing'; SELECT*FROM users WHERE age <18OR age >60; SELECT*FROM users WHERENOT (status ='banned');
4.1.3 BETWEEN(包含边界)
SELECT*FROM orders WHERE order_date BETWEEN'2024-01-01'AND'2024-12-31';
4.1.4 IN(多个值匹配)
SELECT*FROM users WHERE id IN (1, 3, 5, 7); SELECT*FROM products WHERE category_id IN (SELECT id FROM categories WHERE active =1);
4.1.5 LIKE 与通配符(% 任意多个,_ 单个)
SELECT*FROM users WHERE username LIKE'a%'; -- 以 a 开头 SELECT*FROM users WHERE username LIKE'%son'; -- 以 son 结尾 SELECT*FROM users WHERE username LIKE'_a%'; -- 第二个字符是 a -- 转义通配符:使用 ESCAPE SELECT*FROM files WHERE filename LIKE'100\%%'ESCAPE'\'; -- 查找以 100% 开头的
4.1.6 REGEXP / RLIKE(正则表达式,MySQL 8.0 更强大)
SELECT*FROM users WHERE username REGEXP '^[a-z]{3}$'; -- 三个小写字母 SELECT*FROM products WHERE name REGEXP 'iPhone|iPad';
4.1.7 NULL 值处理(必须用 IS NULL / IS NOT NULL)
SELECT*FROM users WHERE email ISNULL; SELECT*FROM users WHERE email ISNOT NULL; -- 错误写法:WHERE email = NULL(永远返回空)
SELECTSUM(amount), AVG(amount) FROM orders; -- 注意:AVG 不包含 NULL 行,若希望 NULL 作为 0 参与计算,需用 IFNULL SELECTAVG(IFNULL(amount, 0)) FROM orders;
5.1.3 MAX 和 MIN
SELECTMAX(price), MIN(price) FROM products;
5.1.4 组合使用
SELECT COUNT(*) AS total_users, AVG(age) AS avg_age, MAX(age) AS oldest, MIN(age) AS youngest FROM users;
5.2 GROUP BY 分组
5.2.1 单列分组
SELECT city, COUNT(*) AS user_count FROM users GROUPBY city;
5.2.2 多列分组
SELECT city, age, COUNT(*) FROM users GROUPBY city, age;
5.2.3 GROUP BY 与聚合函数
-- 每个城市的用户平均年龄 SELECT city, AVG(age) FROM users GROUPBY city;
5.2.4 常见错误:SELECT 中出现了非分组列且非聚合
-- 错误:username 不在 GROUP BY 中,且不是聚合函数 SELECT city, username, COUNT(*) FROM users GROUPBY city;
5.2.5 GROUP_CONCAT(将分组内的值连接成字符串)
SELECT city, GROUP_CONCAT(username ORDERBY username SEPARATOR ', ') AS users_list FROM users GROUPBY city;
5.2.6 WITH ROLLUP(额外增加一行汇总)
SELECT city, SUM(amount) FROM orders GROUPBY city WITHROLLUP; -- 结果中会多一行 city = NULL, 表示所有城市的总和
5.3 HAVING 与 WHERE 的区别
WHERE:在分组前过滤行,不能使用聚合函数。
HAVING:在分组后过滤组,可以使用聚合函数。
-- 查找平均年龄大于 30 的城市 SELECT city, AVG(age) AS avg_age FROM users GROUPBY city HAVINGAVG(age) >30;
-- WHERE 和 HAVING 并用(先过滤再分组再筛选组) SELECT city, AVG(age) AS avg_age FROM users WHERE status ='active'-- 过滤掉非活跃用户 GROUPBY city HAVINGAVG(age) >25;
第六章:多表查询 —— 连接与子查询
6.1 内连接(INNER JOIN)
返回两表完全匹配的行。
-- 标准语法 SELECT u.username, o.order_date FROM users u INNERJOIN orders o ON u.id = o.user_id;
-- 多表连接 SELECT u.username, p.name, oi.quantity FROM users u JOIN orders o ON u.id = o.user_id JOIN order_items oi ON o.id = oi.order_id JOIN products p ON oi.product_id = p.id;
-- 自连接(同一张表连接自己,比如查找员工和上级) SELECT e.name AS employee, m.name AS manager FROM employees e LEFTJOIN employees m ON e.manager_id = m.id;
6.2 外连接(LEFT / RIGHT / FULL)
-- 左连接:保留左表所有行,右表无匹配则为 NULL SELECT u.username, o.order_id FROM users u LEFTJOIN orders o ON u.id = o.user_id;
-- 右连接:保留右表所有行(可用 LEFT 改写) SELECT u.username, o.order_id FROM orders o RIGHTJOIN users u ON u.id = o.user_id;
-- FULL OUTER JOIN(MySQL 不直接支持,可通过 UNION 模拟) SELECT u.name, o.order_id FROM users u LEFTJOIN orders o ON u.id = o.user_id UNION SELECT u.name, o.order_id FROM users u RIGHTJOIN orders o ON u.id = o.user_id;
-- 合并两个查询结果,自动去重(有排序开销) SELECT name FROM employees UNION SELECT name FROM customers;
-- 不去重,效率更高 SELECT name FROM employees UNIONALL SELECT name FROM customers;
注意:两个结果集的列数必须相同,对应列数据类型应兼容。
6.5 子查询
6.5.1 标量子查询(返回单个值)
-- 查询年龄大于平均年龄的用户 SELECT username, age FROM users WHERE age > (SELECTAVG(age) FROM users);
6.5.2 行子查询(返回一行多列)
-- 查询与 alice 同城市且同龄的用户 SELECT*FROM users WHERE (city, age) = (SELECT city, age FROM users WHERE username ='alice');
6.5.3 列子查询(返回一列多个值)
-- 查询在订单表中出现过的用户 SELECT username FROM users WHERE id IN (SELECTDISTINCT user_id FROM orders);
6.5.4 表子查询(返回多行多列,用于 FROM 子句)
-- 派生表(必须给别名) SELECT dept_id, avg_salary FROM (SELECT dept_id, AVG(salary) AS avg_salary FROM employees GROUPBY dept_id) AS dept_avg WHERE avg_salary >50000;
6.5.5 相关子查询(引用外层查询的列)
-- 查询每个分类中价格高于该分类平均价格的产品 SELECT p1.name, p1.price, p1.category_id FROM products p1 WHERE p1.price > (SELECTAVG(p2.price) FROM products p2 WHERE p2.category_id = p1.category_id);
6.5.6 EXISTS / NOT EXISTS(效率通常优于 IN)
-- 查询下过订单的用户 SELECT username FROM users u WHEREEXISTS (SELECT1FROM orders o WHERE o.user_id = u.id);
-- 查询从未下过订单的用户 SELECT username FROM users u WHERENOTEXISTS (SELECT1FROM orders o WHERE o.user_id = u.id);
-- 查看当前自动提交状态(默认 ON) SHOW VARIABLES LIKE'autocommit';
-- 关闭自动提交(当前会话生效) SET autocommit =0; -- 此时每条 DML 不会自动提交,需显式 COMMIT
7.2 索引概念与初步理解
7.2.1 索引的类比
索引就像书的目录:没有目录要翻遍全书(全表扫描);有目录可以快速定位页码(数据行)。
代价:占用额外磁盘,写操作(INSERT/UPDATE/DELETE)变慢(因为要更新索引)。
7.2.2 主键索引与二级索引
主键索引(聚簇索引):叶子节点直接存储整行数据。表必须有且仅有一个。
二级索引(辅助索引):叶子节点存储主键值。查询时需回表(先找到主键,再用主键去聚簇索引找行)。
7.2.3 覆盖索引
如果二级索引已经包含了查询所需要的所有列,则无需回表,直接返回索引中的值,性能极高。
-- 假设在 username 列上建有索引 -- 覆盖索引示例(索引包含了 username 和 age?需建复合索引) CREATE INDEX idx_user_age ON users(username, age); -- 查询不需要回表,因为 age 也在索引中 SELECT username, age FROM users WHERE username ='alice';
7.2.4 创建和删除索引
-- 创建普通索引 CREATE INDEX idx_email ON users(email);
-- 创建唯一索引 CREATEUNIQUE INDEX idx_username ON users(username);
-- 删除索引 DROP INDEX idx_email ON users;
-- 查看表的索引 SHOW INDEX FROM users;
7.2.5 索引使用原则(进阶篇会深入)
对 WHERE、ORDER BY、GROUP BY 中的列创建索引。
避免在索引列上使用函数或运算(会导致索引失效)。
区分度高的列优先索引(如身份证号 > 性别)。
总结与下期预告
恭喜你完成了 MySQL 初级篇的超级扩充版!现在你掌握了:
✅ MySQL 发展历程与安装配置
✅ 用户创建、权限授予与撤销(安全基石)
✅ 所有数据类型(数值、字符串、日期时间)和六大约束
✅ DDL 库表创建、修改、删除的几十种用法
✅ DML 增删改查的七种 INSERT、多表 UPDATE、多表 DELETE
✅ DQL 中 WHERE、ORDER BY、DISTINCT 的详细运算符
✅ GROUP BY 聚合与 HAVING 的精确使用
✅ 多表连接(内外连接、自连接、UNION)和子查询(标量、行、列、相关)
✅ 事务 ACID 和索引基本概念
下一阶段(进阶篇) 我们将深入:
B+ 树索引原理与执行计划(Explain)
索引优化实战(最左前缀、索引失效场景)
事务隔离级别(脏读、幻读、MVCC)
锁机制(行锁、间隙锁、死锁分析)
存储过程、触发器、视图的高级应用
练习建议:自己搭建一个 blog 系统数据库,包含用户、文章、评论、标签表,完成至少 10 个不同难度的查询(如:按评论数排序的热门文章、用户权限分级)。并在每张表上合理添加索引与约束。只有亲手敲过,知识才是你的。