Mysql 基础

MYSQL 学习记录

数据模型与 SQL 基础

什么是数据模型

数据模型是用来组织和描述数据结构的方式,关系型数据库使用关系模型。
核心概念包含:

  • 表 (Table)
    现实世界实体的抽象表示
    以行、列描述字段

  • 行 (Row)
    一条完整的数据记录

  • 列 (Column)
    数据字段,例如 name 列,age 列,每列有数据类型

  • 主键 (Primary Key)
    唯一标识,一行数据,不可重复,不能为空

  • 外键 (Forrign Key)
    用与表示表之间的关系,例如用户表和订单表
    user_id INT, FOREIGN KEY (user_id) REFERENCES users(id)

  • 索引 (Index)
    用于提高查询速度的结构
    CREATE INDEX idx_email ON users(email)

SQL 语言分类

SQL(Structured Query Language)包括四大类:

DDL (数据定义语言)

用于操作库和表结构:

  1. CREATE: 创建数据库,表

  2. ALTER: 修改表结构

  3. DROP: 删除数据库,表

  4. RENAME: 重命名表

特点:立即生效(自动提交,不能回滚),立即操作结构而不是数据

数据库
  1. 创建数据库:
1
CREATE DATABASE company;
  1. 查看所有数据库:
1
SHOW DATABASES;
  1. 切换数据库:
1
USE company;
  1. 删除数据库:
1
DROP DATABASE company;
  1. 判断不存在再创建:
1
CREATE DATABASE IF NOT EXISTS company;
数据表

表结构是数据库设计的核心。

1
2
3
4
5
6
7
8
CREATE TABLE employees (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    position VARCHAR(50),
    salary DECIMAL(10,2) DEFAULT 0,
    hire_date DATE,
    email VARCHAR(100) UNIQUE
);
基础数据类型

数值类型:

类型 字节 示例 说明
TINYINT 1 127 / -128 小整数,取值范围 -128~127
SMALLINT 2 32767 较小整数
MEDIUMINT 3 8388607 中等整数
INT / INTEGER 4 2,147,483,647 最常用整型
BIGINT 8 9,223,372,036,854,775,807 大整数(如用户 ID)
FLOAT 4 3.14 单精度浮点数(不精确)
DOUBLE 8 3.1415926535 双精度浮点数(不精确)
DECIMAL(M,D) 可变 123.45 精确小数,金额必用

字符串类型:

类型 最大长度 示例 说明
CHAR(n) 固定 n 字节 ‘abc’ 固定长度字符串(效率高)
VARCHAR(n) 可变 ‘hello’ 最常用变长字符串
TINYTEXT 255 字节 文本 小文本
TEXT 65KB 长文内容 一般文本字段
MEDIUMTEXT 16MB 大文本 不建议频繁查询
LONGTEXT 4GB 超大文本 存储日志等
TINYBLOB 255 字节 二进制 小型二进制数据
BLOB 65KB 二进制 存储图片/文件
MEDIUMBLOB 16MB 二进制 较大文件
LONGBLOB 4GB 二进制 超大文件
ENUM(…) 1 字节 ENUM(‘male’,‘female’) 枚举类型(可读性高)
SET(…) 1~8 字节 SET(‘a’,‘b’) 多选集合

日期与时间类型:

类型 字节 示例 说明
DATE 3 ‘2024-01-01’ 仅日期
TIME 3 ‘12:30:00’ 时间
YEAR 1 2024 年份
DATETIME 5 ‘2024-01-01 12:30:00’ 无时区影响,常用
TIMESTAMP 4 ‘2024-01-01 12:30:00’ 受时区影响,可自动填充
查看表结构
1
2
3
4
5
DESC employess;

-- 或者

SHOW CREATE TABLE employess;
修改表结构
  1. 添加字段
1
2
3
4
5
ALTER TABLE employess
ADD phone VARCHAR(50);

ALTER TABLE employess
ADD address VARCHAR(100) AFTER name; -- 添加到某字段后
  1. 修改字段类型
