目录
- [MySQL 基础入门](#MySQL 基础入门)
- [SQL 查询语法与函数](#SQL 查询语法与函数)
- 多表关联查询
- 子查询
- [SQL 综合练习](#SQL 综合练习)
- 事务与隔离级别
- 视图、存储过程和函数
- [Node.js 操作 MySQL](#Node.js 操作 MySQL)
一、MySQL 基础入门
1.1 MySQL 架构
MySQL 分为 Server(服务端) 和 Client(客户端) 两部分:
- Server:后台守护进程,负责存储数据
- Client:连接 Server 发送 SQL 语句操作数据库
1.2 使用 Docker 部署 MySQL
bash
# 运行 MySQL 容器
docker run -d \
--name mysql \
-p 3306:3306 \
-v /path/to/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=your_password \
mysql:latest
参数说明:
-p 3306:3306:端口映射,客户端通过此端口连接-v:数据卷挂载,持久化数据MYSQL_ROOT_PASSWORD:root 用户密码
1.3 SQL 分类
| 类型 | 说明 | 示例 |
|---|---|---|
| DDL (Data Definition Language) | 数据定义语言,修改结构 | CREATE, ALTER, DROP, TRUNCATE |
| DML (Data Manipulation Language) | 数据操作语言,增删改 | INSERT, UPDATE, DELETE |
| DQL (Data Query Language) | 数据查询语言,查询数据 | SELECT |
1.4 数据库基本操作
sql
-- 创建数据库
CREATE DATABASE hello-mysql CHARACTER SET utf8mb4;
-- 查看所有数据库
SHOW DATABASES;
-- 切换数据库
USE hello-mysql;
-- 删除数据库
DROP DATABASE hello-mysql;
1.5 数据类型
常用类型:
- INT:整数
- VARCHAR(n):变长字符串
- CHAR(n):定长字符串
- DOUBLE:浮点数
- DECIMAL(m,n):定点数
- DATE:日期
- TIME:时间
- DATETIME:日期时间
- TEXT:长文本
- TIMESTAMP:时间戳(自动转 UTC)
1.6 表的创建与约束
sql
CREATE TABLE student (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID',
name VARCHAR(50) NOT NULL COMMENT '名字',
age INT DEFAULT NULL COMMENT '年龄',
email VARCHAR(100) COMMENT '邮箱',
create_time DATETIME NOT NULL COMMENT '创建时间',
status INT DEFAULT 0 COMMENT '状态 0-正常 1-删除',
PRIMARY KEY (id)
) CHARSET=utf8mb4;
约束类型:
PRIMARY KEY:主键,唯一标识一行,自带 UNIQUE + NOT NULLAUTO_INCREMENT:自增NOT NULL:非空DEFAULT:默认值UNIQUE:唯一COMMENT:注释
1.7 增删改查(CRUD)
sql
-- 插入数据
INSERT INTO student (name, age, email, create_time)
VALUES ('张三', 20, 'zhangsan@qq.com', NOW());
-- 批量插入
INSERT INTO student (name, age) VALUES
('李四', 22), ('王五', 21);
-- 查询数据
SELECT * FROM student;
SELECT name, age FROM student;
-- 更新数据
UPDATE student SET email = 'new@qq.com' WHERE id = 1;
-- 删除数据
DELETE FROM student WHERE id = 1;
-- 清空表(重置自增 ID)
TRUNCATE TABLE student;
-- 删除表
DROP TABLE student;
二、SQL 查询语法与函数
2.1 WHERE 条件查询
sql
-- 基本条件
SELECT * FROM student WHERE age >= 19;
-- 多条件(AND/OR)
SELECT * FROM student WHERE age >= 18 AND score >= 90;
-- 模糊查询(LIKE)
SELECT * FROM student WHERE name LIKE '王%'; -- 以"王"开头
SELECT * FROM student WHERE name LIKE '%明%'; -- 包含"明"
-- 集合查询(IN/NOT IN)
SELECT * FROM student WHERE class IN ('一班', '二班');
SELECT * FROM student WHERE class NOT IN ('三班');
-- 区间查询(BETWEEN AND)
SELECT * FROM student WHERE age BETWEEN 18 AND 20;
-- NULL 判断
SELECT * FROM student WHERE email IS NULL;
SELECT * FROM student WHERE email IS NOT NULL;
2.2 别名(AS)
sql
SELECT name AS 姓名, score AS 分数 FROM student;
2.3 去重(DISTINCT)
sql
SELECT DISTINCT class FROM student;
2.4 排序(ORDER BY)
sql
-- 升序(ASC)/ 降序(DESC)
SELECT * FROM student ORDER BY score ASC;
SELECT * FROM student ORDER BY score DESC, age ASC;
-- 多字段排序:先按 score 降序,score 相同按 age 升序
2.5 分页(LIMIT)
sql
-- 从第 0 条开始,取 5 条(第 1 页)
SELECT * FROM student LIMIT 0, 5;
SELECT * FROM student LIMIT 5; -- 简写
-- 第 2 页
SELECT * FROM student LIMIT 5, 5;
-- 第 3 页(page=3, size=5)
SELECT * FROM student LIMIT (page-1)*size, size;
2.6 分组查询(GROUP BY)
sql
-- 按班级分组,统计平均分
SELECT class, AVG(score) AS avg_score
FROM student
GROUP BY class;
-- 统计每个班的人数
SELECT class, COUNT(*) AS count
FROM student
GROUP BY class;
-- 分组后过滤(HAVING)
SELECT class, AVG(score) AS avg_score
FROM student
GROUP BY class
HAVING avg_score > 80;
WHERE vs HAVING:
WHERE:在分组前过滤HAVING:在分组后过滤
2.7 聚合函数
sql
SELECT
COUNT(*) AS 总人数,
AVG(score) AS 平均分,
SUM(score) AS 总分,
MIN(score) AS 最低分,
MAX(score) AS 最高分
FROM student;
2.8 字符串函数
sql
SELECT
CONCAT('姓名:', name), -- 字符串拼接
SUBSTR(name, 2, 3), -- 截取(从第2位开始取3个)
LENGTH(name), -- 长度
UPPER('hello'), -- 转大写
LOWER('HELLO'), -- 转小写
TRIM(' hello '), -- 去除首尾空格
REPLACE(name, '张', '章'), -- 替换
GROUP_CONCAT(name SEPARATOR ',') -- 分组拼接
FROM student;
2.9 数值函数
sql
SELECT
ROUND(1.234567, 2), -- 四舍五入,保留2位
CEIL(1.2), -- 向上取整 = 2
FLOOR(1.8), -- 向下取整 = 1
ABS(-5), -- 绝对值 = 5
MOD(10, 3); -- 取模 = 1
2.10 日期函数
sql
SELECT
NOW(), -- 当前日期时间
CURDATE(), -- 当前日期
CURTIME(), -- 当前时间
YEAR('2023-06-01'), -- 提取年份
MONTH('2023-06-01'), -- 提取月份
DAY('2023-06-01'), -- 提取日
DATE_FORMAT(NOW(), '%Y年%m月%d日'), -- 格式化日期
STR_TO_DATE('2023-06-01', '%Y-%m-%d'), -- 字符串转日期
DATEDIFF('2023-12-31', '2023-01-01'), -- 日期差
DATE_ADD(NOW(), INTERVAL 7 DAY), -- 日期加
DATE_SUB(NOW(), INTERVAL 1 MONTH); -- 日期减
2.11 条件函数
sql
-- IF 函数
SELECT name, IF(score >= 60, '及格', '不及格') AS result
FROM student;
-- CASE 函数
SELECT name, score,
CASE
WHEN score >= 90 THEN '优秀'
WHEN score >= 80 THEN '良好'
WHEN score >= 60 THEN '及格'
ELSE '不及格'
END AS 等级
FROM student;
2.12 类型转换函数
sql
-- 字符串转数字
SELECT CONVERT('123', SIGNED);
SELECT CAST('123' AS SIGNED);
-- 数字转字符串
SELECT CAST(123 AS CHAR);
2.13 其他常用函数
sql
SELECT
NULLIF(1, 1), -- 相等返回 NULL,不等返回第一个值
COALESCE(NULL, 1), -- 返回第一个非 NULL 值
GREATEST(1, 2, 3), -- 返回最大值
LEAST(1, 2, 3), -- 返回最小值
VERSION(), -- MySQL 版本
DATABASE(), -- 当前数据库
USER(); -- 当前用户
三、多表关联查询
3.1 表关系类型
| 关系类型 | 说明 | 示例 |
|---|---|---|
| 一对一 | 1:1 关系 | 用户 ↔ 身份证 |
| 一对多 | 1:N 关系 | 部门 → 员工 |
| 多对多 | N:N 关系 | 文章 ↔ 标签 |
3.2 外键约束
sql
CREATE TABLE id_card (
id INT PRIMARY KEY AUTO_INCREMENT,
card_name VARCHAR(45) NOT NULL,
user_id INT,
FOREIGN KEY (user_id) REFERENCES user(id)
);
3.3 JOIN 连接查询
INNER JOIN(内连接)
sql
-- 只返回两表都有关联的记录
SELECT u.id, u.name, c.card_name
FROM user u
INNER JOIN id_card c ON u.id = c.user_id;
-- 简写为 JOIN
SELECT * FROM user u
JOIN id_card c ON u.id = c.user_id;
LEFT JOIN(左连接)
sql
-- 返回左表所有记录,右表无关联则为 NULL
SELECT u.id, u.name, c.card_name
FROM user u
LEFT JOIN id_card c ON u.id = c.user_id;
RIGHT JOIN(右连接)
sql
-- 返回右表所有记录,左表无关联则为 NULL
SELECT u.id, u.name, c.card_name
FROM user u
RIGHT JOIN id_card c ON u.id = c.user_id;
3.4 级联操作
| 级联方式 | 说明 |
|---|---|
| CASCADE | 主表删除/更新,从表关联记录级联删除/更新 |
| SET NULL | 主表删除/更新,从表外键置为 NULL |
| RESTRICT | 有从表关联记录时,禁止删除/更新主表 |
| NO ACTION | 同 RESTRICT |
sql
-- 创建外键并设置级联
ALTER TABLE employee
ADD CONSTRAINT fk_department
FOREIGN KEY (department_id) REFERENCES department(id)
ON DELETE SET NULL
ON UPDATE CASCADE;
3.5 多对多关系
多对多需要通过 中间表 维护关系:
sql
-- 文章表
CREATE TABLE article (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100) NOT NULL
);
-- 标签表
CREATE TABLE tag (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL
);
-- 中间表(复合主键)
CREATE TABLE article_tag (
article_id INT NOT NULL,
tag_id INT NOT NULL,
PRIMARY KEY (article_id, tag_id),
FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tag(id) ON DELETE CASCADE
);
多表关联查询:
sql
SELECT a.title, t.name AS tag_name
FROM article a
JOIN article_tag at ON a.id = at.article_id
JOIN tag t ON t.id = at.tag_id
WHERE a.id = 1;
四、子查询
4.1 基本子查询
sql
-- 查询成绩最高的学生
SELECT name, class FROM student
WHERE score = (SELECT MAX(score) FROM student);
-- 查询高于平均分的学生
SELECT * FROM student
WHERE score > (SELECT AVG(score) FROM student);
4.2 EXISTS / NOT EXISTS
sql
-- 查询有员工的部门
SELECT name FROM department d
WHERE EXISTS (
SELECT 1 FROM employee e WHERE e.department_id = d.id
);
-- 查询没有员工的部门
SELECT name FROM department d
WHERE NOT EXISTS (
SELECT 1 FROM employee e WHERE e.department_id = d.id
);
4.3 子查询在 INSERT/UPDATE/DELETE 中使用
sql
-- INSERT + SELECT
INSERT INTO avg_price_by_category (category, avg_price)
SELECT category, AVG(price) FROM product GROUP BY category;
-- UPDATE + 子查询
UPDATE employee SET name = CONCAT('技术-', name)
WHERE department_id = (
SELECT id FROM department WHERE name = '技术部'
);
-- DELETE + 子查询
DELETE FROM employee WHERE department_id = (
SELECT id FROM department WHERE name = '技术部'
);
五、SQL 综合练习
5.1 表结构设计
sql
-- 客户表
CREATE TABLE customers (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL
);
-- 订单表
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(id)
ON DELETE CASCADE ON UPDATE CASCADE
);
-- 订单项表
CREATE TABLE order_items (
id INT PRIMARY KEY AUTO_INCREMENT,
order_id INT NOT NULL,
product_name VARCHAR(100) NOT NULL,
quantity INT NOT NULL,
price DECIMAL(10,2) NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id)
ON DELETE CASCADE ON UPDATE CASCADE
);
5.2 常见查询需求
sql
-- 1. 查询每个客户的订单总金额
SELECT c.name, SUM(o.total_amount) AS total
FROM customers c
JOIN orders o ON c.id = o.customer_id
GROUP BY c.id;
-- 2. 查询总金额排名前 3 的客户
SELECT c.name, SUM(o.total_amount) AS total
FROM customers c
JOIN orders o ON c.id = o.customer_id
GROUP BY c.id
ORDER BY total DESC
LIMIT 3;
-- 3. 查询每个客户的订单商品清单
SELECT c.name, o.order_date, o.total_amount,
oi.product_name, oi.quantity, oi.price
FROM customers c
JOIN orders o ON c.id = o.customer_id
JOIN order_items oi ON o.id = oi.order_id
ORDER BY c.name, o.order_date;
-- 4. 查询姓"张"客户的订单详情
SELECT c.name, o.order_date, o.total_amount,
oi.product_name, oi.quantity, oi.price
FROM customers c
JOIN orders o ON c.id = o.customer_id
JOIN order_items oi ON o.id = oi.order_id
WHERE c.name LIKE '张%'
ORDER BY c.name;
-- 5. 查询指定日期范围的订单
SELECT * FROM orders
WHERE order_date BETWEEN '2022-01-01' AND '2022-01-31';
-- 6. 查询订单总金额并计算占比
SELECT c.name,
SUM(o.total_amount) AS total,
SUM(o.total_amount) / (SELECT SUM(total_amount) FROM orders) * 100 AS percentage
FROM customers c
JOIN orders o ON c.id = o.customer_id
GROUP BY c.id;
-- 7. 批量更新(打九折)
UPDATE orders o
SET o.total_amount = o.total_amount * 0.9
WHERE o.customer_id IN (
SELECT id FROM customers WHERE name = '王磊'
);
六、事务与隔离级别
6.1 事务基础
事务保证多条 SQL 要么全部成功,要么全部失败。
sql
-- 开启事务
START TRANSACTION;
-- 执行多条 SQL
UPDATE order_items SET quantity = 1 WHERE order_id = 3;
UPDATE orders SET total_amount = 200 WHERE id = 3;
-- 提交事务
COMMIT;
-- 或回滚事务
ROLLBACK;
6.2 保存点(SAVEPOINT)
sql
START TRANSACTION;
SAVEPOINT step1;
UPDATE order_items SET quantity = 1 WHERE order_id = 3;
SAVEPOINT step2;
UPDATE orders SET total_amount = 200 WHERE id = 3;
-- 回滚到某个保存点
ROLLBACK TO SAVEPOINT step2;
-- 释放保存点
RELEASE SAVEPOINT step1;
6.3 隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | ✓ | ✓ | ✓ |
| READ COMMITTED | ✗ | ✓ | ✓ |
| REPEATABLE READ (默认) | ✗ | ✗ | ✓ |
| SERIALIZABLE | ✗ | ✗ | ✗ |
问题说明:
- 脏读:读到其他事务未提交的数据
- 不可重复读:同一事务内两次读取结果不同
- 幻读:同一事务内两次查询行数不同
sql
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
七、视图、存储过程和函数
7.1 视图(VIEW)
视图是虚拟表,基于查询结果创建。
sql
-- 创建视图
CREATE VIEW customer_orders AS
SELECT
c.name AS customer_name,
o.id AS order_id,
o.order_date,
o.total_amount
FROM customers c
JOIN orders o ON c.id = o.customer_id;
-- 查询视图
SELECT * FROM customer_orders;
-- 修改视图
CREATE OR REPLACE VIEW customer_orders AS
SELECT * FROM customers WHERE id > 10;
-- 删除视图
DROP VIEW IF EXISTS customer_orders;
视图优点:
- 简化复杂查询
- 控制权限,隐藏敏感字段
- 数据安全性
7.2 存储过程(PROCEDURE)
sql
-- 修改分隔符
DELIMITER $$
-- 创建存储过程
CREATE PROCEDURE get_customer_orders(IN customer_id INT)
BEGIN
SELECT o.id, o.order_date, o.total_amount
FROM orders o
WHERE o.customer_id = customer_id;
END $$
-- 恢复分隔符
DELIMITER ;
-- 调用存储过程
CALL get_customer_orders(5);
-- 删除存储过程
DROP PROCEDURE IF EXISTS get_customer_orders;
参数类型:
IN:输入参数(默认)OUT:输出参数INOUT:输入输出参数
7.3 自定义函数(FUNCTION)
sql
-- 允许创建函数
SET GLOBAL log_bin_trust_function_creators = 1;
DELIMITER $$
-- 创建函数
CREATE FUNCTION get_order_total(order_id INT)
RETURNS DECIMAL(10,2)
BEGIN
DECLARE total DECIMAL(10,2);
SELECT SUM(quantity * price) INTO total
FROM order_items
WHERE order_id = order_items.order_id;
RETURN total;
END $$
DELIMITER ;
-- 调用函数
SELECT id, get_order_total(id) FROM orders;
-- 删除函数
DROP FUNCTION IF EXISTS get_order_total;
存储过程 vs 函数:
| 特性 | 存储过程 | 函数 |
|---|---|---|
| 调用方式 | CALL |
直接在 SQL 中使用 |
| 返回值 | 无(可通过 OUT 参数) | 必须有返回值 |
| 使用场景 | 复杂业务逻辑 | 计算并返回单个值 |
八、Node.js 操作 MySQL
8.1 使用 mysql2 包
bash
npm install mysql2
基本连接与查询
javascript
const mysql = require('mysql2');
// 创建连接
const connection = mysql.createConnection({
host: 'localhost',
port: 3306,
user: 'root',
password: 'your_password',
database: 'your_database'
});
// 查询
connection.query('SELECT * FROM users', (err, results, fields) => {
console.log(results); // 查询结果
console.log(fields.map(f => f.name)); // 字段名
});
// 带参数查询
connection.query(
'SELECT * FROM users WHERE age > ?',
[18],
(err, results) => {
console.log(results);
}
);
// 插入
connection.execute(
'INSERT INTO users (name, age) VALUES (?, ?)',
['张三', 25],
(err, results) => {
console.log(err);
}
);
// 关闭连接
connection.end();
Promise 版本
javascript
const mysql = require('mysql2/promise');
async function main() {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'your_password',
database: 'your_database'
});
const [results, fields] = await connection.query('SELECT * FROM users');
console.log(results);
await connection.end();
}
8.2 使用连接池(推荐)
javascript
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'your_password',
database: 'your_database',
waitForConnections: true, // 无可用连接时等待
connectionLimit: 10, // 最大连接数
maxIdle: 10, // 最大空闲连接数
idleTimeout: 60000, // 空闲超时时间
queueLimit: 0 // 排队请求数(0=无限制)
});
// 自动获取/释放连接
const [results] = await pool.query('SELECT * FROM users');
console.log(results);
// 手动获取连接
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
await connection.query('UPDATE users SET age = age + 1 WHERE id = ?', [1]);
await connection.commit();
} catch (err) {
await connection.rollback();
throw err;
} finally {
connection.release();
}
8.3 使用 TypeORM(ORM 框架)
bash
npx typeorm@latest init --name my-project --database mysql
cd my-project
npm install
npm install mysql2
实体定义
typescript
// src/entity/User.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column({ default: 0 })
age: number;
@Column({ nullable: true })
email: string;
}
数据源配置
typescript
// src/data-source.ts
import { DataSource } from 'typeorm';
import { User } from './entity/User';
export const AppDataSource = new DataSource({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'your_password',
database: 'your_database',
synchronize: true, // 自动同步表结构
entities: [User],
connectorPackage: 'mysql2'
});
使用示例
typescript
import { AppDataSource } from './data-source';
import { User } from './entity/User';
AppDataSource.initialize().then(async () => {
const userRepo = AppDataSource.getRepository(User);
// 插入
const user = new User();
user.name = '张三';
user.age = 25;
await userRepo.save(user);
// 查询
const users = await userRepo.find();
console.log(users);
// 条件查询
const adultUsers = await userRepo.findBy({ age: 25 });
console.log(adultUsers);
// 更新
await userRepo.update({ id: 1 }, { age: 30 });
// 删除
await userRepo.delete({ id: 1 });
});
8.4 ORM vs 原生 SQL
| 特性 | ORM (TypeORM) | 原生 SQL (mysql2) |
|---|---|---|
| 开发效率 | 高 | 低 |
| 学习曲线 | 需要学习框架 | 直接写 SQL |
| 灵活性 | 中等 | 高 |
| 复杂查询 | 可能受限 | 完全灵活 |
| 类型安全 | 支持 | 需手动处理 |
| 适用场景 | 中小型项目,简单 CRUD | 复杂查询,高性能要求 |
九、最佳实践总结
9.1 SQL 编写规范
- 关键字大写:SELECT, FROM, WHERE, GROUP BY 等
- 使用反引号:表名、列名使用反引号包裹避免关键字冲突
- 字符串使用单引号
- 添加注释:复杂查询添加说明
- 避免 SELECT *:明确指定查询字段
9.2 索引优化
sql
-- 创建索引
CREATE INDEX idx_name ON users(name);
-- 创建唯一索引
CREATE UNIQUE INDEX idx_email ON users(email);
-- 查看索引
SHOW INDEX FROM users;
9.3 事务使用原则
- 增删改操作必须使用事务
- 事务尽量简短
- 合理设置隔离级别
- 使用 SAVEPOINT 处理复杂事务
9.4 安全建议
- 不要拼接 SQL:使用参数化查询防止 SQL 注入
- 最小权限原则:应用程序使用专用数据库用户
- 定期备份:重要数据定期备份
十、常用命令速查表
| 命令 | 说明 |
|---|---|
SHOW DATABASES |
查看所有数据库 |
USE db_name |
切换数据库 |
SHOW TABLES |
查看当前数据库所有表 |
DESC table_name |
查看表结构 |
SHOW CREATE TABLE table_name |
查看建表语句 |
SELECT @@transaction_isolation |
查看隔离级别 |
SELECT VERSION() |
查看 MySQL 版本 |
SELECT DATABASE() |
查看当前数据库 |
SELECT USER() |
查看当前用户 |
参考文档:
- MySQL 官方文档:https://dev.mysql.com/doc/