0%

'mysql学习'

数据库操作

创建

名称若为关键词需用单引号

1
CREATE DATABASE [IF NOT_EXISTS] XX

查看/删除

1
2
3
SHOW DATABASES 查看数据库
SHOW CREATE DATABASE XXX 数据库创建语句
DROP DATABASE [IF EXITS] XX 删除数据库

备份恢复

必须在DOS执行
备份

1
mysqldump -u * -p * -B 数据库

生成 xx.sql文件
恢复

1
2
Source xx.sq了、

表操作

创建表

1
2
3
4
CREATE TABLE table_name
{
field datatype
}

character 字符集 (不指定默认用数据库字符集)
collate 校队规则
ENGINE

修改表

ALTER TABLE table_name

  • ADD 添加列
  • MODIFY 修改列
  • DROP 删除列
  • Rename table name to new_name 修改表名
  • ALTER table name character set 字符集 修改字符集
  • NOT NULL DEFAULT 默认不为空
  • DESC 查看字段

数据类型

数值类型

  1. 整型

     tinyint[1字节] (-127~127) unsigned(0~255)
     smallint[2]
     mediumint[3]
     int[4]
     bigint[8]
    
  2. 小数类型

     float[4]
     double[8]
     decimal[M,D] (M为小数位数总数,D为小数点后的位数)(M默认10,D默认0)max(65,30)
    

文本类型

  1. char(0~255)
  2. varchar(065535[2^16-1]) 注:utf-8编码最大21844(65535 - 3)/3 13个字节用于记录大小
  3. text(0~2^16-1)
  4. longtext(2^32-1)

细节

  • 1.char(4)和varchar(4)中的4表示字符
  • 2.char(4)是定长,varchar(4)是变长

二进制数据

  1. blob(0~2^16-1)
  2. longblob(2^32-1)

日期

  1. date [日期 年月日]
  2. time [时间 时分秒]
  3. datetime [YYYY-MM-DD HH:mm:ss]
  4. timestamp 时间戳 自动更新:NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

增删改查

INSERT

1
2
INSERT INTO table_name(columns)
VALUES (具体数值)

注意:

  1. 日期和字符数据需包含在单引号中
  2. 可以插入空值
  3. insert into table () values (),(),()可以添加多条记录
  4. 给表中所有字段添加数据可以不写字段名称

UPDATE

1
2
3
UPDATE name
SET COLUNMS
(WHERE ...)

DETELE

1
2
DELETE name FROM
WHERE ...

注意:

  1. delete不能单独删除一列的数据
  2. delete只删除记录。不删除表本身

SELECT

1
2
SELECT [DISTINCT] * | {COLUMN}
FROM table_name

注意:

  1. DISTINCT 是否去重
  2. 使用表达式对查询的列进行运算:
1
2
SELECT *|{COLUMN1|EXPRESSION}
FROM table_name
  1. AS 语句
1
SELECT column_name as 别名 from table
  1. WHERE 子句:条件筛选

🔹 逻辑运算符(组合条件)

运算符 说明
AND 逻辑与(全部为真才真)
OR 逻辑或(任一为真即真)
NOT 逻辑非(取反)

✅ 注意:AND 优先级高于 OR,建议用括号 () 明确逻辑分组,例如:

1
WHERE (status = 'active' OR level > 5) AND NOT is_deleted

🔹 比较运算符(判断单值/集合)

类型 运算符 / 语法 示例
常规比较 =, <>!=, <, <=, >, >= age > 18
区间匹配 BETWEEN val1 AND val2 score BETWEEN 60 AND 100(含边界)
集合成员判断 IN (val1, val2, ...) dept_id IN (1, 3, 5)
模糊匹配 LIKE / NOT LIKE + 通配符 name LIKE 'A%'%:任意字符,_:单字符)
空值判断(⚠️特殊) IS NULL / IS NOT NULL email IS NULL(不能用 = NULL!)

📌 提示:

  • NULL 表示“缺失/未知”,任何与 NULL 的比较(如 =, !=)结果均为 UNKNOWN
  • 字符串模糊匹配注意大小写敏感性(依赖数据库设置,如 MySQL 默认不区分,PostgreSQL 区分)。
  1. ORDER BY 排序查询结果
  • 升序 ASC
  • 降序 DESC

合计函数 COUNT

返回行的总数
SELECT COUNT *|COLUMN FROM table_name

合计函数 SUM

返回满足WHERE条件的行的和

1
SELECT SUM (COLUMN) FROM table_name

合计函数 AVG

返回平均值

1
SELECT AVG (COLUMN) FROM table_name

合计函数 MAX/MIN

1
SELECT MAX/MIN (COLUMN) FROM table_name

GROUP BY 分组查询 + HAVING 限制显示结果

字符串函数

🔤 一、基础操作

函数 说明 示例
LENGTH(str) 返回字符串的字节长度(注意:UTF8 中中文占 3 字节) LENGTH('你好')6
CHAR_LENGTH(str)CHARACTER_LENGTH(str) 返回字符串的字符个数 CHAR_LENGTH('你好')2
UPPER(str) / UCASE(str) 转大写 UPPER('Hello')'HELLO'
LOWER(str) / LCASE(str) 转小写 LOWER('Hello')'hello'
TRIM([{BOTH|LEADING|TRAILING} [remstr] FROM] str) 去除首尾空格或指定字符 TRIM(' abc ')'abc'
TRIM(BOTH 'x' FROM 'xxabcxx')'abc'
LTRIM(str) 去除左侧空格 LTRIM(' abc')'abc'
RTRIM(str) 去除右侧空格 RTRIM('abc ')'abc'

🔗 二、拼接与拆分