1
2
ALTER TABLE employess
MODIFY salary DECIMAL(12, 2);
  1. 修改字段名 + 类型
1
2
ALTER TABLE employees
CHANGE phone mobile VARCHAR(30);
  1. 删除字段
1
2
ALTER TABLE employees
DROP COLUMN address;
  1. 重命名表
1
RENAME TABLE employees TO staff;
  1. 添加约束
1
2
3
4
5
6
ALTER TABLE employees
ADD UNIQUE (email);

ALTER TABLE employees
ADD CONSTRAINT fk_user
FOREIGN KEY (user_id) REFERENCES users(id);
删除表
1
2
3
DROP TABLE employees;

DROP TABLE IF EXISTS employees;

DML (数据操作语言)

用于对表中的数据进行增删改:

  1. INSERT: 插入数据
1
2
3
4
5
6
INSERT INTO employees (name, position, salary)
VALUES ('Alice', 'Engineer', 12000);

-- 也可不指定字段
INSERT INTO employees
VALUES (1, 'Bob', 'Manager', 18000, '2023-01-10');

可同时插入多条记录

1
2
3
4
5
INSERT INTO employees (name, position, salary)
VALUES 
('Tom', 'HR', 9000),
('Lucy', 'Engineer', 13000),
('Ken', 'Designer', 11000);

也可使用默认值插入

1
2
INSERT INTO employees (name, position)
VALUES ('Jerry', DEFAULT);

从查询结果插入

1
2
3
4
INSERT INTO developer (name, salary)
SELECT name, salary
FROM employees
WHERE position = 'Engineer';
  1. UPDATE: 更新数据
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
UPDATE employees
SET salary = 15000
WHERE name = 'Alice';

-- 更新多字段
UPDATE employees
SET position = 'Senior Engineer',
    salary = salary + 2000
WHERE id = 1;

-- 若不使用 WHERE 即为全表更新
UPDATE employees
SET salary = salary + 1000;
  1. DELETE: 删除数据
1
2
3
4
5
6
7
8
9
DELETE FROM employees
WHERE id = 3;  -- 删除单条

DELETE FROM employees
WHERE salary < 5000; -- 删除多条

DELETE FROM employees; -- 危险,全表删除

TRUNCATE TABLE employees; -- 清空表(不记录逐行删除日志)

DQL (数据查询语言)

SELECT 查询记录

基础查询
  1. 查询所有字段
1
SELECT * FROM employees;
  1. 查询指定字段
1
SELECT name, salary FROM employees;
  1. 查询计算结果
1
2
SELECT name, salary * 12 annual_salary
FROM employees;
  1. 去重
1
SELECT DISTINCT position FROM employees;
条件查询
  1. 比较运算符
1
2
3
SELECT * FROM employees WHERE salary > 10000;
SELECT * FROM employees WHERE age >= 30;
SELECT * FROM employees WHERE name <> 'Alice';
  1. 逻辑运算符

AND OR NOT

1
2
3
4
5
6
7
8
SELECT * FROM employees
WHERE position = 'Engineer' AND salary > 12000;

SELECT * FROM employees
WHERE position = 'Engineer' OR position = 'Designer';

SELECT * FROM employees
WHERE NOT (salary > 20000);
  1. 范围查询
1
2
SELECT * FROM employees
WHERE salary BETWEEN 10000 AND 20000;
  1. 集合查询
1
2
SELECT * FROM employees
WHERE position IN ('Engineer', 'Manager', 'Designer');
  1. 模糊查询

_ 表示一位模糊, % 多位模糊

1
2
3
SELECT * FROM employees WHERE name LIKE 'A%';   -- A开头
SELECT * FROM employees WHERE name LIKE '%son'; -- 以 son 结尾
SELECT * FROM employees WHERE name LIKE '%a%';  -- 含 a
  1. NULL 判断
1
2
SELECT * FROM employees WHERE email IS NULL;
SELECT * FROM employees WHERE email IS NOT NULL;
聚合函数
  • COUNT 计数
  • SUM 求和
  • AVG 平均值
  • MAX/MIN 最大最小值

