基础查询 SELECT 与 DISTINCT
本教程共 46 篇 · 第 19 篇 · 更新于 2026-07-30 · 约 9 分钟阅读
19. 基础查询 SELECT 与 DISTINCT
本节目标:学会用 SELECT 从表里取数据,掌握查全部列、查指定列、用 DISTINCT 去重、给表和列起别名,理解 SELECT * 什么时候能用什么时候不能碰。
19.1 SELECT 是干什么的
前面几章学了怎么插数据、改数据、删数据。现在到了最核心的部分—查数据。
SELECT 是 SQL 里用得最多的语句,没有之一。你日常工作中 80% 的 SQL 都是 SELECT。说白了,它就是”从表里把我要的数据捞出来”。
最简单的查询,查整张表所有数据:
SELECT * FROM users;
* 代表所有列。执行后,users 表里的全部行、全部列都显示出来:
+----+----------+----------------------+-----+---------------------+
| id | username | email | age | created_at |
+----+----------+----------------------+-----+---------------------+
| 1 | 张三 | zhangsan@example.com | 25 | 2026-07-30 10:00:00 |
| 2 | 李四 | lisi@example.com | 30 | 2026-07-30 11:00:00 |
| 3 | 王五 | wangwu@example.com | 28 | 2026-07-30 12:00:00 |
+----+----------+----------------------+-----+---------------------+
3 rows in set (0.00 sec)
19.2 查指定列
SELECT * 虽然方便,但实际开发中不推荐用。原因后面讲。更推荐的做法是指定要查的列:
SELECT username, age FROM users;
只查用户名和年龄两列:
+----------+-----+
| username | age |
+----------+-----+
| 张三 | 25 |
| 李四 | 30 |
| 王五 | 28 |
+----------+-----+
列的顺序由你决定,不必跟建表时的顺序一样:
SELECT age, username FROM users;
这样 age 在前,username 在后。
Tip指定列查询不仅结果更清晰,速度也更快。数据库不需要读取不需要的列,网络传输的数据量也少。养成习惯:查什么列就写什么列,别偷懒用
*。
19.3 SELECT * 的利弊
SELECT * 查所有列,看起来省事,但有几个问题:
| 问题 | 说明 |
|---|---|
| 性能浪费 | 查了一堆不需要的列,白白消耗 IO 和网络带宽 |
| 表结构变了就出事 | 加了新列后,SELECT * 返回的列数变了,依赖列顺序的代码会崩 |
| 索引失效 | 有些查询本可以走覆盖索引(只查索引列就够),SELECT * 强制回表 |
| 传输膨胀 | TEXT/BLOB 大字段被查出来,严重拖慢查询 |
什么时候可以用 SELECT *:
- 在命令行里快速看一眼表数据
- 写临时脚本快速验证
- 表只有两三列且数据量很小
Warning生产代码里、API 接口里、视图定义里,不要用
SELECT *。我之前接手过一个项目,接口返回SELECT *,后来表加了个 BLOB 字段存头像,接口响应时间从 50ms 涨到 2 秒。
19.4 DISTINCT:去掉重复行
查询结果里如果有重复的行,可以用 DISTINCT 去重。
比如想看看 users 表里都有哪些不同的年龄:
SELECT DISTINCT age FROM users;
不加 DISTINCT 的结果可能有重复(多个用户同岁),加了之后每种年龄只出现一次。
多列去重
DISTINCT 后面可以跟多列,去重的是这些列的组合:
SELECT DISTINCT age, username FROM users;
只有 age 和 username 都完全相同的行才算重复,才会被去掉。
Note
DISTINCT是对整行结果去重,不是对某一列去重。它作用于SELECT后面所有列的组合。如果你只想对一列去重但又要显示其他列,那需要用 GROUP BY,后面章节会讲。
DISTINCT 的性能
DISTINCT 需要对结果排序去重,数据量大时有性能开销。MySQL 内部通常用临时表来处理去重。
如果列上有索引,去重会快一些,因为索引本身就是有序的。
-- 如果 email 上有索引,这个去重会走索引,比较快
SELECT DISTINCT email FROM users;
Tip| 如果只是想知道某列有多少个不同的值,用
COUNT(DISTINCT 列名)比先 DISTINCT 再 COUNT 更高效。这在聚合函数章节会讲。
19.5 列别名(AS)
查出来的列名默认就是表里的字段名。但有时候你想给列起个更好看或更有意义的名字,用 AS 关键字:
SELECT username AS 用户名, age AS 年龄 FROM users;
结果就变成:
+--------+--------+
| 用户名 | 年龄 |
+--------+--------+
| 张三 | 25 |
| 李四 | 30 |
| 王五 | 28 |
+--------+--------+
AS 可以省略,效果一样:
SELECT username 用户名, age 年龄 FROM users;
但建议写上 AS,可读性更好。
别名在以下场景特别有用:
- 计算列没有名字,需要起一个
- 列名太长或不够直观
- 多表连接时有同名列需要区分
-- 计算列起别名
SELECT username, amount * 0.9 AS 折后金额 FROM orders;
-- 函数结果起别名
SELECT username, UPPER(email) AS 大写邮箱 FROM users;
Warning别名只在结果集中有效,不能在 WHERE 子句里用。因为 SQL 执行时 WHERE 比 SELECT 先执行,那时候别名还没生成。这个执行顺序后面会反复提到。
19.6 表别名
除了给列起别名,还能给表起别名。在多表查询时特别有用,能省很多字:
SELECT u.username, u.age
FROM users AS u
WHERE u.age > 25;
users AS u 给 users 表起了个短名 u,后面引用列时写 u.username 就行。
AS 同样可以省略:
SELECT u.username, u.age
FROM users u
WHERE u.age > 25;
Tip多表查询时一定要用表别名,不然每列都要写完整的
表名.列名,SQL 会又长又难读。别名要短且有意义,比如 users 用 u、orders 用 o。
19.7 查询中的表达式和函数
SELECT 不只能查列,还能写表达式和调用函数:
-- 算术运算
SELECT username, age, age * 365 AS 出生天数 FROM users;
-- 字符串函数
SELECT username, CONCAT(username, '@example.com') AS 推测邮箱 FROM users;
-- 日期函数
SELECT username, YEAR(created_at) AS 注册年份 FROM users;
-- 条件表达式
SELECT username,
CASE WHEN age >= 30 THEN '资深'
WHEN age >= 25 THEN '中级'
ELSE '新人'
END AS 级别
FROM users;
这些在后面的函数章节会详细讲。这里只要知道:SELECT 后面能放的东西很灵活,不限于列名。
19.8 SELECT 的执行顺序
虽然 SQL 是先写 SELECT 后写 FROM,但 MySQL 执行时的顺序不是这样的:
1. FROM -- 先确定从哪张表取数据
2. WHERE -- 过滤行
3. GROUP BY -- 分组
4. HAVING -- 过滤分组
5. SELECT -- 选择列、计算表达式
6. ORDER BY -- 排序
7. LIMIT -- 截取行数
理解这个顺序很重要。比如为什么 WHERE 里不能用列别名?因为 WHERE 在 SELECT 之前执行,别名还没生成。而 ORDER BY 在 SELECT 之后,所以 ORDER BY 里可以用别名。
-- 这个会报错:WHERE 里不能用别名
SELECT age * 365 AS 出生天数 FROM users WHERE 出生天数 > 9000;
-- 这个没问题:ORDER BY 里可以用别名
SELECT age * 365 AS 出生天数 FROM users ORDER BY 出生天数 DESC;
Note记住这个执行顺序,后面学 WHERE、GROUP BY、ORDER BY 时都会用到。很多 SQL 报错就是因为不理解执行顺序,在不该用的地方用了别名或聚合函数。
19.9 小结
SELECT 是查询的起点。核心知识点:指定列查询优于 SELECT *、DISTINCT 去重作用于所有列的组合、AS 给列和表起别名让结果更清晰、理解 SELECT 的执行顺序能帮你避免很多坑。下一章我们给查询加上条件,学 WHERE 和运算符。