函数 说明 示例
CONCAT(str1, str2, …) 拼接字符串(任一参数为 NULL,结果为 NULL CONCAT('a', 'b', 'c')'abc'
CONCAT_WS(sep, str1, str2, …) 用分隔符拼接(自动跳过 NULL CONCAT_WS(',', 'a', NULL, 'c')'a,c'
SUBSTRING(str, pos[, len])
SUBSTR(str, pos[, len])
从第 pos 位(从 1 开始)截取 len 个字符;pos 可为负(从末尾算) SUBSTR('MySQL', 2, 3)'ySQ'
SUBSTR('MySQL', -3)'SQL'
LEFT(str, n) 取左边 n 个字符 LEFT('MySQL', 3)'MyS'
RIGHT(str, n) 取右边 n 个字符 RIGHT('MySQL', 2)'QL'

🔍 三、查找与定位

函数 说明 示例
LOCATE(substr, str[, pos]) 返回 substrstr 中首次出现的位置(从 1 开始),可指定起始位置 LOCATE('S', 'MySQL')3
LOCATE('Q', 'MySQL', 4)4
INSTR(str, substr) LOCATE(substr, str),但参数顺序相反(兼容 Oracle) INSTR('MySQL', 'S')3
POSITION(substr IN str) LOCATE,标准 SQL 写法 POSITION('S' IN 'MySQL')3

🛠️ 四、替换与格式化

函数 说明 示例
REPLACE(str, from_str, to_str) str 中所有 from_str 替换为 to_str REPLACE('a-b-c', '-', '/')'a/b/c'
INSERT(str, pos, len, newstr) strpos 处删除 len 个字符,插入 newstr INSERT('Quadratic', 3, 4, 'What')'QuWhattic'
LPAD(str, len, pad) 左填充:用 padstr 补至 len 长(超长则截断) LPAD('abc', 5, '0')'00abc'
RPAD(str, len, pad) 右填充 RPAD('abc', 5, 'x')'abcxx'
REPEAT(str, n) str 重复 n REPEAT('Ha', 3)'HaHaHa'

五、高级处理(MySQL 8.0+)

函数 说明 示例
REGEXP_LIKE(str, pattern[, match_type]) 正则匹配(返回 1/0) SELECT 'abc123' REGEXP '^[a-z]+[0-9]+$'1
REGEXP_SUBSTR(str, pattern[, pos[, occ[, match_type]]]) 提取匹配的子串 REGEXP_SUBSTR('user@example.com', '@(.+)', 1, 1, 'i', 1)'example.com'
REGEXP_REPLACE(str, pattern, replace_str[, pos[, occ[, match_type]]]) 正则替换 REGEXP_REPLACE('abc123def', '[0-9]+', '#')'abc#def'
REGEXP_INSTR(str, pattern[, pos[, occ[, return_opt[, match_type]]]]) 返回匹配位置(支持更灵活控制) REGEXP_INSTR('abc123def', '[0-9]+')4

数学函数

以下是 MySQL 中常用的数学类函数(Mathematical Functions),适用于 MySQL 5.7 / 8.0+,按功能分类整理,附典型用法与注意事项。


一、基本运算与取整

函数 说明 示例 注意
ABS(X) 绝对值 ABS(-5)5 支持整数、浮点、DECIMAL
SIGN(X) 符号函数:正→1,负→-1,零→0 SIGN(-3.2)-1
MOD(N, M)N % MN MOD M 取模(余数) MOD(10, 3)1
-10 MOD 3-1
负数取模结果符号与被除数一致
FLOOR(X) 向下取整(不大于 X 的最大整数) FLOOR(3.9)3
FLOOR(-3.2)-4
CEIL(X) / CEILING(X) 向上取整(不小于 X 的最小整数) CEIL(3.1)4
CEIL(-3.2)-3
ROUND(X[, D]) 四舍五入;D 为小数位数(默认 0) ROUND(3.456, 2)3.46
ROUND(123, -1)120
D 可为负(对整数位取整)
TRUNCATE(X, D) 截断(直接舍弃小数,不四舍五入 TRUNCATE(3.999, 1)3.9
TRUNCATE(123.456, -1)120
常用于精度控制

二、幂、指数与对数

函数 说明 示例
POW(X, Y) / POWER(X, Y) X 的 Y 次方 POW(2, 3)8
POW(4, 0.5)2(开平方)
SQRT(X) 平方根(X ≥ 0) SQRT(16)4
EXP(X) e 的 X 次方 EXP(1)2.718281828459045
LN(X) 自然对数(以 e 为底,X > 0) LN(EXP(2))2
LOG(X) 自然对数(同 LN(X) LOG(2.71828)1
LOG(B, X) 以 B 为底的对数(X > 0, B > 0, B ≠ 1) LOG(10, 1000)3
LOG2(X) 以 2 为底的对数 LOG2(8)3
LOG10(X) 以 10 为底的对数 LOG10(100)2

三、三角函数(角度单位:弧度

函数 说明 示例
SIN(X), COS(X), TAN(X) 正弦、余弦、正切 SIN(PI()/2)1
ASIN(X), ACOS(X), ATAN(X) 反正弦、反余弦、反正切 ASIN(1)1.570796...(≈π/2)
ATAN(Y, X)ATAN2(Y, X) 四象限反正切(返回点 (X, Y) 的角度) ATAN2(1, 1)0.785398...(≈π/4)
COT(X) 余切(= 1 / TAN(X)) COT(PI()/4)1
DEGREES(X) 弧度 → 角度 DEGREES(PI())180
RADIANS(X) 角度 → 弧度 RADIANS(180)3.141592653589793

⚠️ 注意:三角函数入参为弧度,若需角度计算,先用 RADIANS() 转换。


四、随机数

函数 说明 示例
RAND() 返回 [0, 1) 之间随机浮点数 RAND()0.732...
RAND(N) 用种子 N 初始化,之后每次调用 RAND() 生成可重现序列 SELECT RAND(1), RAND(), RAND(); → 每次执行结果一致

✅ 生成 [a, b] 区间随机整数:

1
2
3
FLOOR(a + RAND() * (b - a + 1))
-- 例如生成 [1, 100]:
SELECT FLOOR(1 + RAND() * 100);

五、进制与位运算(部分)

函数 说明 示例
BIN(N) 十进制 → 二进制字符串 BIN(10)'1010'
OCT(N) 十进制 → 八进制字符串 OCT(10)'12'
HEX(N) / HEX(str) 十进制 → 十六进制字符串;或字符串转 HEX HEX(255)'FF'
HEX('A')'41'
CONV(N, from_base, to_base) 任意进制转换(2~36 进制) CONV('FF', 16, 10)'255'
CONV('1010', 2, 16)'A'

实用示例

  1. 计算用户年龄(假设 birth_date 为 DATE 类型)

    1
    2
    3
    4
    SELECT 
    name,
    FLOOR(DATEDIFF(CURDATE(), birth_date) / 365.25) AS age
    FROM users;
  2. 评分归一化到 [0, 100]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    SELECT 
    score,
    ROUND(100 * (score - min_score) / (max_score - min_score), 1) AS normalized_score
    FROM (
    SELECT score,
    (SELECT MIN(score) FROM exams) AS min_score,
    (SELECT MAX(score) FROM exams) AS max_score
    FROM exams
    ) t;
  3. 生成带随机延迟的模拟数据

    1
    2
    SELECT 
    NOW() + INTERVAL FLOOR(RAND() * 3600) SECOND AS random_time;

以下是 MySQL 中常用的日期与时间函数(Date and Time Functions),适用于 MySQL 5.7 / 8.0+,按功能分类整理,附详细说明、典型示例与实用技巧。


一、获取当前日期/时间

函数 说明 示例 返回类型
NOW() / SYSDATE() 当前日期时间(YYYY-MM-DD HH:MM:SS NOW()'2025-12-07 15:30:45' DATETIME
CURDATE() / CURRENT_DATE 当前日期 CURDATE()'2025-12-07' DATE
CURTIME() / CURRENT_TIME 当前时间 CURTIME()'15:30:45' TIME
UTC_DATE() UTC 日期 UTC_DATE()'2025-12-07' DATE
UTC_TIME() UTC 时间 UTC_TIME()'07:30:45' TIME
UTC_TIMESTAMP() UTC 日期时间 UTC_TIMESTAMP()'2025-12-07 07:30:45' DATETIME

NOW() 返回语句开始执行时的时间;SYSDATE() 返回函数执行时的时间(在 SLEEP() 或触发器中差异明显)。


二、日期时间构造与提取

函数 说明 示例
DATE(expr) 提取日期部分 DATE('2025-12-07 15:30:45')'2025-12-07'
TIME(expr) 提取时间部分 TIME('2025-12-07 15:30:45')'15:30:45'
YEAR(date) 年份(1000–9999) YEAR('2025-12-07')2025
MONTH(date) 月份(1–12) MONTH('2025-12-07')12
DAY(date) / DAYOFMONTH(date) 日期(1–31) DAY('2025-12-07')7
HOUR(time) 小时(0–23) HOUR('15:30:45')15
MINUTE(time) 分钟(0–59) MINUTE('15:30:45')30
SECOND(time) 秒(0–59) SECOND('15:30:45')45
DAYOFWEEK(date) 星期几(1=周日, 2=周一,…,7=周六) DAYOFWEEK('2025-12-07')1(2025-12-07 是周日)
WEEKDAY(date) 星期几(0=周一, 1=周二,…,6=周日) WEEKDAY('2025-12-07')6
DAYOFYEAR(date) 一年中的第几天(1–366) DAYOFYEAR('2025-12-07')341
WEEK(date[, mode]) 一年中的第几周(mode 控制起始日与范围) WEEK('2025-12-07')49(默认周日为每周首日)
YEARWEEK(date[, mode]) 返回 YYYYWW 格式(年+周) YEARWEEK('2025-12-07')202549

推荐用 WEEK(date, 1)周一为每周首日,第1周需 >=4 天(ISO 标准兼容)。


三、日期时间格式化与解析

函数 说明 示例
DATE_FORMAT(date, format) 按格式输出日期字符串 DATE_FORMAT(NOW(), '%Y年%m月%d日 %H:%i')'2025年12月07日 15:30'
TIME_FORMAT(time, format) 按格式输出时间字符串 TIME_FORMAT('15:30:45', '%H时%i分')'15时30分'
STR_TO_DATE(str, format) 按格式将字符串转为日期/时间 STR_TO_DATE('2025/12/07', '%Y/%m/%d')'2025-12-07'

常用格式符(DATE_FORMAT / STR_TO_DATE

格式符 说明 示例
%Y 4 位年 2025
%y 2 位年 25
%m 2 位月(01–12) 12
%c 月(1–12,无前导零) 12
%d 2 位日(01–31) 07
%e 日(1–31,无前导零) 7
%H 24 小时制(00–23) 15
%h / %I 12 小时制(01–12) 03
%i 分钟(00–59) 30
%s / %S 秒(00–59) 45
%p AM/PM PM
%W 星期名(英文全称) Sunday
%a 星期缩写 Sun
%M 月名(英文全称) December
%b 月缩写 Dec

示例:中文日期格式

1
2
SELECT DATE_FORMAT(NOW(), '%Y年%m月%d日 %W %H:%i');
-- 输出:2025年12月07日 Sunday 15:30

四、日期时间运算(加减间隔)

核心函数:DATE_ADD() / DATE_SUB() / INTERVAL

函数 说明 示例
DATE_ADD(date, INTERVAL expr unit) 日期加间隔 DATE_ADD('2025-12-07', INTERVAL 3 DAY)'2025-12-10'
DATE_SUB(date, INTERVAL expr unit) 日期减间隔 DATE_SUB(NOW(), INTERVAL 1 HOUR) → 1 小时前
date + INTERVAL expr unit 等价于 DATE_ADD(更简洁) CURDATE() + INTERVAL 7 DAY → 7 天后
date - INTERVAL expr unit 等价于 DATE_SUB NOW() - INTERVAL 30 MINUTE → 30 分钟前

支持的 unit 类型(常用)

单位 说明 示例
DAY, HOUR, MINUTE, SECOND 日、时、分、秒 INTERVAL 2 HOUR
WEEK, MONTH, QUARTER, YEAR 周、月、季、年 INTERVAL 1 MONTH
DAY_HOUR '天 小时' 格式 INTERVAL '2 05' DAY_HOUR → 2天5小时
HOUR_MINUTE '小时:分' 格式 INTERVAL '1:30' HOUR_MINUTE → 1小时30分
MINUTE_SECOND '分:秒' 格式 INTERVAL '30:15' MINUTE_SECOND → 30分15秒
YEAR_MONTH '年-月' 格式 INTERVAL '1-6' YEAR_MONTH → 1年6个月

示例:获取本月第一天/最后一天

1
2
3
4
5
6
7
8
9
-- 本月第一天
SELECT DATE_FORMAT(CURDATE(), '%Y-%m-01');
-- 或
SELECT CURDATE() - INTERVAL DAYOFMONTH(CURDATE())-1 DAY;

-- 本月最后一天(MySQL 8.0+ 推荐)
SELECT LAST_DAY(CURDATE());
-- 兼容旧版:
SELECT DATE_ADD(DATE_FORMAT(CURDATE(), '%Y-%m-01'), INTERVAL 1 MONTH) - INTERVAL 1 DAY;

五、日期差值计算

函数 说明 示例
DATEDIFF(expr1, expr2) 日期差(天数),忽略时间部分 DATEDIFF('2025-12-10', '2025-12-07')3
TIMEDIFF(expr1, expr2) 时间差(TIME 类型),仅限 TIME 或同日 DATETIME TIMEDIFF('15:30:00', '14:00:00')'01:30:00'
TIMESTAMPDIFF(unit, datetime1, datetime2) 按指定单位计算差值(更灵活) TIMESTAMPDIFF(HOUR, '2025-12-07 10:00', NOW()) → 相差小时数

TIMESTAMPDIFFunitYEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND
示例:计算年龄(精确到月)

1
2
3
4
5
SELECT 
name,
TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age_years,
TIMESTAMPDIFF(MONTH, birth_date, CURDATE()) % 12 AS extra_months
FROM users;

六、其他实用函数

函数 说明 示例
LAST_DAY(date) 返回该月最后一天 LAST_DAY('2025-02-10')'2025-02-28'
MAKEDATE(year, dayofyear) 用年份 + 年内第几天构造日期 MAKEDATE(2025, 341)'2025-12-07'
MAKETIME(hour, minute, second) 构造时间 MAKETIME(15, 30, 45)'15:30:45'
FROM_DAYS(N) 将天数(自公元0年1月1日起)转为日期 FROM_DAYS(738864)'2025-12-07'
TO_DAYS(date) 日期转天数(同上) TO_DAYS('2025-12-07')738864

数学类函数


一、基础算术与取整

函数 说明 示例 返回类型
ABS(X) 绝对值 ABS(-5.3)5.3 与输入一致(DECIMAL/DOUBLE/BIGINT
SIGN(X) 符号:正→1,负→-1,零→0 SIGN(-10)-1 整数
MOD(N, M)
N % M
N MOD M
取模(余数) MOD(17, 5)2
MOD(-17, 5)-2
N
FLOOR(X) 向下取整(≤ X 的最大整数) FLOOR(3.9)3
FLOOR(-2.1)-3
整数
CEIL(X) / CEILING(X) 向上取整(≥ X 的最小整数) CEIL(3.1)4
CEIL(-2.1)-2
整数
ROUND(X)
ROUND(X, D)
四舍五入;D 为小数位(默认 0)
注意:当小数位为 5 时,向最近偶数取整(银行家舍入)
ROUND(2.5)2
ROUND(3.5)4
ROUND(123.456, 2)123.46
ROUND(123.456, -1)120
同输入(D=0 时为整数)
TRUNCATE(X, D) 截断(直接舍弃,不四舍五入 TRUNCATE(3.999, 2)3.99
TRUNCATE(123, -1)120
同输入

ROUND 的“银行家舍入”说明:
ROUND(1.5) = 2ROUND(2.5) = 2ROUND(3.5) = 4 —— 避免统计偏差。


二、幂、指数与对数

函数 说明 示例
POW(X, Y) / POWER(X, Y) X 的 Y 次方 POW(2, 3)8
POW(25, 0.5)5(开方)
SQRT(X) 平方根(X ≥ 0) SQRT(16)4
EXP(X) eX(自然指数) EXP(1)2.718281828459045
LN(X) 自然对数(logeX,X > 0) LN(EXP(2))2
LOG(X) LN(X) LOG(2.71828)1
LOG(B, X) 以 B 为底的对数(B > 0, B ≠ 1, X > 0) LOG(2, 8)3
LOG2(X) 以 2 为底对数(常用于信息熵、位运算) LOG2(1024)10
LOG10(X) 以 10 为底对数(常用对数) LOG10(1000)3

输入为负或零时,对数函数返回 NULL 并警告。


三、三角函数(弧度制

函数 说明 示例
PI() 返回 π(≈ 3.141593) PI()3.141593
SIN(X) 正弦(X 为弧度) SIN(PI()/2)1
COS(X) 余弦 COS(0)1
TAN(X) 正切 TAN(PI()/4)1
ASIN(X) 反正弦(值域 [-1,1]) ASIN(1)1.570796(≈π/2)
ACOS(X) 反余弦(值域 [-1,1]) ACOS(0)1.570796
ATAN(X) 反正切(单参数) ATAN(1)0.785398(≈π/4)
ATAN(Y, X) / ATAN2(Y, X) 四象限反正切(推荐用 ATAN2 ATAN2(1, 1)0.785398
ATAN2(-1, -1)-2.356194(第三象限)
COT(X) 余切 = 1 / TAN(X) COT(PI()/4)1
DEGREES(X) 弧度 → 角度 DEGREES(PI())180
RADIANS(X) 角度 → 弧度 RADIANS(90)1.570796

角度计算模板:

1
2
-- 计算 30 度的正弦值
SELECT SIN(RADIANS(30)); -- → 0.5

四、随机数

函数 说明 示例
RAND() 返回 [0, 1) 的随机浮点数 SELECT RAND();0.7321...
RAND(N) 用种子 N 初始化随机数生成器,之后调用 RAND() 可重现序列 SELECT RAND(1), RAND(), RAND();
→ 每次执行结果相同

常用随机数生成公式

1
2
3
4
5
6
7
8
-- [a, b] 区间随机整数(含端点)
SELECT FLOOR(a + RAND() * (b - a + 1));

-- 示例:生成 [1, 100] 随机整数
SELECT FLOOR(1 + RAND() * 100);

-- 生成 6 位随机数字验证码
SELECT LPAD(FLOOR(RAND() * 1000000), 6, '0');

五、进制与位运算辅助(部分)

函数 说明 示例
BIN(N) 十进制 → 二进制字符串 BIN(10)'1010'
OCT(N) 十进制 → 八进制字符串 OCT(9)'11'
HEX(N) 十进制 → 十六进制字符串 HEX(255)'FF'
CONV(N, from_base, to_base) 任意进制转换(2~36 进制) CONV('FF', 16, 10)'255'
CONV('1010', 2, 16)'A'

注意:BIN(), HEX() 等返回的是字符串,不是数值。


六、聚合中的数学应用(补充)

虽属聚合函数,但常与数学结合:

1
2
3
4
5
6
7
8
SELECT
AVG(score) AS avg_score,
STDDEV_POP(score) AS population_std, -- 总体标准差
STDDEV_SAMP(score) AS sample_std, -- 样本标准差(更常用)
VARIANCE(score) AS variance,
-- 百分位(MySQL 8.0+)
PERCENT_RANK() OVER (ORDER BY score) AS pct_rank
FROM scores;

STDDEV_POP vs STDDEV_SAMP

  • 总体标准差:分母为 N
  • 样本标准差:分母为 N-1(无偏估计)

附:常见错误与注意事项

问题 说明
ROUND(2.5) ≠ 3 因采用银行家舍入(向最近偶数取整)
MOD(-7, 3) = -1 MySQL 中 MOD(a,b) 符号与 a 一致(不同于 Python)
三角函数输入为角度 必须先用 RADIANS() 转换
LOG(0) / LOG(-1) 返回 NULL(对数定义域限制)
SQRT(-1) 返回 NULL(实数域无解)

加密函数

  1. USER() 查询用户
  2. DATABASE() 当前数据库名称
  3. MD5(str)
  4. PASSWORD(str)
  5. SELECT * FROM mysql.user \G

流程控制函数

  1. IF
  2. IFNULL
  3. SELECT CASE XX THEN XX

查询增强

  • LIKE % 表示0到多个字符 、 - 表示单个字符

分页查询

1
SELECT XXX LIMIT START, LOWS

从start + 1 开始,取rows行

公式:

1
LIMIT 每页显示数 * 第几页 - 1,每页显示数

多表查询

直接查询两张表

从第一张表N取一行与第二张表M每一行进行组合,一共返回 N * M 行

解决方法

WHERE 过滤

注:查询条件不能少于 表数 - 1,否则会出现笛卡尔积

若表 A 有 1万 行,表 B 有 1万 行,笛卡尔积就是 1亿行

自连接

  1. 把一张表当两张表用
  2. 需要给表起别名

子查询

单行子查询

子查询返回 0 行或 1 行(即至多一行结果)。

1
2
3
4
-- 查询工资高于平均工资的员工
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees); -- ← 单行子查询(AVG 返回一个值)

多行子查询

  • 必须配合多行操作符使用

常用操作符:

  • IN:等于列表中任意一个值
  • NOT IN:不等于列表中所有值
  • ANY / SOME:与子查询结果中的任意一个比较为真即满足
  • ALL:与子查询结果中的所有值比较都为真才满足
1
2
3
4
5
6
7
-- 查询工资高于“任一”经理工资的员工
SELECT name, salary
FROM employees
WHERE salary > ANY (
SELECT salary FROM employees WHERE job_title = '经理'
);
-- 等价于:salary > (子查询中最小的经理工资)

多列子查询

返回多个列数据的子查询语句。

1
2
3
4
5
6
7
8
-- 高薪岗位定义:salary > 10000 的 (dept_id, job_title) 组合
SELECT name, dept_id, job_title
FROM employees
WHERE (dept_id, job_title) IN (
SELECT dept_id, job_title
FROM employees
WHERE salary > 10000
);

数据库会把 (col1, col2) 视为一个逻辑元组(tuple) 进行匹配。

表复制

只复制表结构

1
CREATE TABLE new_table LIKE old_table;

复制全部数据

1
2
CREATE TABLE new_table AS
SELECT * FROM old_table;

合并查询

合并查询”在 SQL 中通常指将多个查询结果集纵向拼接为一个结果集,核心语法是 UNIONUNION ALLINTERSECTEXCEPT(或 MINUS)。下面以标准 SQL 为主,结合主流数据库(MySQL / PostgreSQL / Oracle / SQL Server)差异,系统讲解:


✅ 一、核心操作符对比

操作符 作用 去重? 排序? MySQL PostgreSQL SQL Server Oracle
UNION 合并结果,去重 ✅(隐式按列排序) ✔️ ✔️ ✔️ ✔️
UNION ALL 合并结果,保留重复 ❌(保持原顺序) ✔️ ✔️ ✔️ ✔️
INTERSECT 取多个查询交集(都存在的行) ✔️ ✔️ ✔️
EXCEPT / MINUS 差集(第一查询有、后续没有的行) ❌(可用 NOT EXISTS 替代) EXCEPT ✔️ EXCEPT ✔️ MINUS ✔️

🔔 MySQL 不支持 INTERSECTEXCEPT,需用 INNER JOIN / NOT EXISTS 模拟(后文给出方案)。


二、UNIONUNION ALL

基本规则

  1. 列数必须相同
  2. 对应列数据类型需兼容(如 INT + VARCHAR 可能隐式转换);
  3. 结果集列名取自第一个查询
  4. 每个 SELECT 可独立使用 WHEREGROUP BYHAVING,但不能单独用 ORDER BY(除非加括号+LIMIT)

示例:合并销售数据(按季度分表)

1
2
3
4
5
6
7
8
9
10
11
12
-- 合并 Q1、Q2 销售记录,去重(如避免重复录入)
SELECT product, amount, 'Q1' AS quarter
FROM sales_q1
WHERE amount > 1000

UNION -- ← 自动去重 + 排序

SELECT product, amount, 'Q2'
FROM sales_q2
WHERE amount > 1000

ORDER BY amount DESC; -- 全局排序

UNION ALL 高性能场景

1
2
3
4
5
6
-- 日志按天分表,合并最近3天(无需去重)
SELECT * FROM log_20251206
UNION ALL
SELECT * FROM log_20251207
UNION ALL
SELECT * FROM log_20251208;
  • ⚡ 比 UNION 快 5~10 倍(省去排序+去重开销);
  • 适合:分区表合并、ETL 临时表整合、大数据分析。

✅ 三、交集 INNER JOININ

1
2
3
4
5
6
7
8
-- 方案1:INNER JOIN(推荐,性能好)
SELECT v.user_id
FROM vip_users v
INNER JOIN active_users a ON v.user_id = a.user_id;

-- 方案2:IN + 子查询
SELECT user_id FROM vip_users
WHERE user_id IN (SELECT user_id FROM active_users);

四、差集 NOT EXISTS

1
2
3
4
5
SELECT r.user_id
FROM registered_users r
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = r.user_id
);

避免用 NOT IN(若 orders.user_idNULL,结果为空!)


✅ 五、高级技巧与注意事项

1️ 为每个来源打标签

1
2
3
4
SELECT name, email, 'internal' AS source FROM employees
UNION ALL
SELECT name, email, 'external' FROM partners
ORDER BY source, name;

2️ 处理列类型不一致

1
2
3
4
5
6
7
8
9
-- 错误:INT vs VARCHAR
SELECT id FROM table1
UNION
SELECT name FROM table2; -- 可能报错或隐式转换失败

-- 正确:显式转换
SELECT CAST(id AS CHAR) FROM table1
UNION
SELECT name FROM table2;

3️ 子查询中使用 UNION

1
2
3
4
5
6
7
SELECT *
FROM (
SELECT id, name FROM students
UNION ALL
SELECT id, name FROM teachers
) AS all_people
WHERE name LIKE '张%';

4️ ORDER BY 限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--  错误:不能在中间 SELECT 加 ORDER BY
SELECT * FROM t1 ORDER BY id
UNION ALL
SELECT * FROM t2;

-- 正确1:全局排序
(SELECT * FROM t1)
UNION ALL
(SELECT * FROM t2)
ORDER BY id;

-- 正确2:各子查询排序+LIMIT(需括号)
(SELECT * FROM t1 ORDER BY id LIMIT 5)
UNION ALL
(SELECT * FROM t2 ORDER BY id DESC LIMIT 5);

六、性能优化建议

场景 建议
大结果集合并 优先用 UNION ALL,避免无意义去重
需去重但数据量大 UNION ALL + 外层 DISTINCT,或用 GROUP BY
多表 UNION 确保各 SELECTWHERE 条件能走索引
分页合并 外层分页,而非每个子查询分页(否则逻辑错)

约束

主键

  1. 主键列的值不能重复 PRIMARY KEY
  2. 值不能为NULL
  3. 可以复合,但必须唯一
  4. DESC 会显示主键

UNIQUE

被添加的值不能重复

外键

1
FOREIGN KEY (外键) REFERENCES table (属性)
  1. 外键类型与主键要一致
  2. 外键指向主键或UNIQUE
  3. 外键字段的值,必须出现在主键中,或为空
  4. 建立主外键关系后,数据不能随意删除

check

MySQL5.7不支持,只做语法校验

自增长

AUTO_INSERT

  • 自增长默认从1开始,但可修改
  • 通常搭配主键或UNIQUE

索引

MySQL 索引底层结构:B+ 树

MySQL 默认(InnoDB 引擎)使用 B+ 树(B+ Tree) 实现索引,不是哈希,也不是普通二叉树

B+ 树特点(InnoDB):

特性 说明
多路平衡查找树 每个节点可存多个 key,树高度低(通常 3~4 层可存千万级数据)→ 减少磁盘 IO
非叶子节点只存 key 不存数据,只存索引导航信息,单页可存更多 key,树更矮
叶子节点存完整数据(聚簇索引)或 主键值(二级索引) 叶子节点之间用双向链表连接 → 利于范围查询、排序
数据按 key 顺序存储 插入时可能分裂节点,但查询效率稳定

示意图简化版

1
2
3
4
5
              [20, 50]               ← 根节点(内存中常驻)
/ | \
[5,10,15] [25,30,40] [55,60,70] ← 非叶子节点
/ | | \ / | | \ / | | \
[1..5]...[15..20] ... [70..∞] ← 叶子节点(存数据或主键),双向链表连接

知识点:

  • 一次查询 = 树的高度次磁盘 IO(例如 3 层 → 3 次 IO);
  • 全表扫描 = 扫所有叶子节点(数据页);
  • 范围查询(如 WHERE id BETWEEN 10 AND 30)只需在叶子节点链表上顺序遍历,效率高。

MySQL 索引类型(按存储 & 逻辑分)

1️ 按存储结构分(InnoDB 引擎)

类型 中文名 说明 是否必须?
Clustered Index 聚簇索引 叶子节点直接存储整行数据。每个表只能有一个(因为数据只能按一种顺序物理存储)。InnoDB 中,主键就是聚簇索引 ✅ 是(无主键时,InnoDB 隐式创建 6 字节 row_id 作聚簇索引)
Secondary Index 二级索引(辅助索引) 叶子节点只存索引列 + 主键值。查数据时需“回表”(先查二级索引 → 拿主键 → 再查聚簇索引)。 ❌ 按需创建

举例(表:users(id PK, name, age)

  • 聚簇索引:按 id 组织数据页 → 查 WHERE id=100 → 1 次 IO 直接得数据。
  • 二级索引 idx_name (name)
    • 叶子节点存 (name, id)
    • WHERE name='Alice' → 先在 idx_name 找到 'Alice', id=500 → 再用 id=500 回表查聚簇索引 → 2 次 IO(即“回表”)。

优化技巧:覆盖索引(Covering Index)
如果查询的列全部包含在索引中,就无需回表!

1
2
3
4
5
-- 创建联合索引 (name, age)
CREATE INDEX idx_name_age ON users(name, age);

-- 查询只涉及 name 和 age → 直接从索引取数据,不回表!
SELECT name, age FROM users WHERE name = 'Alice';

2️ 按逻辑功能分(常用)

类型 语法 特点 适用场景
主键索引(PRIMARY KEY) PRIMARY KEY (id) 唯一、非空、聚簇索引 每表必有(显式或隐式)
唯一索引(UNIQUE) UNIQUE KEY (email) 值唯一(可 NULL,但只能一个 NULL) 身份证号、邮箱等唯一字段
普通索引(INDEX / KEY) INDEX idx_age (age) 无限制,可重复、可 NULL 高频查询字段(如状态、分类)
联合索引(Composite Index) INDEX idx_a_b_c (a, b, c) 多列组合,最左前缀原则生效 组合查询(如 WHERE a=? AND b=?
前缀索引 INDEX idx_url (url(20)) 只索引字符串前 N 字符,省空间 长文本(URL、描述),但可能降低区分度
全文索引(FULLTEXT) FULLTEXT (content) 支持 MATCH ... AGAINST 模糊搜索 文章内容、评论搜索(MyISAM / InnoDB ≥ 5.6)
空间索引(SPATIAL) SPATIAL INDEX (geom) 用于地理数据(POINT, POLYGON) GIS 应用

联合索引 & 最左前缀原则(面试高频!)

这是高效使用索引的关键

规则:

对联合索引 (a, b, c)

  • WHERE a = 1用索引
  • WHERE a = 1 AND b = 2用索引
  • WHERE a = 1 AND b = 2 AND c = 3用索引
  • WHERE a = 1 AND c = 3只用到 a(b 跳过,c 无效)
  • WHERE b = 2索引失效
  • WHERE c = 3索引失效
  • WHERE a = 1 AND b > 2 AND c = 3用到 a, b(c 无法用,因 b 是范围查询)

原理:B+ 树先按 a 排序,a 相同再按 b,再按 c
就像字典:先按首字母(a),再按第二字母(b)…… 要查“apple”,必须从 ‘a’ 开始;若直接查“pple”,无法定位。

正确建联合索引顺序:

1
2
3
4
5
6
7
8
-- 查询常为:WHERE status = 'active' AND create_time > '2024-01-01' ORDER BY user_id
-- 好顺序:(status, create_time, user_id)
-- 理由:
-- status = 常量 → 精准定位
-- create_time > 范围 → 可继续用索引范围扫描
-- user_id → 用于 ORDER BY,避免 filesort

CREATE INDEX idx_status_time_user ON orders(status, create_time, user_id);

如何判断索引是否生效?——EXPLAIN

这是调试索引的必备工具

1
EXPLAIN SELECT * FROM users WHERE name = 'Alice';

重点关注字段:

说明
type 访问类型:system > const > eq_ref > ref > range > index > ALL(越靠左越好,ALL=全表扫描)
key 实际使用的索引名
key_len 使用的索引长度(可判断联合索引用到几列)
rows 预估扫描行数(越少越好)
Extra 额外信息:
Using index → 覆盖索引(不回表)
⚠️ Using filesort → 内存/磁盘排序(慢)
⚠️ Using temporary → 用了临时表(慢)

示例:

1
2
EXPLAIN SELECT name, age FROM users WHERE name = 'Alice';
-- 若 key = idx_name_age, Extra = "Using index" → 完美覆盖索引!

索引失效的常见场景(避坑指南!)

场景 原因 解决方案
对字段做函数/运算 WHERE YEAR(create_time) = 2024 改写为范围:WHERE create_time BETWEEN '2024-01-01' AND '2024-12-31'
隐式类型转换 WHERE phone = 13800138000(phone 是 VARCHAR) 加引号:WHERE phone = '13800138000'
隐式字符集转换 关联字段字符集/排序规则不同 统一 CHARSETCOLLATE
LIKE 以通配符开头 WHERE name LIKE '%Alice%' 改用全文索引 / 模糊搜索中间件(Elasticsearch)
OR 条件未全覆盖索引 WHERE a=1 OR b=2(只有 a 有索引) UNION 拆分,或给 b 也加索引
不满足最左前缀 WHERE b=2(联合索引 (a,b) 调整索引顺序或建单列索引
数据区分度低 gender(只有 男/女)建索引 通常不建(除非配合其他列联合)

索引设计最佳实践(总结清单)

  1. 主键首选自增 INT/BIGINT
    → 避免 UUID 乱序插入导致页分裂(如需分布式 ID,用雪花算法 + 前移时间位)。

  2. 高频 WHERE / JOIN / ORDER BY 字段建索引
    → 先用 slow query logEXPLAIN 找慢查询。

  3. 优先考虑联合索引
    → 避免单列索引过多;按“等值在前,范围在后,排序最后”排序列。

  4. 善用覆盖索引
    SELECT 的列尽量包含在索引中,减少回表。

  5. 定期检查无效索引

    1
    2
    -- MySQL 5.7+:查看未使用的索引(需开启 performance_schema)
    SELECT * FROM sys.schema_unused_indexes;

    删除不用的索引,提升写性能。

  6. 大表加索引用 ALGORITHM=INPLACE(Online DDL)

    1
    2
    3
    ALTER TABLE users 
    ADD INDEX idx_name (name),
    ALGORITHM=INPLACE, LOCK=NONE; -- MySQL 5.6+ 支持,不锁表!
  7. 监控索引效率

    1
    SHOW INDEX FROM users;  -- 查看索引 Cardinality(区分度),越高越好

事务

用于保证数据的一致性,要么全部成功,要么全部失败。

概念

场景:用户 A 给用户 B 转账 100 元

1
2
3
4
5
-- 步骤1:A 扣款
UPDATE accounts SET balance = balance - 100 WHERE user = 'A';

-- 步骤2:B 收款
UPDATE accounts SET balance = balance + 100 WHERE user = 'B';

问题
如果步骤1成功,但步骤2因断电/崩溃失败 → A 少了 100,B 没收到 → 数据不一致!

事务的使命:把多个操作打包成一个“原子单元”,要么全成功,要么全失败回滚,绝不留半截状态!

1
2
3
4
5
6
7
START TRANSACTION;  -- 开启事务

UPDATE accounts SET balance = balance - 100 WHERE user = 'A';
UPDATE accounts SET balance = balance + 100 WHERE user = 'B';

COMMIT; -- 全部成功 → 永久生效
-- 或 ROLLBACK; -- 任一失败 → 撤销所有修改

设置保存点

1
2
SAVEPOINT A
ROLLBACK A (不具体指向默认回退到初始点)

事务的四大特性:ACID

字母 全称 含义 MySQL 如何保证
A Atomicity(原子性) 事务是最小单位,不可分割:要么全做,要么全不做 通过 Undo Log(回滚日志) 实现回滚
C Consistency(一致性) 事务前后,数据库从一个合法状态转移到另一个合法状态(如约束、触发器、业务规则仍成立) 由 A + I + D 共同保障 + 应用逻辑
I Isolation(隔离性) 多个事务并发执行时,互不干扰(如同串行执行) 通过 锁(Locking) + MVCC(多版本并发控制) 实现
D Durability(持久性) 事务一旦提交,结果永久保存(即使宕机也不丢) 通过 Redo Log(重做日志) + fsync 写入磁盘

关键理解

  • ACID 不是 MySQL “发明”的,而是事务的设计目标
  • InnoDB 引擎通过 WAL(Write-Ahead Logging) + Undo/Redo Log 实现 ACID;
  • MyISAM 不支持事务!务必用 InnoDB

事务隔离级别(解决并发问题)

多个事务同时操作数据,可能引发 3 类经典问题:

问题 描述 举例
脏读(Dirty Read) 读到未提交的中间数据 T1 改余额为 900(未提交)→ T2 读到 900 → T1 回滚 → T2 依据错误数据决策
不可重复读(Non-Repeatable Read) 同一事务内,多次读同一行,结果不同(被别人改了) T1 两次读 A 余额:第一次 1000 → T2 提交修改为 900 → T1 第二次读得 900
幻读(Phantom Read) 同一事务内,多次查同一范围,结果行数不同(被别人插入/删除) T1 SELECT * FROM orders WHERE amount > 100 得 5 行 → T2 插入一条 150 的订单 → T1 再查得 6 行

MySQL 的 4 种隔离级别(InnoDB 默认:REPEATABLE READ

隔离级别 脏读 不可重复读 幻读 实现方式 性能
READ UNCOMMITTED ❌ 允许 ❌ 允许 ❌ 允许 几乎无锁 ⚡ 最快
READ COMMITTED ✅ 禁止 ❌ 允许 ❌ 允许 行锁 + MVCC(每次读取最新已提交版本)
REPEATABLE READ(MySQL 默认) ⚠️ InnoDB 用 MVCC + 间隙锁基本解决 MVCC + Next-Key Lock(行锁+间隙锁)
SERIALIZABLE 所有读加共享锁,串行执行 最慢

🔍 重点:InnoDB 的 RR 级别如何防止幻读?

  • 快照读(普通 SELECT):通过 MVCC 读事务开始时的一致性视图,天然无幻读;
  • 当前读(SELECT … FOR UPDATE / LOCK IN SHARE MODE / UPDATE / DELETE):通过 Next-Key Lock(锁住记录 + 前后间隙)防止新记录插入。
1
2
3
4
-- 当前读示例:防止别人在 (100, 200) 之间插新订单
SELECT * FROM orders
WHERE amount > 100 AND amount < 200
FOR UPDATE; -- 加 Next-Key Lock

事务控制语句(实战语法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 1. 开启事务(显式)
START TRANSACTION;
-- 或
BEGIN;

-- 2. 执行 SQL(DML:INSERT/UPDATE/DELETE)
UPDATE accounts SET balance = balance - 100 WHERE user = 'A';
INSERT INTO transfers (from_user, to_user, amount) VALUES ('A', 'B', 100);

-- 3. 提交 or 回滚
COMMIT; -- 永久生效
-- ROLLBACK; -- 撤销所有修改(到 START TRANSACTION 之前)

-- 4. 设置保存点(部分回滚)
SAVEPOINT sp1;
UPDATE ...;
ROLLBACK TO sp1; -- 只回滚到 sp1,之前的操作仍保留

注意:

  • DDL(如 CREATE TABLE)、LOCK TABLES隐式提交当前事务;
  • 客户端自动提交模式:
    1
    2
    SELECT @@autocommit;  -- 1=开启(每条 SQL 自动 COMMIT),0=关闭
    SET autocommit = 0; -- 关闭自动提交,需手动 COMMIT

InnoDB 事务实现原理(深入理解)

核心组件:

组件 作用
Undo Log 存储修改前的旧值 → 用于回滚(ROLLBACK)和 MVCC 快照读
Redo Log 存储修改后的物理日志 → 崩溃恢复时重做(保证 Durability)
Write-Ahead Logging:先写日志,再写数据页
Read View 事务开启时生成的“一致性视图” → 决定能看到哪些版本的数据(MVCC 关键)
Next-Key Lock 行锁(Record Lock) + 间隙锁(Gap Lock) → 防止幻读

事务执行流程(简化):

  1. START TRANSACTION → 生成 Read View
  2. 执行 UPDATE
    • 修改 Buffer Pool 中的数据页;
    • Undo Log(旧值);
    • Redo Log Buffer(新值);
  3. COMMIT
    • Redo Log Buffer 刷盘(fsync)→ 事务持久化
    • 释放锁;
  4. 后台线程异步刷脏页到磁盘。

崩溃恢复:重启时,InnoDB 用 Redo Log 重做已提交事务,用 Undo Log 回滚未提交事务。


事务使用最佳实践 & 常见误区

推荐做法:

场景 建议
短事务 尽量缩短事务时间(避免 BEGIN 后做耗时计算/网络请求)→ 减少锁竞争
小批量提交 大量 INSERT/UPDATE 分批提交(如每 1000 行 COMMIT 一次)→ 防止 Undo Log 暴胀
明确隔离级别 按需设置(如报表用 READ COMMITTED,金融用 REPEATABLE READ):
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
异常处理 代码中捕获异常 → 显式 ROLLBACK(避免连接池残留未提交事务)
避免长事务 监控 information_schema.INNODB_TRX,杀掉长时间未提交事务

常见错误:

误区 后果 正确做法
BEGIN 后不 COMMIT/ROLLBACK 连接一直占着锁 → 阻塞其他事务 try-catch-finally 确保提交/回滚
在事务中调用外部 API 事务长时间挂起 → 锁超时、连接池耗尽 先提交事务 → 再调外部服务(用消息队列解耦)
默认隔离级别下误以为无幻读 当前读(FOR UPDATE)仍可能见幻读 理解 MVCC vs 当前读区别;必要时显式加锁
大事务导致主从延迟 Redo Log 太多,从库追不上 分批提交 + 并行复制

如何监控事务?

1
2
3
4
5
6
7
8
9
-- 1. 查看当前运行的事务(重点关注 trx_state, trx_started)
SELECT * FROM information_schema.INNODB_TRX;

-- 2. 查看锁等待
SELECT * FROM sys.innodb_lock_waits;

-- 3. 查看长事务(运行超过 60 秒)
SELECT * FROM information_schema.INNODB_TRX
WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;

存储引擎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SHOW ENGINES

mysql> SHOW ENGINES;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears)| NO | NO | NO |
| MyISAM | YES | Non-transactional engine with good read performance | NO | NO | NO |
| CSV | YES | CSV storage engine | NO | NO | NO |
| ARCHIVE | YES | Compression-focused storage engine for archival data | NO | NO | NO |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
| FEDERATED | NO | Federated MySQL storage engine (not available in 8.0+) | NULL | NULL | NULL |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
  • Support:DEFAULT = 默认引擎;YES = 支持但非默认;NO = 编译支持但禁用;DISABLED = 不支持
  • Transactions:是否支持事务(YES = 事务安全型;NO = 非事务安全型)
  • XA:是否支持分布式事务(XA 协议)
  • Savepoints:是否支持保存点(SAVEPOINT / ROLLBACK TO)

事务安全型

InnoDB(MySQL 5.5+ 默认引擎)

特性 说明
事务支持 ACID 全满足(靠 Undo Log + Redo Log + MVCC)
行级锁 并发写入性能高(MyISAM 是表锁)
外键约束 FOREIGN KEY 支持(数据完整性保障)
崩溃恢复 重启自动恢复,数据不丢失
MVCC 多版本并发控制,高并发读写无阻塞
全文索引 MySQL 5.6+ 支持(FULLTEXT
数据压缩 支持页压缩(节省磁盘)

非事务安全型

引擎 特点 适用场景 ⚠️ 主要缺陷
MyISAM • 表级锁(写阻塞读)
• 查询快(尤其 COUNT(*))
• 支持全文索引(旧版)
• 不支持事务/外键
• 只读报表库
• 日志归档(极少更新)
• 低频读、无并发写场景
• 崩溃后需手动修复(myisamchk
• 写入时全表锁 → 高并发下性能差
• 无崩溃恢复 → 可能丢数据
MEMORY • 数据全在内存
• 哈希/ B树索引
• 极快读写
• 临时缓存表
• 会话级临时数据
• 小型维度表
• 重启数据全丢
• 不支持 BLOB/TEXT
• 内存有限,易 OOM
CSV • 数据存为 CSV 文件
• 可直接用文本工具编辑
• 数据导入导出中转
• 与外部系统交换数据
• 无索引
• 不支持 NULL
• 全表扫描
ARCHIVE • 高压缩比(zlib)
• 只支持 INSERT / SELECT
• 日志、审计、历史归档(只追加不更新) • 无索引(全表扫描)
• 不支持 DELETE/UPDATE
BLACKHOLE • 写入即丢弃(/dev/null • 主从复制中“过滤”数据
• 压测写入吞吐
• 数据不持久化


如何查看/设置表的存储引擎?

1. 查看某张表的引擎:

1
2
3
4
SHOW CREATE TABLE users;  -- 看 CREATE 语句末尾的 ENGINE=...
-- 或
SELECT ENGINE FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'users';

2. 建表时指定引擎:

1
2
3
4
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
amount DECIMAL(10,2)
) ENGINE = InnoDB; -- 显式指定(推荐)

3. 修改已有表的引擎(谨慎!大数据量会锁表):

1
2
ALTER TABLE old_myisam_table ENGINE = InnoDB;
-- ⚠️ 大表建议用 pt-online-schema-change 工具在线转换

选择存储引擎决策树

1
2
3
4
5
6
7
8
9
10
11
12
graph TD
A[需要事务/外键/崩溃恢复?]
-->|是| B[选 InnoDB]
A -->|否| C{主要是读?写很少?}
C -->|是| D{需要极快 COUNT* 或全文检索(旧版)?}
D -->|是| E[考虑 MyISAM]
D -->|否| F{数据临时/内存足够?}
F -->|是| G[MEMORY]
F -->|否| H{只追加不更新?需高压缩?}
H -->|是| I[ARCHIVE]
H -->|否| J[仍推荐 InnoDB]
C -->|否(高并发写)| B

终极建议
除非有非常明确的特殊需求(如 MEMORY 做缓存),否则一律用 InnoDB
它是 MySQL 社区和官方持续优化的重点,功能、稳定性、性能综合最佳。


附:InnoDB vs MyISAM 关键对比表

特性 InnoDB MyISAM
事务 ✅ ACID
锁粒度 行锁 表锁
外键
崩溃恢复 ✅ 自动 ❌ 需手动修复
全文索引 ✅(5.6+) ✅(但旧版)
COUNT(*) 慢(需扫描) 快(存了行数)
存储空间 稍大(存双写缓冲等)
适用场景 通用、高可靠 只读/低写