聚合函数会忽略 NULL 的数据

分组查询

GROUP BY 用与按字段分组,可结合聚合函数使用

示例 1: 统计各职位的平均薪资

1
2
3
SELECT position, AVG(salary) AS avg_salary
FROM employees
GROUP BY position;

HAVING 对分组结果过滤

1
2
3
4
SELECT position, AVG(salary) AS avg_salary
FROM employees
GROUP BY position
HAVING avg_salary > 15000;
排序查询

ORDER BY 支持升降序

1
2
SELECT * FROM employees
ORDER BY position ASC, salary DESC;

按顺序进行

分页查询

通过 LIMIT 实现

1
2
3
LIMIT offset, size;

LIMIT size;
SQL 的执行顺序
1
FROM  WHERE  GROUP BY  HAVING  SELECT  ORDER BY  LIMIT

DCL (数据控制语言)

主要包含两类操作

  • 用户管理
    CREATE USER / DROP USER / ALTER USER

  • 权限管理
    GRANT / REVOKE

用户管理
  1. 创建用户
1
2
3
CREATE USER 'username'@'host' IDENTIFIED BY 'password';

-- localhost 为本机,% 为任意 IP  
  1. 查看现有用户
1
SELECT user, host FROM mysql.user;
  1. 修改密码
1
ALTER USER 'dev'@'%' IDENTIFIED BY 'newpassword';
  1. 删除用户
1
DROP USER 'dev'@'%';
权限管理
权限 说明
SELECT 查询数据
INSERT 插入数据
UPDATE 更新数据
DELETE 删除数据
CREATE 创建数据库/表
DROP 删除数据库/表
ALTER 修改表结构
INDEX 创建索引
ALL PRIVILEGES 所有权限(不包含 GRANT 权限)
GRANT OPTION 允许把自己的权限再授予别人
  1. 授权
1
2
3
4
5
6
7
-- 对 company 的所有表,赋予所有权限
GRANT ALL PRIVILEGES ON company.* TO 'dev'@'%';

-- 授予部分权限  
GRANT SELECT, INSERT, UPDATE
ON company.employees
TO 'dev'@'%';
  1. 取消权限
1
2
3
4
5
6
7
-- 部分撤销
REVOKE INSERT, UPDATE
ON company.employees
FROM 'dev'@'%';

-- 全部撤销
REVOKE ALL PRIVILEGES ON company.* FROM 'dev'@'%';

内置函数

字符串函数

  1. LENGTH/CHAR_LENGTH

获取长度

1
2
SELECT LENGTH('Hello');       -- 字节长度
SELECT CHAR_LENGTH('Hello');  -- 字符长度

注意:LENGTH 获取中文字符时,一个中文等于 3 的长度

  1. CONCAT

拼接字符串

1
2
SELECT CONCAT(first_name, ' ', last_name) AS fullname
FROM users;
  1. SUBSTRING/LEFT/RIGHT

截取字符串

1
2
3
SELECT SUBSTRING('HelloWorld', 1, 5); -- Hello
SELECT LEFT('HelloWorld', 5);         -- Hello
SELECT RIGHT('HelloWorld', 5);        -- World
  1. REPLACE

替换字符串

1
SELECT REPLACE('2024-01-01', '-', '/')  -- 2024/01/01
  1. UPPER/LOWER

大小写转换

1
2
SELECT UPPER('hello');  -- HELLO
SELECT LOWER('Hello');  -- hello
  1. TRIM/LTRIM/RTRIM

去除空格

1
2
3
SELECT TRIM('   hi   ');   -- 'hi'
SELECT LTRIM('   hi');     -- 'hi'
SELECT RTRIM('hi   ');     -- 'hi'

数值函数

  1. ABS

绝对值

1
SELECT ABS(-5);  -- 5
  1. ROUND/CEIL/FLOOR
