SELECT
导学
SELECT 是 SQL 中使用频率最高的语句,也是你"从数据库中获取数据"的唯一途径。本节从最简单的全表查询开始,逐步拆解 SELECT 的核心语法结构。
定义
SELECT:DQL(数据查询语言)语句,用于从一个或多个表中检索数据。它指定了要返回的列、数据来源、筛选条件、排序方式和结果限制。
核心语法
SELECT [DISTINCT] 列1, 列2, ...
FROM 表名
[WHERE 条件]
[ORDER BY 列 [ASC|DESC]]
[LIMIT 数量];
演示数据准备
为了让你看到每一步的真实效果,我们先建立一张员工表并插入数据。
DROP TABLE IF EXISTS employees;
CREATE TABLE employees (
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(20),
dept VARCHAR(20),
score DECIMAL(5,2)
);
INSERT INTO employees (emp_name, dept, score) VALUES
('大翔', '技术部', 100),
('白歌', '技术部', NULL);
当前 employees 表中的完整数据如下:
| emp_id | emp_name | dept | score |
|---|---|---|---|
| 1 | 大翔 | 技术部 | 100 |
| 2 | 白歌 | 技术部 | NULL |
SQL 示例
场景一:查询所有列(开发调试用,生产慎用)
当前数据状态:见上文 employees 表完整数据。
执行语句:
SELECT * FROM employees;
操作后结果:
| emp_id | emp_name | dept | score |
|---|---|---|---|
| 1 | 大翔 | 技术部 | 100 |
| 2 | 白歌 | 技术部 | NULL |
结果解读:* 是通配符,表示返回所有列。在开发阶段方便快速查看数据,但在生产环境的应用程序代码中应明确指定列名,原因有三:
- 减少网络传输(多余的列浪费带宽)
- 避免表结构变更导致程序出错
- 利用覆盖索引优化查询(后续章节详解)
场景二:查询指定列(推荐)
当前数据状态:见上文 employees 表完整数据。
执行语句:
SELECT emp_name, dept, score FROM employees;
操作后结果:
| emp_name | dept | score |
|---|---|---|
| 大翔 | 技术部 | 100 |
| 白歌 | 技术部 | NULL |
结果解读:只返回 emp_name、dept、score 三列,数据量更小,接口更稳定。即使表后面新增了列,这条查询的返回结构也不会改变。
场景三:给列起别名(增强可读性)
当前数据状态:见上文 employees 表完整数据。
执行语句:
SELECT
emp_name AS 姓名,
dept AS 部门,
score AS 绩效分
FROM employees;
操作后结果:
| 姓名 | 部门 | 绩效分 |
|---|---|---|
| 大翔 | 技术部 | 100 |
| 白歌 | 技术部 | NULL |
结果解读:AS 关键字可以省略,但建议保留以增强可读性。别名在结果集中显示为列标题,不影响原始列名。注意:WHERE 子句中不能使用别名,因为 SQL 执行顺序中 WHERE 在 SELECT 之前。
场景四:去重查询
当前数据状态:见上文 employees 表完整数据,其中 dept 列有重复值。
执行语句:
SELECT DISTINCT dept FROM employees;
操作后结果:
| dept |
|---|
| 技术部 |
结果解读:DISTINCT 对 dept 列去重,只保留不同的部门名称。当前表中所有员工都在技术部,去重后只有 1 行。注意:DISTINCT 不是函数,它作用于所有列的组合。如果写成 SELECT DISTINCT dept, score,则表示"部门+绩效分"的组合去重,结果可能更多。
再看一个多列去重的例子:
SELECT DISTINCT dept, score FROM employees;
操作后结果:
| dept | score |
|---|---|
| 技术部 | 100 |
| 技术部 | NULL |
结果解读:(技术部, 100)、(技术部, NULL) 被视为不同的组合,因此都保留。这说明 DISTINCT 是对整行选定列的组合去重,而非单独对某一列去重。
场景五:常量表达式与计算
当前数据状态:见上文 employees 表完整数据。
执行语句:
SELECT
emp_name,
CONCAT(dept, '-精英') AS dept_label,
score * 0.4 + 60 AS adjusted_score
FROM employees;
操作后结果:
| emp_name | dept_label | adjusted_score |
|---|---|---|
| 大翔 | 技术部-精英 | 100.0000 |
| 白歌 | 技术部-精英 | NULL |
结果解读:SELECT 不仅可以选列,还可以写表达式。数据库在返回结果前会计算这些表达式。注意白歌的 adjusted_score 为 NULL,因为 score 为 NULL,任何包含 NULL 的算术运算结果都是 NULL。
场景六:无 FROM 的 SELECT(常量计算)
当前数据状态:无需表数据,直接计算常量表达式。
执行语句:
SELECT 1 + 1;
SELECT VERSION();
SELECT NOW();
SELECT UPPER('hello mysql');
操作后结果:
SELECT 1 + 1;
| 1 + 1 |
|---|
| 2 |
SELECT VERSION();
| VERSION() |
|---|
| 5.7.xxx |
SELECT NOW();
| NOW() |
|---|
| 2026-06-10 15:58:44 |
SELECT UPPER('hello mysql');
| UPPER('hello mysql') |
|---|
| HELLO MYSQL |
结果解读:MySQL 允许无 FROM 子句的 SELECT,常用于计算常量表达式、获取系统信息或测试连接。这在验证函数行为和调试时非常有用。
SELECT 执行顺序(重要)
关键认知:虽然书写顺序是
SELECT ... FROM ... WHERE ...,但执行顺序是FROM → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT。理解这个顺序对写复杂查询和排查错误至关重要。
我们用一张小表验证执行顺序中 WHERE 先于 SELECT:
-- 建表
CREATE TABLE IF NOT EXISTS test_order (
id INT,
val INT
);
INSERT INTO test_order VALUES (1, 10), (2, 20), (3, 30);
当前数据状态:
| id | val |
|---|---|
| 1 | 10 |
| 2 | 20 |
| 3 | 30 |
执行语句:
-- 错误:WHERE 中不能用 SELECT 中定义的别名
SELECT id, val * 2 AS double_val FROM test_order WHERE double_val > 30;
MySQL 会报错:Unknown column 'double_val' in 'where clause'。
正确写法:
SELECT id, val * 2 AS double_val FROM test_order WHERE val * 2 > 30;
操作后结果:
| id | double_val |
|---|---|
| 2 | 40 |
| 3 | 60 |
结果解读:WHERE 在 SELECT 之前执行,此时 double_val 这个别名还不存在,所以 WHERE 中只能使用原始列名或表达式。而 ORDER BY 在 SELECT 之后执行,因此可以使用别名。
常见误区
| 误区 | 正解 |
|---|---|
"SELECT * 最方便,Always 用" | 生产代码应明确列名,* 仅用于临时查询。 |
"DISTINCT 是函数,可以 DISTINCT(dept)" | DISTINCT 是关键字而非函数,正确写法是 SELECT DISTINCT dept。 |
| "别名可以在 WHERE 中使用" | 不行。WHERE 执行在 SELECT 之前,此时别名还未生成。 |
"DISTINCT 只对后面第一个列生效" | DISTINCT 作用于所有选定列的组合。 |
面试考点
Q:为什么生产环境不推荐 SELECT *?
- 增加网络开销;2. 表结构变更可能导致程序字段映射错误;3. 无法利用覆盖索引(查询的列全在索引中时,无需回表查数据行)。
Q:SELECT 语句中别名可以在哪些地方使用?
别名在
SELECT执行后才生效,因此只能用于ORDER BY、HAVING和外部查询,不能用于WHERE、GROUP BY。
Q:SELECT 1+1 不跟 FROM 可以吗?
可以。MySQL 允许无
FROM子句的SELECT,常用于计算常量表达式或测试连接,如SELECT VERSION(), NOW();。
Q:DISTINCT 和 GROUP BY 去重有什么区别?
DISTINCT只是去重,不聚合;GROUP BY会把相同值的行归为一组,通常配合聚合函数使用。在 MySQL 5.7 中,单纯去重时两者结果相同,但DISTINCT语义更清晰。
小结
SELECT是数据查询的核心语句- 生产环境应明确指定列名,避免使用
* - 理解 SQL 的执行顺序(
FROM → WHERE → SELECT → ORDER BY → LIMIT)是写好复杂查询的基础 DISTINCT实现去重,作用于所有选定列的组合- 别名只能在
ORDER BY、HAVING中使用,不能在WHERE、GROUP BY中使用
下一章引子:学会了选列,下一步是学会筛选——只拿你想要的那部分行。