数据库基础:DQL 数据查询语言
前言 :本文是"数据库基础"系列的第六篇。上一篇我们完成了 MySQL 的安装和基础操作,学会了创建数据库、创建表和简单的增删改查。本篇我们将正式进入 SQL 语言中最核心的部分------DQL 数据查询语言(Data Query Language)。在数据库操作中,查询是最频繁、最复杂的操作,掌握 SELECT 语句的各种用法,是数据库开发的必备技能。
目录
- 一、数据准备
- 二、基础查询
- 三、条件查询
- 四、排序查询
- 五、常见函数
- 六、分组函数
- 七、分组查询
- 八、连接查询
- 九、子查询
- 十、分页查询
- 十一、union 联合查询
- 十二、总结
一、数据准备
1.1 导入数据
本篇章使用 myemployees.sql 示例数据库,包含员工、部门、位置、工种四张表。
Navicat 导入步骤:
| 步骤 | 操作 |
|---|---|
| 1 | 打开 Navicat,连接到 MySQL(master) |
| 2 | 右键点击连接 → 选择"新建数据库",命名为 myemployees,字符集选择 utf8mb4 |
| 3 | 双击打开 myemployees 数据库(高亮显示) |
| 4 | 右键点击 myemployees → 选择"运行 SQL 文件" |
| 5 | 选择 myemployees.sql 文件,点击"开始" |
| 6 | 等待执行完成,刷新表列表 |
命令行导入方式(备选):
bash
mysql -uroot -p myemployees < /root/myemployees.sql
1.2 表结构说明
员工表(employees)
| 字段 | 类型 | 长度 | 备注 |
|---|---|---|---|
employee_id |
int | - | 员工编号 |
first_name |
varchar | 20 | 名 |
last_name |
varchar | 25 | 姓 |
email |
varchar | 25 | 邮箱 |
phone_number |
varchar | 20 | 电话号码 |
job_id |
varchar | 10 | 工作编号 |
salary |
double | 10,2 | 月薪 |
commission_pct |
double | 4,2 | 奖金率 |
manager_id |
int | - | 上级领导的员工编号 |
department_id |
int | - | 部门编号 |
hiredate |
datetime | - | 入职日期 |
部门表(departments)
| 字段 | 类型 | 长度 | 备注 |
|---|---|---|---|
department_id |
int | - | 部门编号 |
department_name |
varchar | 3 | 部门名称 |
manager_id |
int | - | 部门领导的员工编号 |
location_id |
int | - | 位置编号 |
工种表(jobs)
| 字段 | 类型 | 长度 | 备注 |
|---|---|---|---|
job_id |
varchar | 10 | 工种编号 |
job_title |
varchar | 35 | 工种名称 |
min_salary |
int | - | 最低工资 |
max_salary |
int | - | 最高工资 |
位置表(locations)
| 字段 | 类型 | 长度 | 备注 |
|---|---|---|---|
location_id |
int | - | 位置编号 |
street_address |
varchar | 40 | 街道 |
postal_code |
varchar | 12 | 邮编 |
city |
varchar | 30 | 城市 |
state_province |
varchar | 25 | 州/省 |
country_id |
varchar | 2 | 国家编号 |
二、基础查询
2.1 基本语法
sql
SELECT 查询列表 FROM 表名;
语法说明:
| 部分 | 说明 |
|---|---|
SELECT |
标识选择哪些列(字段、常量、表达式、函数) |
FROM |
标识从哪个表中查询 |
查询列表 |
可以是表中的字段、常量值、表达式、函数 |
特点:
- 查询列表可以是:表中的字段、常量值、表达式、函数
- 查询的结果是一个虚拟的表格,不会改变原表数据
2.2 查询单个字段
sql
-- 查询员工表中的姓名字段
SELECT last_name FROM employees;
2.3 查询多个字段
sql
-- 查询员工表中的员工编号、名、姓
SELECT employee_id, first_name, last_name FROM employees;
2.4 查询所有字段
方式一:列出所有字段
sql
SELECT employee_id, first_name, last_name, email, phone_number,
job_id, salary, commission_pct, manager_id, department_id, hiredate
FROM employees;
方式二 :使用 * 通配符
sql
SELECT * FROM employees;
建议:在开发中建议写明具体字段名,避免查询不需要的数据,影响性能。
2.5 查询常量值
sql
-- 查询数值常量
SELECT 66;
-- 查询字符串常量
SELECT "bigdata";
2.6 查询表达式
sql
-- 查询表达式计算结果
SELECT 100 * 2;
2.7 查询函数
sql
-- 查询当前 MySQL 版本
SELECT VERSION();
2.8 起别名(AS)
作用:
- 便于理解
- 如果查询的字段有重名的情况,使用别名可以区分开来
方式一 :使用 AS 关键字
sql
-- 给表达式起别名
SELECT 100 * 2 AS 结果;
-- 给字段起别名
SELECT first_name AS 名, last_name AS 姓 FROM employees;
方式二:使用空格
sql
SELECT first_name 名, last_name 姓 FROM employees;
特殊场景:别名中包含特殊字符(如空格),需要加引号
sql
-- 查询 salary,显示结果为 "out put"(包含空格)
SELECT salary AS "out put" FROM employees;
2.9 去重(DISTINCT)
sql
-- 查询员工表中涉及到的所有部门编号(去重)
SELECT DISTINCT department_id FROM employees;
2.10 + 号的作用
MySQL 中的 + 号只有一个功能:运算符。
规则:
| 情况 | 结果 |
|---|---|
| 两个操作数都为数值型 | 做加法运算,如 SELECT 66 + 99; |
| 一方为字符型,可转换为数值 | 转换后做加法运算,如 SELECT '88' + 66; |
| 一方为字符型,不可转换 | 将字符型转换为 0 再做运算,如 SELECT 'abc' + 66; |
| 一方为 NULL | 结果必定为 NULL,如 SELECT NULL + 10; |
sql
-- 查询员工名和姓连成一个字段,显示为姓名
-- 错误写法(+ 不能拼接字符串)
SELECT last_name + first_name AS 姓名 FROM employees;
-- 正确写法(使用 CONCAT 函数)
SELECT CONCAT(last_name, first_name) AS 姓名 FROM employees;
-- 拼接示例
SELECT CONCAT('h', 'e', 'l', 'l', 'o') AS 结果;
三、条件查询(WHERE)
3.1 基本语法
sql
SELECT 查询列表
FROM 表名
WHERE 筛选条件;
执行顺序 :FROM → WHERE → SELECT
3.2 筛选条件分类
| 分类 | 说明 | 运算符 |
|---|---|---|
| 按条件表达式筛选 | 使用比较运算符进行筛选 | >、<、=、!=(<>)、>=、<= |
| 按逻辑表达式筛选 | 使用逻辑运算符连接多个条件 | AND(&&)、OR(` |
| 模糊查询 | 使用关键字进行模糊匹配 | LIKE、BETWEEN AND、IN、IS NULL |
3.3 按条件表达式筛选
sql
-- 查询工资大于 12000 的员工信息
SELECT *
FROM employees
WHERE salary > 12000;
-- 查询部门编号不等于 90 的员工
SELECT last_name, department_id
FROM employees
WHERE department_id <> 90;
3.4 按逻辑表达式筛选
| 逻辑运算符 | 说明 |
|---|---|
AND(&&) |
两个条件都为 TRUE,结果为 TRUE |
OR(` |
|
NOT(!) |
如果条件本身为 FALSE,结果为 TRUE |
sql
-- 查询工资在 10000 到 20000 之间的员工
SELECT last_name, salary
FROM employees
WHERE salary >= 10000 AND salary <= 20000;
-- 查询部门编号为 90 或 100 的员工
SELECT last_name, department_id
FROM employees
WHERE department_id = 90 OR department_id = 100;
3.5 模糊查询
LIKE
特点:一般和通配符搭配使用。
| 通配符 | 说明 |
|---|---|
% |
匹配任意多个字符(包含 0 个字符) |
_ |
匹配任意单个字符 |
sql
-- 查询员工名中包含字符 a 的员工信息
SELECT *
FROM employees
WHERE first_name LIKE '%a%';
-- 查询员工名中第三个字符为 e,第五个字符为 a 的员工名和工资
SELECT first_name, salary
FROM employees
WHERE first_name LIKE '__e_a%';
-- 查询员工姓中第二个字符为 _ 的员工名(使用 \ 转义)
SELECT last_name
FROM employees
WHERE last_name LIKE '_\_%';
-- 使用 ESCAPE 指定转义字符
SELECT last_name
FROM employees
WHERE last_name LIKE '_$_%' ESCAPE '$';
说明 :
ESCAPE关键字用于指定一个字符替代\的转义作用。
BETWEEN AND
特点:
- 使用
BETWEEN AND可以提高语句的简洁度 - 包含临界值
- 两个临界值不要调换顺序(必须小值在前,大值在后)
sql
-- 查询员工编号在 100 到 120 之间的员工信息
SELECT *
FROM employees
WHERE employee_id BETWEEN 100 AND 120;
IN
特点:
- 判断某个字段的值是否属于
IN列表中的某一项 - 使用
IN提高语句简洁度 IN列表的值类型必须一致或兼容
sql
-- 查询员工的工种编号是 IT_PROG、AD_VP、AD_PRES 中的一个
SELECT first_name, job_id
FROM employees
WHERE job_id IN ('IT_PROG', 'AD_VP', 'AD_PRES');
IS NULL
特点:
=或<>不能用于判断NULL值IS NULL或IS NOT NULL可以判断NULL值
sql
-- 查询没有奖金的员工名和奖金率
SELECT first_name, commission_pct
FROM employees
WHERE commission_pct IS NULL;
-- 查询有奖金的员工名和奖金率
SELECT first_name, commission_pct
FROM employees
WHERE commission_pct IS NOT NULL;
3.6 安全等于(<=>)
<=> 是 MySQL 特有的安全等于运算符,既可以判断 NULL 值,也可以判断普通数值。
sql
-- 查询没有奖金的员工(使用 <=>)
SELECT first_name, commission_pct
FROM employees
WHERE commission_pct <=> NULL;
-- 查询工资为 12000 的员工
SELECT *
FROM employees
WHERE salary <=> 12000;
IS NULL 与 <=> 对比:
| 对比项 | IS NULL |
<=> |
|---|---|---|
| 可读性 | 高 | 低 |
| 判断 NULL | 可以 | 可以 |
| 判断普通值 | 不可以 | 可以 |
建议 :可读性较高,建议使用
IS NULL判断NULL值。
四、排序查询(ORDER BY)
4.1 基本语法
sql
SELECT 字段列表 FROM 表名 ORDER BY 排序字段 [ASC|DESC];
| 关键字 | 说明 |
|---|---|
ASC |
升序(默认) |
DESC |
降序 |
4.2 单字段排序
sql
-- 按工资降序排列
SELECT last_name, salary
FROM employees
ORDER BY salary DESC;
-- 按工资升序排列(默认)
SELECT last_name, salary
FROM employees
ORDER BY salary ASC;
4.3 多字段排序
sql
-- 先按部门编号升序,再按工资降序
SELECT last_name, department_id, salary
FROM employees
ORDER BY department_id ASC, salary DESC;
五、常见函数
5.1 字符函数
LENGTH
sql
-- 获取字符串长度
SELECT LENGTH('hello') AS 长度;
-- 获取姓名的长度
SELECT last_name, LENGTH(last_name) FROM employees;
CONCAT
sql
-- 拼接字符串
SELECT CONCAT(last_name, '的邮箱是', email) FROM employees;
UPPER / LOWER
sql
-- 大小写转换
SELECT UPPER('hello') AS 大写, LOWER('HELLO') AS 小写;
-- 将姓转为大写,名转为小写
SELECT UPPER(last_name), LOWER(first_name) FROM employees;
SUBSTR
sql
-- 截取子串(从第1位开始取3个字符)
SELECT SUBSTR('hello world', 1, 3) AS 截取结果;
-- 取姓名的前3个字符
SELECT SUBSTR(last_name, 1, 3) FROM employees;
REPLACE
sql
-- 替换字符串
SELECT REPLACE('hello world', 'world', 'mysql') AS 替换结果;
-- 将邮箱中的'g'替换为'G'
SELECT REPLACE(email, 'g', 'G') FROM employees;
5.2 数学函数
sql
-- 取绝对值
SELECT ABS(-10) AS 绝对值;
-- 向上取整
SELECT CEIL(3.14) AS 向上取整;
-- 向下取整
SELECT FLOOR(3.99) AS 向下取整;
-- 四舍五入
SELECT ROUND(3.14159, 2) AS 四舍五入;
-- 取随机数(0-1之间)
SELECT RAND() AS 随机数;
5.3 日期函数
sql
-- 获取当前日期和时间
SELECT NOW() AS 当前日期时间;
-- 获取当前日期
SELECT CURDATE() AS 当前日期;
-- 获取当前时间
SELECT CURTIME() AS 当前时间;
-- 获取年月日
SELECT YEAR(NOW()) AS 年, MONTH(NOW()) AS 月, DAY(NOW()) AS 日;
-- 查询入职于1992年的员工
SELECT last_name, hiredate
FROM employees
WHERE YEAR(hiredate) = 1992;
-- 日期计算:入职满10年
SELECT last_name, hiredate,
DATE_ADD(hiredate, INTERVAL 10 YEAR) AS 满10年日期
FROM employees;
5.4 流程控制函数
IF
sql
-- IF(条件, 真值, 假值)
SELECT last_name, salary,
IF(salary > 10000, '高薪', '普通') AS 薪资等级
FROM employees;
CASE
sql
-- CASE WHEN 条件 THEN 结果 ELSE 结果 END
SELECT last_name, salary,
CASE
WHEN salary > 15000 THEN 'A'
WHEN salary > 10000 THEN 'B'
WHEN salary > 5000 THEN 'C'
ELSE 'D'
END AS 薪资等级
FROM employees;
六、分组函数
6.1 分组函数列表
| 函数 | 说明 |
|---|---|
COUNT() |
统计行数 |
SUM() |
求和 |
AVG() |
求平均值 |
MAX() |
求最大值 |
MIN() |
求最小值 |
6.2 案例实操
sql
-- 统计员工总数
SELECT COUNT(*) FROM employees;
-- 统计有部门编号的员工数(不包含 NULL)
SELECT COUNT(department_id) FROM employees;
-- 计算员工的平均工资
SELECT AVG(salary) FROM employees;
-- 计算员工的最高工资
SELECT MAX(salary) FROM employees;
-- 计算员工的最低工资
SELECT MIN(salary) FROM employees;
-- 计算员工的工资总和
SELECT SUM(salary) FROM employees;
-- 同时查询多个统计值
SELECT
COUNT(*) AS 人数,
AVG(salary) AS 平均工资,
MAX(salary) AS 最高工资,
MIN(salary) AS 最低工资,
SUM(salary) AS 工资总和
FROM employees;
七、分组查询(GROUP BY)
7.1 基本语法
sql
SELECT 分组字段, 聚合函数 FROM 表名 GROUP BY 分组字段;
7.2 案例实操
sql
-- 按部门分组,统计每个部门的员工数
SELECT department_id, COUNT(*) AS 人数
FROM employees
GROUP BY department_id;
-- 按部门分组,计算每个部门的平均工资
SELECT department_id, AVG(salary) AS 平均工资
FROM employees
GROUP BY department_id;
-- 按职位分组,统计每个职位的最高工资
SELECT job_id, MAX(salary) AS 最高工资
FROM employees
GROUP BY job_id;
7.3 HAVING(分组后的条件筛选)
语法:
sql
SELECT 分组字段, 聚合函数
FROM 表名
GROUP BY 分组字段
HAVING 条件;
WHERE在分组前筛选,HAVING在分组后筛选。
案例实操:
sql
-- 查询平均工资大于 10000 的部门
SELECT department_id, AVG(salary) AS 平均工资
FROM employees
GROUP BY department_id
HAVING AVG(salary) > 10000;
-- 查询员工数大于 5 的部门
SELECT department_id, COUNT(*) AS 人数
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 5;
八、连接查询(JOIN)
8.1 等值连接(旧语法)
sql
-- 查询员工的姓名和所在部门的名称
SELECT e.last_name, d.department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id;
8.2 INNER JOIN(内连接)
语法:
sql
SELECT 字段列表
FROM 表1
INNER JOIN 表2 ON 连接条件;
案例实操:
sql
-- 查询员工的姓名、部门名称和职位名称
SELECT e.last_name, d.department_name, j.job_title
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
INNER JOIN jobs j ON e.job_id = j.job_id;
8.3 LEFT JOIN(左外连接)
语法:
sql
SELECT 字段列表
FROM 表1
LEFT JOIN 表2 ON 连接条件;
说明:返回左表所有记录,右表无匹配则显示 NULL。
案例实操:
sql
-- 查询所有部门及其员工人数(包括空部门)
SELECT d.department_name, COUNT(e.employee_id) AS 人数
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id;
-- 查询没有员工的部门
SELECT d.department_name
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
WHERE e.employee_id IS NULL;
8.4 RIGHT JOIN(右外连接)
语法:
sql
SELECT 字段列表
FROM 表1
RIGHT JOIN 表2 ON 连接条件;
说明:返回右表所有记录,左表无匹配则显示 NULL。
九、子查询
9.1 WHERE 子句中的子查询
sql
-- 查询工资高于平均工资的员工
SELECT last_name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
9.2 使用 IN 的子查询
sql
-- 查询部门名称中包含 'IT' 的部门的员工
SELECT last_name, department_id
FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE department_name LIKE '%IT%'
);
9.3 FROM 子句中的子查询(派生表)
sql
-- 查询每个部门的平均工资,再按平均工资排序
SELECT department_id, avg_salary
FROM (
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
) AS dept_avg
ORDER BY avg_salary DESC;
9.4 SELECT 子句中的子查询(标量子查询)
sql
-- 查询每个员工的姓名及其部门的平均工资
SELECT
last_name,
salary,
(SELECT AVG(salary) FROM employees e2 WHERE e2.department_id = e1.department_id) AS 部门平均工资
FROM employees e1;
十、分页查询(LIMIT)
10.1 基本语法
sql
SELECT 字段列表 FROM 表名 LIMIT 起始索引, 条数;
| 参数 | 说明 |
|---|---|
起始索引 |
从第几条开始(从 0 开始) |
条数 |
每页显示多少条 |
10.2 案例实操
sql
-- 查询前 5 条数据
SELECT * FROM employees LIMIT 5;
-- 查询第 6 到第 10 条数据
SELECT * FROM employees LIMIT 5, 5;
-- 分页公式:LIMIT (页数-1)*每页条数, 每页条数
-- 第1页(每页5条)
SELECT * FROM employees LIMIT 0, 5;
-- 第2页
SELECT * FROM employees LIMIT 5, 5;
-- 第3页
SELECT * FROM employees LIMIT 10, 5;
十一、UNION 联合查询
11.1 基本语法
sql
SELECT 字段列表 FROM 表1
UNION
SELECT 字段列表 FROM 表2;
11.2 案例实操
sql
-- 查询部门编号为 80 的员工姓名和部门编号为 90 的员工姓名
SELECT last_name, department_id FROM employees WHERE department_id = 80
UNION
SELECT last_name, department_id FROM employees WHERE department_id = 90;
-- 查询工资大于 10000 的员工和奖金率为空的员工
SELECT last_name, salary, commission_pct FROM employees WHERE salary > 10000
UNION
SELECT last_name, salary, commission_pct FROM employees WHERE commission_pct IS NULL;
十二、总结
核心知识点速查
| 知识点 | 关键语法 |
|---|---|
| 基础查询 | SELECT 字段 FROM 表名 |
| 条件查询 | WHERE 条件 |
| 模糊查询 | LIKE '%模式%' |
| 排序 | ORDER BY 字段 DESC |
| 分组统计 | GROUP BY 字段 |
| 分组筛选 | HAVING 条件 |
| 内连接 | INNER JOIN ... ON |
| 左连接 | LEFT JOIN ... ON |
| 子查询 | SELECT ... FROM (...) AS 别名 |
| 分页查询 | LIMIT 起始索引, 条数 |
| 联合查询 | UNION |
注意事项
| 要点 | 说明 |
|---|---|
WHERE vs HAVING |
WHERE 分组前筛选,HAVING 分组后筛选 |
COUNT(*) vs COUNT(字段) |
COUNT(*) 包含 NULL,COUNT(字段) 不包含 NULL |
| 别名 | AS 可省略,但建议保留 |
| 模糊查询性能 | % 开头的模糊查询不会使用索引 |
| 分页查询 | LIMIT 是 MySQL 特有语法,其他数据库不通用 |
| UNION | 默认去重,UNION ALL 不去重效率更高 |
系列直达
- 上篇 :数据库基础:MySQL 安装与基础操作
- 本篇:数据库基础:DQL 数据查询语言(本文)
- 下篇:敬请期待