1
2
3
SELECT ROUND(3.14159, 2);  -- 3.14
SELECT CEIL(3.1);          -- 4
SELECT FLOOR(3.9);         -- 3
  1. RAND

随机数

1
2
SELECT RAND();     -- 0~1 随机小数
SELECT RAND()*100; -- 0~100
  1. MOD

取模

1
SELECT MOD(10, 3);  -- 1

日期与时间函数

  1. NOW/CURDATE/CURTIME
1
2
3
SELECT NOW();      -- 当前日期 + 时间
SELECT CURDATE();  -- 当前日期
SELECT CURTIME();  -- 当前时间
  1. DATE_FORMAT

格式化日期

1
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d %H:%i:%s');
  1. DATEDIFF/TIMESTAMPDIFF

计算时间差值

天数差

1
SELECT DATEDIFF('2024-02-01', '2024-01-01');  -- 31

任意单位差

1
SELECT TIMESTAMPDIFF(unit, datetime1, datetime2)
  1. DATE_ADD/DATE_SUB
1
2
SELECT DATE_ADD('2024-01-01', INTERVAL 30 DAY);
SELECT DATE_SUB(NOW(), INTERVAL 1 MONTH);

流程控制函数

  1. IF 条件判断
1
2
3
SELECT name,
       IF(salary > 10000, 'High', 'Low') AS salary_level
FROM employees;
  1. CASE WHEN
1
2
3
4
5
6
7
SELECT name,
       CASE
            WHEN salary >= 20000 THEN 'A'
            WHEN salary >= 15000 THEN 'B'
            ELSE 'C'
       END AS grade
FROM employees;

JSON 函数

  1. 获取 JSON 中的字段
1
2
3
4
SELECT JSON_EXTRACT(info, '$.age') FROM users;

-- 简写
SELECT info->'$.age' FROM users;
  1. 修改 JSON
1
SELECT JSON_SET(info, '$.age', 30) FROM users;
  1. 判断是否合法
1
SELECT JSON_VALID(info) FROM users;

其他常用函数

  1. COALESCE

返回第一个非 NULL 值

1
SELECT COALESCE(nickname, username, 'unknown') AS display_name FROM users;

约束

约束是保证数据正确性的关键机制

约束目的:

  • 保证数据有效
  • 保证数据唯一
  • 保证关系正确

约束类型

约束类型 作用 典型关键字
主键约束(PRIMARY KEY) 唯一标识每行记录 PRIMARY KEY
唯一约束(UNIQUE) 字段值不能重复 UNIQUE
非空约束(NOT NULL) 字段不能为空 NOT NULL
默认值约束(DEFAULT) 没填值时自动填充 DEFAULT
外键约束(FOREIGN KEY) 保证表之间引用关系 FOREIGN KEY
自增约束(AUTO_INCREMENT) 自动编号 AUTO_INCREMENT

主键约束

主键用于唯一表示一行记录,不能重复,不能为 NULL

1
2
3
4
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,  -- 通常搭配自增使用
    name varchar(50)
);

唯一约束

保证某列的数据不重复

1
2
3
4
5
6
7
8
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    email VARCHAR(100) UNIQUE
);

-- 可多个字段联合唯一  

UNIQUE (user_id, role_id)

非空约束

字段必须有值,不能为 NULL

1
name VARCHAR(50) NOT NULL

默认值约束

提供默认值,用户不填时自动赋值

1
status VARCHAR(20) DEFAULT 'active'

外键约束

外间保证表与表之间关系正确

1
2
3
4
5
CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT,
    FOREIGN KEY (user_id) REFERENCES users(id);
);

外键约束级联行为

行为 说明
CASCADE 父表删除/更新 → 子表同步删除/更新
SET NULL 父表删除/更新 → 子表字段置为 NULL
RESTRICT 不允许删除父表数据(默认行为)
NO ACTION 与 RESTRICT 类似

综合示例

1
2
3
4
5
6
7
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(50) UNIQUE NOT NULL,
    age INT DEFAULT 18,
    created_ad DATETIME DEFAULT CURRENT_TIMESTAMP
);

