1. 项目概述
作为一名数据库工程师,我经常需要整理MySQL的学习笔记。2026年3月4日上午的34节课,主要聚焦在数据库创建和SQL语句执行这两个核心技能点上。这节课的内容看似基础,但却是每个数据库从业者必须扎实掌握的看家本领。
在实际工作中,我发现很多初级开发者虽然能写出复杂的SQL查询,却经常在基础的数据库创建环节犯错。这节课的笔记详细记录了从零开始创建数据库,到执行各类SQL语句的全过程,包括DDL、DML、DQL等不同类型的SQL语句操作。这些内容不仅适用于MySQL初学者,对有经验的开发者来说也是一次很好的知识梳理机会。
2. 核心概念解析
2.1 数据库创建基础
创建数据库是使用MySQL的第一步,也是最容易被忽视的环节。很多人直接使用默认配置创建数据库,这在实际生产环境中是非常危险的做法。正确的数据库创建需要考虑字符集、排序规则、存储引擎等多个关键参数。
字符集的选择直接影响数据的存储和检索。我推荐使用utf8mb4而不是老旧的utf8,因为utf8mb4支持完整的Unicode字符集,包括emoji表情符号。排序规则则决定了字符串比较和排序的方式,常用的有utf8mb4_general_ci(不区分大小写)和utf8mb4_bin(二进制比较)。
2.2 SQL语句分类
SQL语句主要分为以下几类:
- DDL(数据定义语言):用于定义数据库对象,如CREATE、ALTER、DROP等
- DML(数据操作语言):用于操作数据,如INSERT、UPDATE、DELETE等
- DQL(数据查询语言):主要是SELECT查询语句
- DCL(数据控制语言):如GRANT、REVOKE等权限控制语句
- TCL(事务控制语言):如COMMIT、ROLLBACK等事务相关语句
理解这些分类有助于我们更好地组织SQL语句,并在适当的场景使用正确的语句类型。
3. 实操步骤详解
3.1 数据库创建实操
让我们从最基础的数据库创建开始。以下是一个完整的数据库创建语句:
sql复制CREATE DATABASE `company_db`
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
这个语句创建了一个名为company_db的数据库,使用utf8mb4字符集和utf8mb4_unicode_ci排序规则。我特意使用了反引号(`)包裹数据库名,这是一种良好的习惯,可以避免使用MySQL保留关键字作为名称时出现问题。
创建数据库后,我们需要确认数据库是否创建成功:
sql复制SHOW DATABASES;
这个命令会列出当前MySQL实例中的所有数据库,我们可以检查company_db是否在列表中。
3.2 表创建与结构设计
有了数据库后,下一步是创建表。表的设计是数据库应用的核心,需要考虑字段类型、主键、索引等多个因素。以下是一个员工表的创建示例:
sql复制CREATE TABLE `employees` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`email` VARCHAR(100) UNIQUE,
`department_id` INT,
`salary` DECIMAL(10,2),
`hire_date` DATE,
PRIMARY KEY (`id`),
INDEX `idx_department` (`department_id`),
CONSTRAINT `fk_department`
FOREIGN KEY (`department_id`)
REFERENCES `departments` (`id`)
) ENGINE=InnoDB;
这个表定义包含了多个重要元素:
- 自增主键id
- 非空的name字段
- 唯一的email字段
- 外键关联到departments表
- 适当的索引
- 明确指定使用InnoDB引擎
注意:在生产环境中,表设计需要更详细的规划,包括字段注释、更精确的数据类型选择等。
3.3 数据操作语言(DML)实践
3.3.1 插入数据
插入数据是最基本的DML操作。以下是几种不同的INSERT语句写法:
sql复制-- 基本插入
INSERT INTO `employees` (`name`, `email`, `department_id`, `salary`, `hire_date`)
VALUES ('张三', 'zhangsan@example.com', 1, 8000.00, '2025-01-15');
-- 批量插入
INSERT INTO `employees` (`name`, `email`, `department_id`, `salary`, `hire_date`)
VALUES
('李四', 'lisi@example.com', 2, 7500.00, '2025-02-20'),
('王五', 'wangwu@example.com', 1, 8500.00, '2025-03-10');
-- 使用SET语法
INSERT INTO `employees`
SET `name` = '赵六',
`email` = 'zhaoliu@example.com',
`department_id` = 3,
`salary` = 9000.00,
`hire_date` = '2025-04-05';
批量插入可以显著提高性能,特别是在需要插入大量数据时。根据我的测试,批量插入比单条插入快5-10倍。
3.3.2 更新数据
UPDATE语句用于修改现有数据。以下是一些常见的更新模式:
sql复制-- 基本更新
UPDATE `employees`
SET `salary` = 8500.00
WHERE `id` = 1;
-- 带条件的更新
UPDATE `employees`
SET `salary` = `salary` * 1.1
WHERE `department_id` = 1
AND `hire_date` < '2025-06-01';
-- 使用JOIN的更新
UPDATE `employees` e
JOIN `departments` d ON e.department_id = d.id
SET e.salary = e.salary * 1.05
WHERE d.name = '研发部';
重要提示:执行UPDATE语句前,最好先用SELECT语句测试WHERE条件,确保只更新目标行。
3.3.3 删除数据
DELETE语句用于删除数据,需要特别小心:
sql复制-- 基本删除
DELETE FROM `employees`
WHERE `id` = 5;
-- 带条件的删除
DELETE FROM `employees`
WHERE `hire_date` < '2024-01-01'
AND `salary` < 6000;
-- 清空表(谨慎使用!)
TRUNCATE TABLE `employees`;
TRUNCATE与DELETE的区别:
- TRUNCATE是DDL操作,DELETE是DML操作
- TRUNCATE不能带WHERE条件,会清空整个表
- TRUNCATE重置自增值,DELETE不会
- TRUNCATE不触发触发器,DELETE会
3.4 数据查询语言(DQL)实践
SELECT查询是SQL中最复杂也最强大的部分。以下是一些典型查询示例:
3.4.1 基础查询
sql复制-- 查询所有字段
SELECT * FROM `employees`;
-- 查询特定字段
SELECT `id`, `name`, `salary` FROM `employees`;
-- 带条件的查询
SELECT `name`, `salary`
FROM `employees`
WHERE `department_id` = 1
AND `salary` > 8000;
3.4.2 聚合查询
sql复制-- 基本聚合
SELECT
COUNT(*) AS employee_count,
AVG(`salary`) AS avg_salary,
MAX(`salary`) AS max_salary,
MIN(`salary`) AS min_salary
FROM `employees`;
-- 分组聚合
SELECT
`department_id`,
COUNT(*) AS dept_count,
AVG(`salary`) AS dept_avg_salary
FROM `employees`
GROUP BY `department_id`;
3.4.3 连接查询
sql复制-- 内连接
SELECT e.`name`, d.`name` AS department_name
FROM `employees` e
INNER JOIN `departments` d ON e.department_id = d.id;
-- 左外连接
SELECT e.`name`, d.`name` AS department_name
FROM `employees` e
LEFT JOIN `departments` d ON e.department_id = d.id;
-- 自连接(查询同一部门员工)
SELECT e1.`name` AS employee1, e2.`name` AS employee2
FROM `employees` e1
JOIN `employees` e2 ON e1.department_id = e2.department_id
WHERE e1.id < e2.id;
3.4.4 子查询
sql复制-- WHERE子句中的子查询
SELECT `name`, `salary`
FROM `employees`
WHERE `salary` > (
SELECT AVG(`salary`) FROM `employees`
);
-- FROM子句中的子查询
SELECT dept.`name`, emp_stats.avg_salary
FROM `departments` dept
JOIN (
SELECT `department_id`, AVG(`salary`) AS avg_salary
FROM `employees`
GROUP BY `department_id`
) emp_stats ON dept.id = emp_stats.department_id;
4. 高级SQL技巧
4.1 窗口函数
MySQL 8.0引入了窗口函数,大大增强了SQL的分析能力:
sql复制-- 计算部门内薪资排名
SELECT
`name`,
`department_id`,
`salary`,
RANK() OVER (PARTITION BY `department_id` ORDER BY `salary` DESC) AS dept_salary_rank
FROM `employees`;
-- 计算移动平均
SELECT
`name`,
`hire_date`,
`salary`,
AVG(`salary`) OVER (
ORDER BY `hire_date`
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS moving_avg
FROM `employees`;
4.2 公用表表达式(CTE)
CTE使复杂查询更易读和维护:
sql复制-- 基本CTE
WITH dept_stats AS (
SELECT
`department_id`,
AVG(`salary`) AS avg_salary
FROM `employees`
GROUP BY `department_id`
)
SELECT e.`name`, e.`salary`, ds.avg_salary
FROM `employees` e
JOIN dept_stats ds ON e.department_id = ds.department_id
WHERE e.`salary` > ds.avg_salary;
-- 递归CTE(生成序列)
WITH RECURSIVE numbers AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM numbers WHERE n < 100
)
SELECT * FROM numbers;
4.3 JSON支持
MySQL 5.7+提供了强大的JSON支持:
sql复制-- 创建包含JSON列的表
CREATE TABLE `products` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(100),
`details` JSON,
`price` DECIMAL(10,2)
);
-- 插入JSON数据
INSERT INTO `products` (`name`, `details`, `price`)
VALUES (
'智能手机',
'{"brand": "Xiaomi", "color": "black", "specs": {"ram": "8GB", "storage": "256GB"}}',
2999.00
);
-- 查询JSON字段
SELECT
`name`,
`details`->>"$.brand" AS brand,
`details`->>"$.specs.ram" AS ram
FROM `products`;
-- 更新JSON字段
UPDATE `products`
SET `details` = JSON_SET(`details`, '$.color', 'blue')
WHERE `id` = 1;
5. 性能优化技巧
5.1 索引优化
正确的索引可以大幅提高查询性能:
sql复制-- 添加索引
ALTER TABLE `employees` ADD INDEX `idx_name` (`name`);
-- 复合索引
ALTER TABLE `employees` ADD INDEX `idx_dept_salary` (`department_id`, `salary`);
-- 查看索引使用情况
EXPLAIN SELECT * FROM `employees` WHERE `name` = '张三';
索引设计原则:
- 为WHERE、JOIN、ORDER BY子句中的列创建索引
- 高选择性的列更适合索引
- 避免过度索引,因为索引会降低写入性能
- 复合索引要注意列顺序
5.2 查询优化
sql复制-- 避免SELECT *
SELECT `id`, `name` FROM `employees`; -- 好
SELECT * FROM `employees`; -- 不好
-- 使用LIMIT限制结果集
SELECT `name`, `salary` FROM `employees` LIMIT 10;
-- 使用EXISTS代替IN(大数据量时)
SELECT `name` FROM `departments` d
WHERE EXISTS (
SELECT 1 FROM `employees` e
WHERE e.department_id = d.id
);
5.3 分页优化
传统分页在大偏移量时性能很差:
sql复制-- 低效的分页(偏移量大时)
SELECT * FROM `employees` LIMIT 10000, 20;
-- 高效的分页(使用索引列过滤)
SELECT * FROM `employees`
WHERE `id` > 10000
ORDER BY `id`
LIMIT 20;
6. 事务与并发控制
6.1 基本事务
sql复制-- 开始事务
START TRANSACTION;
-- 执行多个操作
UPDATE `accounts` SET `balance` = `balance` - 1000 WHERE `id` = 1;
UPDATE `accounts` SET `balance` = `balance` + 1000 WHERE `id` = 2;
-- 提交或回滚
COMMIT; -- 或 ROLLBACK;
6.2 隔离级别
MySQL支持四种隔离级别:
sql复制-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
不同隔离级别的特点:
- READ UNCOMMITTED:可能读到未提交数据(脏读)
- READ COMMITTED:避免脏读,但可能有不可重复读
- REPEATABLE READ(MySQL默认):避免脏读和不可重复读
- SERIALIZABLE:最高隔离级别,避免所有并发问题
6.3 锁机制
sql复制-- 共享锁(读锁)
SELECT * FROM `employees` WHERE `id` = 1 LOCK IN SHARE MODE;
-- 排他锁(写锁)
SELECT * FROM `employees` WHERE `id` = 1 FOR UPDATE;
锁的使用注意事项:
- 尽量缩小锁定范围
- 减少锁定时间
- 注意锁升级情况
- 避免死锁
7. 备份与恢复
7.1 逻辑备份
bash复制# 使用mysqldump备份
mysqldump -u root -p company_db > company_db_backup.sql
7.2 物理备份
bash复制# 停止MySQL服务
sudo systemctl stop mysql
# 复制数据文件
sudo cp -R /var/lib/mysql /backup/mysql_backup
# 启动MySQL服务
sudo systemctl start mysql
7.3 恢复数据
bash复制# 从逻辑备份恢复
mysql -u root -p company_db < company_db_backup.sql
备份策略建议:
- 定期全量备份+增量备份
- 测试恢复流程
- 异地备份
- 监控备份完整性
8. 安全最佳实践
8.1 用户权限管理
sql复制-- 创建用户
CREATE USER 'app_user'@'%' IDENTIFIED BY 'strong_password';
-- 授予权限
GRANT SELECT, INSERT, UPDATE ON company_db.* TO 'app_user'@'%';
-- 撤销权限
REVOKE DELETE ON company_db.* FROM 'app_user'@'%';
-- 查看权限
SHOW GRANTS FOR 'app_user'@'%';
权限管理原则:
- 最小权限原则
- 避免使用root账户
- 定期审查权限
- 使用强密码
8.2 数据加密
sql复制-- 创建加密表
CREATE TABLE `sensitive_data` (
`id` INT PRIMARY KEY,
`data` VARBINARY(255)
);
-- 插入加密数据
INSERT INTO `sensitive_data` VALUES (1, AES_ENCRYPT('secret', 'encryption_key'));
-- 查询解密数据
SELECT AES_DECRYPT(`data`, 'encryption_key') FROM `sensitive_data` WHERE `id` = 1;
9. 常见问题排查
9.1 连接问题
错误:Can't connect to MySQL server on 'localhost' (10061)
解决方法:
- 检查MySQL服务是否运行
- 检查防火墙设置
- 验证连接参数
9.2 性能问题
慢查询排查步骤:
- 开启慢查询日志
- 使用EXPLAIN分析查询
- 检查索引使用情况
- 优化SQL语句
9.3 数据一致性问题
修复损坏的表:
sql复制-- 检查表状态
CHECK TABLE `employees`;
-- 修复表
REPAIR TABLE `employees`;
10. 学习资源推荐
- 官方文档:https://dev.mysql.com/doc/
- 《高性能MySQL》
- MySQL技术内幕:InnoDB存储引擎
- 在线练习平台:LeetCode数据库题目
在实际工作中,我发现很多问题都源于对基础知识的掌握不牢固。这34节课的笔记虽然基础,但涵盖了MySQL最核心的操作。建议定期复习这些基础内容,随着经验的积累,每次复习都会有新的收获。