感谢阅读!❤️
如果这篇文章对你有帮助,欢迎 **点赞** 👍 和 **关注** ⭐,获取更多实用技巧和干货内容!你的支持是我持续创作的动力!
**关注我,不错过每一篇精彩内容!**
目录
- 内容介绍
-
- [一、🧠什么是 SQL?](#一、🧠什么是 SQL?)
-
- [SQL 的主要分类](#SQL 的主要分类)
- [二、🐳环境搭建:Docker 快速部署 MySQL](#二、🐳环境搭建:Docker 快速部署 MySQL)
- 三、🗃️数据库基本操作
- 四、🧪测试数据准备
- 五、🔍DQL:数据查询语言详解
-
- [1. 简单查询](#1. 简单查询)
- [2. 条件查询(WHERE)](#2. 条件查询(WHERE))
- [3. 排序(ORDER BY)](#3. 排序(ORDER BY))
- [4. 去重(DISTINCT)](#4. 去重(DISTINCT))
- 六、🧮常用函数
- [七、📊分组与聚合(GROUP BY + 聚合函数)](#七、📊分组与聚合(GROUP BY + 聚合函数))
- 八、🔗连接查询(JOIN)
-
- [1. 内连接(INNER JOIN)](#1. 内连接(INNER JOIN))
- [2. 外连接](#2. 外连接)
- [3. 多表连接](#3. 多表连接)
- 九、📦子查询(嵌套查询)
-
- [1. WHERE 中的子查询](#1. WHERE 中的子查询)
- [2. FROM 中的子查询(派生表)](#2. FROM 中的子查询(派生表))
- [3. SELECT 中的子查询](#3. SELECT 中的子查询)
- [4. EXISTS / NOT EXISTS](#4. EXISTS / NOT EXISTS)
- [十、🧩UNION 和 UNION ALL](#十、🧩UNION 和 UNION ALL)
- [十一、⏳SQL 执行顺序(重要!)](#十一、⏳SQL 执行顺序(重要!))
- 十二、🔚结语
内容介绍
本文旨在帮助初学者系统掌握 SQL 的核心语法、常用操作及实战技巧。无论你是刚接触数据库的新手,还是希望巩固基础的开发者,这篇文章都将为你提供清晰、实用的参考。
一、🧠什么是 SQL?
SQL(Structured Query Language) 是用于管理关系型数据库的标准语言。它主要用于对数据库中的数据进行查询、插入、更新、删除等操作,并可控制用户权限、事务处理等。
SQL 的主要分类
| 类别 | 全称 | 常用语句 | 功能说明 |
|---|---|---|---|
| DQL | 数据查询语言 | SELECT |
查询数据 |
| DDL | 数据定义语言 | CREATE, DROP, ALTER |
定义或修改数据库结构 |
| DML | 数据操作语言 | INSERT, UPDATE, DELETE |
操作表中数据 |
| DCL | 数据控制语言 | GRANT, REVOKE |
控制用户权限 |
| TPL | 事务处理语言 | BEGIN, COMMIT, ROLLBACK |
管理事务 |
| CCL | 游标控制语言 | DECLARE CURSOR, FETCH |
行级操作(较少使用) |
二、🐳环境搭建:Docker 快速部署 MySQL
没安装Docker的小伙伴,可以参考我的另一篇博客:在 Ubuntu 上安装 Docker 并部署 MySQL 容器
bash
docker run -d \
--name mysql-container \
-e MYSQL_ROOT_PASSWORD=123 \
-e MYSQL_DATABASE=mydb \
-e MYSQL_USER=meme \
-e MYSQL_PASSWORD=123 \
-p 3306:3306 \
-v /mysql-data:/var/lib/mysql \
--restart=unless-stopped \
mysql:8.4.7
登录方式
-
本地登录 :
bashmysql -u root -p 123 -
远程登录 :
bashmysql -u meme -h 127.0.0.1 -p 123
三、🗃️数据库基本操作
sql
SHOW DATABASES; -- 查看所有数据库
CREATE DATABASE mydb; -- 创建数据库
USE mydb; -- 使用数据库
SHOW TABLES; -- 查看当前数据库所有表
SELECT DATABASE(); -- 查看当前使用的数据库
DROP DATABASE testdb; -- 删除数据库
SELECT VERSION(); -- 查看 MySQL 版本
四、🧪测试数据准备
为便于演示,我们创建三张表:DEPT(部门)、EMP(员工)、SALGRADE(薪资等级)。这些数据将贯穿全文示例。
sql
-- 测试数据:DEPT、EMP和SALGRADE表
DROP TABLE IF EXISTS DEPT;
DROP TABLE IF EXISTS EMP;
DROP TABLE IF EXISTS SALGRADE;
CREATE TABLE DEPT(
DEPTID INT(8) NOT NULL,
DNAME VARCHAR(10),
LOCATION VARCHAR(20),
PRIMARY KEY(DEPTID)
);
CREATE TABLE EMP(
EMPID INT(8) NOT NULL,
ENAME VARCHAR(14),
JOB VARCHAR(10),
MANAGER INT(8),
HIREDATE DATE DEFAULT NULL,
SAL DOUBLE(8,2),
COMM DOUBLE(8,2),
DEPTID INT(8),
PRIMARY KEY(EMPID)
);
CREATE TABLE SALGRADE(
GRADE INT,
LOSAL INT,
HISAL INT
);
INSERT INTO DEPT (DEPTID, DNAME, LOCATION) VALUES
(1001, '研发部', '北京'),
(1002, '市场部', '上海'),
(1003, '人事部', '深圳'),
(1004, '财务部', '广州'),
(1005, '运维部', '成都');
INSERT INTO EMP (EMPID, ENAME, JOB, MANAGER, HIREDATE, SAL, COMM, DEPTID) VALUES
(1001, 'Jack', 'CEO', NULL, '2018-03-15', 25000.00, NULL, 1005),
(1002, '李娜', '部门经理', 1001, '2019-01-10', 18000.00, NULL, 1002),
(1003, '王强', '高级工程师', 1002, '2019-05-22', 15000.00, NULL, 1002),
(1004, '刘芳', '工程师', 1002, '2020-02-14', 12000.00, NULL, 1002),
(1005, '陈静', '市场经理', 1001, '2019-07-30', 16000.00, 2000.00, 1003),
(1006, '赵磊', '市场专员', 1005, '2020-08-12', 8000.00, 1500.00, 1003),
(1007, '黄敏', '市场专员', 1005, '2021-03-05', 8200.00, 1600.00, 1003),
(1008, '周涛', '生产主管', 1001, '2019-11-18', 14000.00, NULL, 1004),
(1009, '吴倩', '生产员', 1008, '2020-06-20', 6500.00, NULL, 1004),
(1010, '徐浩', '生产员', 1008, '2021-01-11', 6600.00, NULL, 1004),
(1011, '孙莉', '行政主管', 1001, '2019-04-03', 13000.00, NULL, 1005),
(1012, '胡军', '行政助理', 1011, '2020-09-17', 7000.00, NULL, 1005),
(1013, '郭婷', '宣传主管', 1001, '2020-01-20', 12500.00, NULL, 1001),
(1014, '何杰', '文案策划', 1013, '2021-05-14', 9000.00, NULL, 1001),
(1015, '林雪', '平面设计', 1013, '2021-07-22', 9200.00, NULL, 1001),
(1016, '高翔', '软件工程师', 1002, '2020-11-30', 13000.00, NULL, 1002),
(1017, '郑悦', '测试工程师', 1002, '2021-02-18', 11000.00, NULL, 1002),
(1018, '谢鹏', '运维工程师', 1002, '2021-04-09', 11500.00, NULL, 1002),
(1019, '冯琳', '销售经理', 1001, '2019-08-25', 17000.00, 3000.00, 1003),
(1020, '邓超', '销售代表', 1019, '2020-10-10', 8500.00, 2500.00, 1003),
(1021, '曹阳', '销售代表', 1019, '2021-06-15', 8600.00, 2600.00, 1003),
(1022, '彭丽', 'HR专员', 1011, '2020-12-01', 7500.00, NULL, 1005),
(1023, '韩梅', '前台接待', 1011, '2021-08-03', 6000.00, NULL, 1005),
(1024, '曾伟', '研发经理', 1001, '2019-02-14', 18500.00, NULL, 1002),
(1025, '吕鑫', '算法工程师', 1024, '2020-07-22', 16000.00, NULL, 1002),
(1026, '苏晴', 'UI设计师', 1013, '2021-09-10', 9500.00, NULL, 1001),
(1027, '卢峰', '产品经理', 1001, '2019-10-05', 17500.00, NULL, 1002),
(1028, '蒋华', '数据分析师', 1027, '2020-05-19', 12000.00, NULL, 1002),
(1029, '蔡敏', '客服专员', 1011, '2021-11-20', 6200.00, NULL, 1005),
(1030, '任飞', '仓储管理员', 1008, '2022-01-15', 6300.00, NULL, 1004),
(1031, '杜娟', '采购专员', 1011, '2022-03-08', 7200.00, NULL, 1005),
(1032, '秦朗', '新媒体运营', 1013, '2022-04-12', 8800.00, NULL, 1001),
(1033, '江涛', '后端开发', 1002, '2022-02-28', 14000.00, NULL, 1002),
(1034, '史燕', '前端开发', 1002, '2022-05-17', 13500.00, NULL, 1002),
(1035, '顾明', '安全工程师', 1002, '2022-06-30', 14500.00, NULL, 1002),
(1036, '侯丽', '新媒体运营', 1013, '2022-07-14', 8500.00, NULL, 1001),
(1037, '邵强', '渠道销售', 1019, '2022-08-22', 8700.00, 2800.00, 1003),
(1038, '万婷', '电商运营', 1019, '2022-09-05', 9000.00, 2200.00, 1003),
(1039, '段磊', '设备维护', 1008, '2022-10-11', 6800.00, NULL, 1004),
(1040, '雷霞', '前端开发', 1008, '2022-11-18', 6700.00, NULL, 1004),
(1041, '钱勇', '前端开发', 1011, '2023-01-09', 7800.00, NULL, 1005),
(1042, '汤敏', '培训专员', 1011, '2023-02-14', 7600.00, NULL, 1005),
(1043, '尹浩', '实习生', NULL, '2023-03-01', 4000.00, NULL, 1002),
(1044, '黎娜', '仓储管理员', NULL, '2023-03-01', 4000.00, NULL, 1001),
(1045, '常伟', '实习生', NULL, '2023-03-01', 4000.00, NULL, 1003),
(1046, '武静', '法务助理', 1011, '2023-04-10', 8000.00, NULL, 1005),
(1047, '乔峰', '后端开发', 1027, '2023-05-20', 13000.00, NULL, 1002),
(1048, '贺梅', '新媒体运营', 1013, '2023-06-12', 8200.00, NULL, 1001),
(1049, '赖东', '后端开发', 1011, '2023-07-19', 7000.00, NULL, 1005),
(1050, '龚雪', '新媒体运营', 1013, '2023-08-25', 8600.00, NULL, 1001);
INSERT INTO SALGRADE (GRADE, LOSAL, HISAL) VALUES
(1, 0, 5000),
(2, 5001, 10000),
(3, 10001, 15000),
(4, 15001, 20000),
(5, 20001, 30000);
-- 测试数据:CUSTOMER和CUSTOMER_ORDER表
DROP TABLE IF EXISTS CUSTOMER;
DROP TABLE IF EXISTS CUSTOMER_ORDER;
CREATE TABLE CUSTOMER(
ID INT(4) NOT NULL,
NAME VARCHAR(10),
PRIMARY KEY(ID)
);
CREATE TABLE CUSTOMER_ORDER(
ID INT(4) NOT NULL,
PRICE DECIMAL,
CUSTOMERID INT(4) NOT NULL
);
INSERT INTO CUSTOMER (ID, NAME) VALUES
(1001, 'Alice'),
(1002, 'Bob'),
(1003, 'Charlie'),
(1004, 'Diana'),
(1005, 'Evan'),
(1006, 'Tom'),
(1007, 'Jack');
INSERT INTO CUSTOMER_ORDER (ID, PRICE, CUSTOMERID) VALUES
(2001, 99.99, 1001),
(2002, 150.50, 1001),
(2003, 200.00, 1002),
(2004, 75.25, 1003),
(2005, 300.75, 1003),
(2006, 45.00, 1004),
(2007, 120.30, 1005),
(2008, 88.88, 1005),
(2009, 999.99, 1005),
(2010, 10.00, 1002);
五、🔍DQL:数据查询语言详解
1. 简单查询
sql
-- 查询特定字段
SELECT ENAME, SAL FROM EMP;
-- 使用别名
SELECT ENAME AS 姓名, SAL * 12 AS 年薪 FROM EMP;
-- ❗避免在生产代码中使用 SELECT *
-- 推荐明确指定字段以提高可读性和性能
2. 条件查询(WHERE)
| 条件 | 说明 |
|---|---|
= / != / <> |
等于 / 不等于 |
BETWEEN ... AND ... |
闭区间范围 |
IS NULL / IS NOT NULL |
判断空值 |
AND / OR |
逻辑与/或 |
IN / NOT IN |
多值匹配 |
LIKE |
模糊查询(% 任意字符,_ 单字符) |
示例:
sql
-- 查询佣金为空的员工
SELECT * FROM EMP WHERE COMM IS NULL;
-- 查询部门为 1001 或 1002 且薪资 < 10000 的员工
SELECT ENAME, SAL, DEPTID
FROM EMP
WHERE SAL < 10000 AND (DEPTID = 1001 OR DEPTID = 1002);
-- 姓名包含"伟"的员工
SELECT * FROM EMP WHERE ENAME LIKE '%伟%';
⚠️ 注意:
NOT IN遇到NULL会返回空结果,建议先过滤NULL。
3. 排序(ORDER BY)
sql
-- 默认升序(ASC),可省略
SELECT * FROM EMP ORDER BY SAL DESC;
-- 多字段排序:先按薪资升序,再按姓名降序
SELECT * FROM EMP ORDER BY SAL ASC, ENAME DESC;
4. 去重(DISTINCT)
sql
-- 查询所有不同的(部门, 岗位)组合
SELECT DISTINCT DEPTID, JOB FROM EMP;
DISTINCT必须放在字段列表最前面,作用于所有字段组合。
六、🧮常用函数
字符串函数
| 函数 | 说明 |
|---|---|
UPPER(str) / LOWER(str) |
转大小写 |
SUBSTR(str, start, len) |
截取子串 |
LENGTH(str) |
字节长度 |
CHAR_LENGTH(str) |
字符个数 |
CONCAT(s1, s2, ...) |
拼接字符串 |
| `TRIM([LEADING | TRAILING |
数值函数
| 函数 | 说明 |
|---|---|
RAND() |
0~1 随机数 |
ROUND(x, y) |
四舍五入(保留 y 位小数) |
TRUNCATE(x, y) |
截断小数 |
CEIL(x) / FLOOR(x) |
向上/下取整 |
日期函数
| 函数 | 说明 |
|---|---|
NOW() / CURDATE() / CURTIME() |
当前时间/日期/时间 |
YEAR(date) / MONTH(date) / DAY(date) |
提取年月日 |
DATE_ADD(date, INTERVAL expr unit) |
日期加减 |
DATE_FORMAT(date, '%Y-%m-%d %H:%i:%s') |
格式化日期 |
STR_TO_DATE('01/01/2000', '%m/%d/%Y') |
字符串转日期 |
DATEDIFF(d1, d2) |
两日期相差天数 |
空值处理
sql
-- 若 COMM 为 NULL,则视为 0
SELECT ENAME, SAL + IFNULL(COMM, 0) AS 实发工资 FROM EMP;
所有含
NULL的数学运算结果均为NULL!
条件函数
sql
-- IF 函数
SELECT ENAME, IF(JOB = '生产员', SAL * 1.1, SAL) AS 调薪后 FROM EMP;
-- CASE WHEN(更清晰)
SELECT ENAME,
CASE JOB
WHEN '生产员' THEN SAL * 1.1
WHEN '市场专员' THEN SAL * 1.2
ELSE SAL
END AS NEWSAL
FROM EMP;
七、📊分组与聚合(GROUP BY + 聚合函数)
聚合函数(多行处理函数)
| 函数 | 说明 |
|---|---|
COUNT(*) |
总行数 |
SUM(col) |
求和 |
AVG(col) |
平均值 |
MAX(col) / MIN(col) |
最大/最小值 |
聚合函数自动忽略
NULL,且不能用于WHERE子句。
分组查询
sql
-- 每个岗位的平均工资
SELECT JOB, AVG(SAL) FROM EMP GROUP BY JOB;
-- 每个部门不同岗位的平均工资
SELECT DEPTID, JOB, AVG(SAL) FROM EMP GROUP BY DEPTID, JOB;
✅ 规则:
SELECT中非聚合字段必须出现在GROUP BY中。
HAVING:分组后过滤
sql
-- 平均工资 > 10000 的部门
SELECT DEPTID, AVG(SAL)
FROM EMP
GROUP BY DEPTID
HAVING AVG(SAL) > 10000;
💡 优先使用
WHERE过滤(效率更高),HAVING仅用于聚合后的条件。
八、🔗连接查询(JOIN)
1. 内连接(INNER JOIN)
-
等值连接:
sqlSELECT E.ENAME, D.DNAME FROM EMP E INNER JOIN DEPT D ON E.DEPTID = D.DEPTID; -
非等值连接(如薪资等级):
sqlSELECT E.ENAME, E.SAL, S.GRADE FROM EMP E INNER JOIN SALGRADE S ON E.SAL BETWEEN S.LOSAL AND S.HISAL; -
自连接(员工与领导):
sqlSELECT E1.ENAME AS 员工, E2.ENAME AS 领导 FROM EMP E1 INNER JOIN EMP E2 ON E1.MANAGER = E2.EMPID;
2. 外连接
-
左外连接(LEFT JOIN):保留左表全部记录
sqlSELECT D.DNAME, E.ENAME FROM DEPT D LEFT JOIN EMP E ON D.DEPTID = E.DEPTID; -
右外连接(RIGHT JOIN) :保留右表全部记录
(MySQL 不支持 FULL OUTER JOIN)
3. 多表连接
sql
-- 员工 + 部门 + 薪资等级
SELECT E.ENAME, D.DNAME, S.GRADE
FROM EMP E
LEFT JOIN DEPT D ON E.DEPTID = D.DEPTID
LEFT JOIN SALGRADE S ON E.SAL BETWEEN S.LOSAL AND S.HISAL;
九、📦子查询(嵌套查询)
1. WHERE 中的子查询
sql
-- 薪资高于平均值的员工
SELECT ENAME, SAL FROM EMP
WHERE SAL > (SELECT AVG(SAL) FROM EMP);
2. FROM 中的子查询(派生表)
sql
-- 每个部门的平均工资等级
SELECT A.DNAME, S.GRADE
FROM (
SELECT D.DNAME, AVG(E.SAL) AS AVG_SAL
FROM EMP E LEFT JOIN DEPT D ON E.DEPTID = D.DEPTID
GROUP BY D.DNAME
) AS A
LEFT JOIN SALGRADE S ON A.AVG_SAL BETWEEN S.LOSAL AND S.HISAL;
⚠️ 派生表必须起别名!
3. SELECT 中的子查询
sql
-- 查询每个员工的部门名称
SELECT ENAME,
(SELECT DNAME FROM DEPT WHERE DEPTID = E.DEPTID) AS 部门
FROM EMP E;
4. EXISTS / NOT EXISTS
sql
-- 有订单的客户
SELECT NAME FROM CUSTOMER C
WHERE EXISTS (
SELECT 1 FROM CUSTOMER_ORDER CO WHERE C.ID = CO.CUSTOMERID
);
-- 无订单的客户
SELECT NAME FROM CUSTOMER C
WHERE NOT EXISTS (
SELECT 1 FROM CUSTOMER_ORDER CO WHERE C.ID = CO.CUSTOMERID
);
✅
EXISTS通常比IN更高效,尤其子查询结果集大时。
十、🧩UNION 和 UNION ALL
不管是 union 和 union all 都可以将两个查询结果集进行合并。
union会对合并之后的查询结果集进行去重union all是直接将查询结果集进行合并,不进行去重操作union all和union都可以完成的话,优先选择union all,因为union all不去重,效率高一些- 两个查询结果集合并时,列数量要相同
sql
--- 查询岗位是 "生产员" 和 "市场专员" 的员工
--- 方式一:or
SELECT JOB,SAL FROM EMP WHERE JOB = '生产员' OR JOB = '市场专员';
--- 方式二:in
SELECT JOB,SAL FROM EMP WHERE JOB IN ('生产员','市场专员');
--- 方式三:union
SELECT JOB,SAL FROM EMP WHERE JOB = '生产员'
UNION
SELECT JOB,SAL FROM EMP WHERE JOB = '市场专员';
--- 方式四:union all
SELECT JOB,SAL FROM EMP WHERE JOB = '生产员'
UNION ALL
SELECT JOB,SAL FROM EMP WHERE JOB = '市场专员';
那
or和union all有什么区别?考虑走 索引优化 等场景时,可选择
union all;其他简单场景优先用or。
十一、⏳SQL 执行顺序(重要!)
理解执行顺序有助于写出正确高效的 SQL:
text
1. FROM → 确定数据源
2. WHERE → 行过滤
3. GROUP BY → 分组
4. HAVING → 组过滤
5. SELECT → 选择字段(此时才解析别名)
6. ORDER BY → 排序
7. LIMIT → 限制返回行数
因此,HAVING 中不能使用 SELECT 的别名(除非数据库支持,如 MySQL),而 ORDER BY 可以。
十二、🔚结语
SQL 是数据世界的通用语言。掌握其核心语法、函数、连接与子查询,你就能从容应对绝大多数数据操作场景。本文覆盖了从基础到进阶的关键知识点,并配有大量实用示例。建议结合实际数据库动手练习,加深理解。
📌 最佳实践:
- 避免
SELECT *- 优先
WHERE过滤,慎用HAVING- 多表连接使用别名提升可读性
- 注意
NULL对运算和NOT IN的影响