修改约束

  1. 唯一约束
1
2
3
ALTER TABLE users ADD UNIQUE (email); -- 添加

ALTER TABLE users DROP INDEX email; -- 删除
  1. 外键
1
2
3
4
5
ALTER TABLE orders
ADD CONSTRAINT fk_user
FOREIGN KEY (user_id) REFERENCES users(id); -- 添加

ALTER TABLE orders DROP FOREIGN KEY fk_user; -- 删除

多表查询(联表 & 子查询)

多表查询的核心问题是:当业务数据分散在多张表里时,怎么把他们组合起来查

多表关系

  1. 一对多

例子:一个用户会有多个订单。

1
users.id <- orders.user_id;
  1. 一对一

例子:用户与用户详情表

  1. 多对多

例子:学生和课程

通常使用一张中间表

1
student_course(student_id, course_id)

JOIN(连接查询)

JOIN 是横向拓展表,通过关联字段将数据拼在一起。

1
2
users(id, name)
orders(id, user_id. amount)
内连接(INNER JOIN)

只保留两张表都匹配到的数据

1
2
3
4
SELECT u.name, o.amount 
FROM users AS u
INNER JOIN orders AS o
    ON u.id = o.user_id;

左右两侧相交的部分。

左连接

保留左表全部数据,左表的记录将会全部表示出来,而右表只显示符合搜索条件的记录,右表记录不足的地方均为 NULL

1
2
3
4
SELECT u.name, o.amount
FROM users u
LEFT JOIN orders o
    ON u.id = o.user_id;
右连接

同上,保留的是右表

自连接

用于树形结构(员工-上级) 城市 parent_id 结构

示例(员工表):

1
employees(id, name, manager_id)

查询员工及其上级名称:

1
2
3
4
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m
    ON e.manager_id = m.id;
并集(UNION/UNION ALL)

把多条查询的结果上下拼在一起

  1. UNION (去重)
1
2
3
SELECT id FROM t1
UNION
SELECT id FROM t2;
  1. UNION ALL (不去重, 性能更高)
1
2
3
SELECT id FROM t1
UNION ALL
SELECT id FROM t2;

子查询

子查询就是一条查询套在另一条查询内部

子查询可能出现位置

  • WHERE 中
  • FROM 中
  • SELECT 后的字段位置
标量子查询(返回一个值)

例子:查金额 = 全库最大订单金额的订单

1
2
3
4
5
SELECT *
FROM orders
WHERE amount = (
    SELECT MAX(amount) FROM orders
);
列子查询(返回多行一列)

例子:查所有下单用户

1
2
3
4
5
SELECT *
FROM users
WHERE id IN {
    SELECT user_id FROM orders
};
行子查询(返回一整行)
1
2
3
4
5
SELECT *
FROM users
WHERE (id, name) = (
    SELECT id, name FROM users WHERE id = 1
);
表子查询(FROM 中当作一张表)

例子:先查出总金额大于 100 的订单,再按用户统计数量:

1
2
3
4
5
6
7
SELECT t.user_id, COUNT(*) AS cnt
FROM (
    SELECT user_id, amount
    FROM orders
    WHERE amount > 100
) AS t
GROUP BY t.user_id;

常用于:

  • 优化复杂 SQL
  • 分页优化
  • 统计类查询

多表查询练习

  1. 所有用户及其订单金额(无论是否下单)
1
2
3
4
SELECT u.id, u.name, o.amount
FROM users u
LEFT JOIN orders o 
    ON u.id = o.user_id;
  1. 查询下单金额最高的用户
1
2
3
4
SELECT u.name, o.amount
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.amount = (SELECT MAX(amount) FROM orders);
  1. 查询每个用户总订单金额
1
2
3
4
SELECT u.name, SUM(o.amount) AS total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;

事务

什么是事务: 一组要么全部成功,要么全部失败的操作。

SQL 中一次事务通常由多条 DML 语句组成:

  • INSERT
  • UPDATE
  • DELETE

事务保证:
要么所有语句都完成,要么全部回滚到操作前的状态。

为什么需要事务

例如用户转账,用户 A 扣除 100 元,用户 B 才能增加 100 元。

如果只执行了第一步,第二步失败了,会发生数据错乱。

所以必须把两步放进一个事务:

  • 两句 SQL 都成功 → 提交(COMMIT)

  • 任意一句失败 → 回滚(ROLLBACK)

事务基本语法

1
2
3
4
5
6
START TRANSACTION;  -- 也可以使用 BEGIN

UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

COMMIT;

如果需要撤销:

1
ROLLBACK;

ACID 四大特性

特性 解释 示例
A 原子性 (Atomicity) 要么全做,要么全不做 转账两个 UPDATE 要作为一个整体
C 一致性 (Consistency) 事务前后数据保持业务一致规则 A+B 余额保持不变
I 隔离性 (Isolation) 多个事务彼此互不影响 多人同时转账不冲突
D 持久性 (Durability) 提交后数据永久落盘 服务宕机也保留结果

并发事务问题

当多个事务同时执行时,会产生冲突问题。

  1. 脏读

一个事务读到了另一个事务未提交的数据

例子: 事务 A 修改余额 → 未提交 事务 B 读到了修改后的余额 但 A 回滚了 → B 读到的是不存在的数据

  1. 不可重复读

同一个事务的两次读取,结果不一致

  • 事务 A:SELECT balance FROM accounts WHERE id = 1 → 1000

  • 事务 B:UPDATE accounts SET balance = 900 WHERE id = 1; COMMIT

  • 事务 A 再查 → 900
    → 结果重复不了

  1. 幻读

同一个事务多次读取,出现了多出来或少的数据行

  • 事务 A:SELECT * FROM orders WHERE amount > 100(查到 5 条)

  • 事务 B:插入一条新订单 amount=200;COMMIT

  • 事务 A 再查:出现 6 条
    → 多了一条 “幻影记录”

MySQL 四大隔离级别

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED
READ COMMITTED (RC)
REPEATABLE READ (RR)(默认) ✔*
SERIALIZABLE

RR 下 MySQL 通过 间歇锁 + MVCC 解决大部分幻读问题(但仍可能出现)

如何查看和修改隔离级别

查看:

1
SELECT @@transaction_isolation;

设置:

1
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

系统级:

1
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

MySQL 默认隔离

默认隔离级别为:REPEATABLE READ

MySQL 在 RR 下的行为:

  • 不会脏读(有 MVCC)
  • 不会不可重复读(使用 READ View)
  • 绝大多数场景避免幻读
  • 幻读只可能在 当前读(SELECT … FOR UPDATE)场景发生

当前读 vs 快照读(MVCC)

InnoDB 事务有两种读取方式

类型 语句 特点
快照读 Snapshot Read SELECT 不加锁,使用 MVCC
当前读 Current Read SELECT … FOR UPDATE / LOCK IN SHARE MODE / UPDATE / DELETE 加锁,读取最新版本

快照读使用历史版本链(undo log),能避免不可重复读

事务的提交模式(自动提交)

MySQL 默认自动提交:

1
SHOW VARIABLES LIKE 'autocommit';

一般自动提交为 1,即每条语句都是一个独立事务。

若要手动控制事务,需要:

1
SET autocommit = 0;

事务使用最佳实践

  1. 一定要加 WHERE
    否则会全表更新,且无法回滚数据快照。

  2. 不要在长事务中执行大量 SELECT
    长事务会:

  • 占用大量 undo log

  • 阻塞 purge 清理

  • 让数据库膨胀

  1. 尽量让事务短小,快速完成

  2. 避免在事务中混入用户交互(例如等待用户输入)

Licensed under CC BY-NC-SA 4.0
最后更新于 Dec 27, 2025 20:29 +0800
发表了102篇文章 · 总计18万7千字
永远相信美好的事情即将发生。
使用 Hugo 构建
主题 StackJimmy 设计