正在加载文档...
文档内容较大,正在处理中,请稍候
正在加载文档...
文档内容较大,正在处理中,请稍候
本文档详细说明了 MySQL 数据库的常用操作,包括建库建表的字符集和排序规则、常用字段类型、常用查询语句、视图、触发器、存储过程、事务、备份恢复等,基于项目实际使用的数据库结构和规范。
本文档涵盖了 MySQL 数据库操作的各个方面,包括基础操作、高级特性、性能优化和最佳实践。主要内容包括:
基础操作:
高级特性:
数据库对象:
优化和维护:
参考和排查:
项目统一使用 utf8mb4 字符集,这是 MySQL 5.5.3+ 引入的字符集,支持完整的 UTF-8 编码,包括 4 字节的 emoji 表情符号。
推荐配置:
utf8mb4utf8mb4_unicode_ci(推荐)或 utf8mb4_general_ci| 字符集 | 说明 | 适用场景 |
|---|---|---|
utf8mb4 |
完整的 UTF-8 编码,支持 4 字节字符 | ✅ 推荐:支持 emoji、特殊字符 |
utf8 |
仅支持 3 字节 UTF-8,已过时 | ❌ 不推荐:不支持 emoji |
latin1 |
单字节字符集 | ❌ 不推荐:仅支持 ASCII |
| 排序规则 | 说明 | 适用场景 |
|---|---|---|
utf8mb4_unicode_ci |
✅ 推荐:基于 Unicode 标准排序,更准确 | 中文、英文、多语言混合 |
utf8mb4_general_ci |
基于简化规则排序,性能略好 | 性能要求高的场景 |
utf8mb4_bin |
二进制排序,区分大小写 | 需要区分大小写的场景 |
-- 创建数据库(如果不存在)
CREATE DATABASE IF NOT EXISTS wladmin
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- 使用数据库
USE wladmin;
-- 查看数据库字符集
SHOW CREATE DATABASE wladmin;
-- 修改数据库字符集(谨慎使用,会影响已存在的数据)
ALTER DATABASE wladmin CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;-- 创建表(项目标准格式)
CREATE TABLE user (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
username VARCHAR(50) NOT NULL COMMENT '用户名',
name varchar(100) NOT NULL DEFAULT '' COMMENT '姓名',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';关键配置:
ENGINE=InnoDB:使用 InnoDB 存储引擎(支持事务、外键)DEFAULT CHARSET=utf8mb4:默认字符集COLLATE=utf8mb4_unicode_ci:默认排序规则-- 修改表的字符集和排序规则
ALTER TABLE user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 查看表的字符集
SHOW CREATE TABLE user;
-- 查看表的字符集信息
SELECT
TABLE_SCHEMA,
TABLE_NAME,
TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user';-- 主键ID(推荐使用无符号)
id bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID'
-- 外键ID
user_id bigint(20) unsigned COMMENT '用户ID'
department_id bigint(20) unsigned COMMENT '部门ID'说明:
unsigned:无符号整数,范围 0 到 18,446,744,073,709,551,615NOT NULL:不允许为空AUTO_INCREMENT:自动递增(仅主键)-- 状态、类型、排序等
status int(11) NOT NULL DEFAULT '0' COMMENT '状态'
sort int(11) NOT NULL DEFAULT '1' COMMENT '排序号'
level int(11) NOT NULL DEFAULT '1' COMMENT '层级'-- 布尔值、状态标记(常用)
is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除'
is_system TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否系统预置(0-否,1-是)'
status tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否启用 1:停用;0:启用'
gender TINYINT(1) DEFAULT 1 COMMENT '性别 1:男;0:女, 2: 其他'说明:
TINYINT(1):通常用于布尔值(0/1)TINYINT(4):用于小范围的状态值-- 用户名、编码(短字符串)
username VARCHAR(50) NOT NULL COMMENT '用户名'
code VARCHAR(64) COMMENT '编码'
-- 姓名、标题(中等长度)
name varchar(100) NOT NULL DEFAULT '' COMMENT '姓名'
title VARCHAR(255) NOT NULL COMMENT '标题'
-- 地址、描述(较长字符串)
address VARCHAR(255) DEFAULT '' COMMENT '用户地址'
description VARCHAR(500) DEFAULT '' COMMENT '描述'
-- 路径、JSON字符串(超长字符串)
path VARCHAR(2500) DEFAULT '' COMMENT '树id的路径'说明:
VARCHAR(n):最大长度 n 个字符,实际存储根据内容长度变化VARCHAR 最大长度 65535 字节(utf8mb4 最多 16383 字符)-- 固定长度字符串(较少使用)
status_code CHAR(2) COMMENT '状态码'说明:
CHAR(n):固定长度 n 个字符,存储空间固定-- 内容、备注(大文本)
content MEDIUMTEXT NOT NULL COMMENT '内容'
remark VARCHAR(500) NULL COMMENT '备注信息'
change_summary text DEFAULT NULL COMMENT '变更摘要'说明:
TEXT:最大 65,535 字节MEDIUMTEXT:最大 16,777,215 字节(约 16MB)LONGTEXT:最大 4,294,967,295 字节(约 4GB)-- 创建时间、更新时间(推荐)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'
operation_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间'
-- 生效时间、过期时间
effective_time DATETIME NULL COMMENT '生效时间'
expire_time DATETIME NULL COMMENT '过期时间'
published_at DATETIME NULL COMMENT '发布时间'说明:
DEFAULT CURRENT_TIMESTAMP:插入时自动设置当前时间ON UPDATE CURRENT_TIMESTAMP:更新时自动更新为当前时间-- 生日、日期
birthday DATE DEFAULT NULL COMMENT '生日'说明:
-- 较少使用,建议使用 DATETIME
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'说明:
DATETIME 类似的默认值-- JSON 数据(MySQL 5.7.8+)
before_data json DEFAULT NULL COMMENT '操作前数据(JSON格式)'
after_data json DEFAULT NULL COMMENT '操作后数据(JSON格式)'
related_api_permissions JSON DEFAULT NULL COMMENT '关联的API权限编码(JSON数组)'说明:
-- 枚举类型
token_type ENUM('access', 'refresh') NOT NULL COMMENT 'token类型'
client_type ENUM('web', 'app', 'wx') NOT NULL DEFAULT 'web' COMMENT '客户端类型'
category VARCHAR(20) NOT NULL COMMENT '通知公告类别(announcement:公告, notice:通知)'说明:
VARCHAR + 注释的方式(更灵活)-- 地理位置坐标(用于地图展示)
location_point GEOMETRY DEFAULT NULL COMMENT '地理位置坐标(用于地图展示)'-- 精度要求高的数值(较少使用)
price DECIMAL(10, 2) COMMENT '价格'-- 查看所有数据库
SHOW DATABASES;
-- 查看当前数据库
SELECT DATABASE();
-- 查看数据库字符集
SHOW CREATE DATABASE wladmin;
-- 查看数据库详细信息
SELECT
SCHEMA_NAME AS '数据库名',
DEFAULT_CHARACTER_SET_NAME AS '字符集',
DEFAULT_COLLATION_NAME AS '排序规则'
FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME = 'wladmin';-- 查看所有表
SHOW TABLES;
-- 查看表结构
DESC user;
DESCRIBE user;
-- 查看建表语句
SHOW CREATE TABLE user;
-- 查看表的详细信息
SELECT
TABLE_NAME AS '表名',
TABLE_COLLATION AS '排序规则',
TABLE_COMMENT AS '表注释',
ENGINE AS '存储引擎'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user';-- 查看表的所有字段
SHOW COLUMNS FROM user;
SHOW FULL COLUMNS FROM user; -- 包含更多信息(排序规则等)
-- 查看字段详细信息(推荐)
SELECT
COLUMN_NAME AS '字段名',
COLUMN_TYPE AS '字段类型',
IS_NULLABLE AS '是否为空',
COLUMN_DEFAULT AS '默认值',
COLUMN_KEY AS '键类型',
EXTRA AS '额外信息',
COLUMN_COMMENT AS '字段注释',
CHARACTER_SET_NAME AS '字符集',
COLLATION_NAME AS '排序规则'
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
ORDER BY ORDINAL_POSITION;
-- 查看特定字段
SELECT
COLUMN_NAME,
COLUMN_TYPE,
COLUMN_COMMENT
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
AND COLUMN_NAME IN ('id', 'username', 'name');-- 查看表的所有索引
SHOW INDEX FROM user;
SHOW INDEXES FROM user;
SHOW KEYS FROM user;
-- 查看索引详细信息(推荐)
SELECT
INDEX_NAME AS '索引名',
COLUMN_NAME AS '字段名',
SEQ_IN_INDEX AS '索引顺序',
NON_UNIQUE AS '是否非唯一',
INDEX_TYPE AS '索引类型',
INDEX_COMMENT AS '索引注释'
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
ORDER BY INDEX_NAME, SEQ_IN_INDEX;
-- 查看主键
SELECT
COLUMN_NAME AS '主键字段'
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
AND CONSTRAINT_NAME = 'PRIMARY';-- 查看表的外键
SELECT
CONSTRAINT_NAME AS '外键名',
TABLE_NAME AS '表名',
COLUMN_NAME AS '字段名',
REFERENCED_TABLE_NAME AS '引用表名',
REFERENCED_COLUMN_NAME AS '引用字段名',
UPDATE_RULE AS '更新规则',
DELETE_RULE AS '删除规则'
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
AND REFERENCED_TABLE_NAME IS NOT NULL;
-- 查看所有外键约束(推荐)
SELECT
CONSTRAINT_NAME AS '外键名',
TABLE_NAME AS '表名',
COLUMN_NAME AS '字段名',
REFERENCED_TABLE_NAME AS '引用表名',
REFERENCED_COLUMN_NAME AS '引用字段名'
FROM information_schema.REFERENTIAL_CONSTRAINTS rc
JOIN information_schema.KEY_COLUMN_USAGE kcu
ON rc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
AND rc.TABLE_SCHEMA = kcu.TABLE_SCHEMA
WHERE rc.CONSTRAINT_SCHEMA = 'wladmin'
AND rc.TABLE_NAME = 'user';-- 查看表的数据大小和行数
SELECT
TABLE_NAME AS '表名',
ROUND(DATA_LENGTH / 1024 / 1024, 2) AS '数据大小(MB)',
ROUND(INDEX_LENGTH / 1024 / 1024, 2) AS '索引大小(MB)',
ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS '总大小(MB)',
TABLE_ROWS AS '行数'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin'
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;
-- 查看表的准确行数(较慢)
SELECT COUNT(*) FROM user;-- 查看表的字符集和排序规则
SELECT
TABLE_NAME AS '表名',
TABLE_COLLATION AS '排序规则',
TABLE_COMMENT AS '表注释'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user';
-- 查看字段的字符集和排序规则
SELECT
COLUMN_NAME AS '字段名',
CHARACTER_SET_NAME AS '字符集',
COLLATION_NAME AS '排序规则'
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
AND CHARACTER_SET_NAME IS NOT NULL;-- 查看所有表的注释
SELECT
TABLE_NAME AS '表名',
TABLE_COMMENT AS '表注释'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_COMMENT != '';
-- 查看所有字段的注释
SELECT
TABLE_NAME AS '表名',
COLUMN_NAME AS '字段名',
COLUMN_COMMENT AS '字段注释'
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
AND COLUMN_COMMENT != ''
ORDER BY TABLE_NAME, ORDINAL_POSITION;-- 查看自增ID当前值
SHOW TABLE STATUS LIKE 'user';
-- 或者
SELECT
AUTO_INCREMENT AS '当前自增值'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user';-- 查看所有表的字段(常用查询)
SELECT
TABLE_NAME AS '表名',
GROUP_CONCAT(COLUMN_NAME ORDER BY ORDINAL_POSITION SEPARATOR ', ') AS '字段列表'
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
GROUP BY TABLE_NAME
ORDER BY TABLE_NAME;
-- 查看所有表的字段详情
SELECT
TABLE_NAME AS '表名',
COLUMN_NAME AS '字段名',
COLUMN_TYPE AS '字段类型',
COLUMN_COMMENT AS '字段注释'
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
ORDER BY TABLE_NAME, ORDINAL_POSITION;-- 查看表的所有约束(主键、唯一键、外键等)
SELECT
CONSTRAINT_NAME AS '约束名',
CONSTRAINT_TYPE AS '约束类型',
TABLE_NAME AS '表名'
FROM information_schema.TABLE_CONSTRAINTS
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user';
-- 查看唯一约束
SELECT
CONSTRAINT_NAME AS '唯一约束名',
COLUMN_NAME AS '字段名'
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
AND CONSTRAINT_NAME != 'PRIMARY'
AND CONSTRAINT_NAME IN (
SELECT CONSTRAINT_NAME
FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'UNIQUE'
AND TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
);-- 查看表引用的其他表(通过外键)
SELECT
CONSTRAINT_NAME AS '外键名',
REFERENCED_TABLE_NAME AS '引用的表',
REFERENCED_COLUMN_NAME AS '引用的字段'
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
AND REFERENCED_TABLE_NAME IS NOT NULL;
-- 查看哪些表引用了当前表
SELECT
TABLE_NAME AS '引用表名',
COLUMN_NAME AS '引用字段',
CONSTRAINT_NAME AS '外键名'
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'wladmin'
AND REFERENCED_TABLE_NAME = 'user';基于项目实际使用的表结构,以下是标准的建表模板:
-- 标准建表模板
CREATE TABLE table_name (
-- 主键ID(必须)
id bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
-- 编码字段(可选,用于业务编码)
code VARCHAR(64) COMMENT '编码',
-- 基础字段
name VARCHAR(100) NOT NULL DEFAULT '' COMMENT '名称',
description VARCHAR(500) DEFAULT '' COMMENT '描述',
-- 状态字段
status tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否启用 1:停用;0:启用',
is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除',
is_system TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否系统预置(0-否,1-是)',
is_protected TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否保护,1为不可删除',
-- 关联字段(外键)
parent_id bigint(20) unsigned COMMENT '父节点ID',
create_by bigint(20) unsigned NULL COMMENT '创建人ID',
update_by bigint(20) unsigned NULL COMMENT '更新人ID',
-- 时间字段(必须)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
-- 主键
PRIMARY KEY (id),
-- 唯一索引(软删除场景使用复合唯一索引)
UNIQUE KEY uk_code_delete (code, is_delete),
-- 普通索引
KEY idx_parent_id (parent_id) USING BTREE,
KEY idx_is_delete (is_delete) USING BTREE,
KEY idx_name (name) USING BTREE,
KEY idx_created_at (created_at) USING BTREE,
-- 外键约束(可选)
CONSTRAINT fk_table_parent FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE,
CONSTRAINT fk_table_create_by FOREIGN KEY (create_by) REFERENCES user(id) ON DELETE SET NULL,
CONSTRAINT fk_table_update_by FOREIGN KEY (update_by) REFERENCES user(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='表注释';| 字段类型 | 命名规范 | 示例 |
|---|---|---|
| 主键ID | id |
id |
| 外键ID | 表名_id 或 关联对象_id |
user_id, department_id |
| 编码 | code |
code |
| 名称 | name |
name, username |
| 描述 | description |
description |
| 状态 | status |
status |
| 软删除 | is_delete |
is_delete |
| 系统字段 | is_system, is_protected |
is_system, is_protected |
| 创建/更新人 | create_by, update_by |
create_by, update_by |
| 创建/更新时间 | created_at, updated_at |
created_at, updated_at |
| 布尔值 | is_ 前缀 |
is_enabled, is_active |
| 数量 | count 或 _count 后缀 |
count, children_count |
| 排序 | sort 或 sort_order |
sort, sort_order |
| 路径 | path |
path |
-- 主键必须使用 bigint(20) unsigned AUTO_INCREMENT
PRIMARY KEY (id)-- 软删除场景:使用复合唯一索引(包含 is_delete)
UNIQUE KEY uk_code_delete (code, is_delete)
UNIQUE KEY uk_username_delete (username, is_delete)
-- 普通唯一索引(无软删除)
UNIQUE KEY uk_email (email)-- 单字段索引
KEY idx_name (name) USING BTREE
KEY idx_mobile (mobile) USING BTREE
KEY idx_created_at (created_at) USING BTREE
-- 复合索引(根据查询场景设计)
KEY idx_is_delete_created_at (is_delete, created_at) USING BTREE
KEY idx_user_dept_action (user_id, dept_id, action_type) USING BTREEPRIMARYuk_字段名(如 uk_code_delete)idx_字段名(如 idx_name)idx_字段1_字段2(如 idx_user_dept)bigint(20) unsigned AUTO_INCREMENTis_delete 通常需要索引created_at、updated_at 常用于排序和查询,建议建立索引-- ON DELETE CASCADE:删除父记录时,级联删除子记录
CONSTRAINT fk_department_parent FOREIGN KEY (parent_id) REFERENCES department(id) ON DELETE CASCADE
-- ON DELETE SET NULL:删除父记录时,子记录的外键设置为 NULL
CONSTRAINT fk_user_department FOREIGN KEY (department_id) REFERENCES department(id) ON DELETE SET NULL
-- ON DELETE RESTRICT:禁止删除父记录(默认行为)
CONSTRAINT fk_user_role_user FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE RESTRICT| 场景 | 推荐规则 | 示例 |
|---|---|---|
| 父子关系(树形结构) | ON DELETE CASCADE |
部门表的 parent_id |
| 关联关系(可选) | ON DELETE SET NULL |
用户表的 department_id |
| 关联关系(必须) | ON DELETE RESTRICT |
用户角色关联表的 user_id |
| 审计字段(创建人) | ON DELETE SET NULL |
所有表的 create_by |
项目使用软删除机制,通过 is_delete 字段标记删除状态:
-- 软删除字段
is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除'
-- 唯一索引需要考虑软删除
UNIQUE KEY uk_code_delete (code, is_delete)
-- 查询时过滤已删除记录
SELECT * FROM user WHERE is_delete = 0;-- 创建时间:插入时自动设置
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
-- 更新时间:插入和更新时自动设置
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'
-- 操作时间:仅插入时设置
operation_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间'-- 创建表
CREATE TABLE table_name (...);
-- 删除表(谨慎使用)
DROP TABLE IF EXISTS table_name;
-- 重命名表
ALTER TABLE old_name RENAME TO new_name;
-- 修改表注释
ALTER TABLE table_name COMMENT '新的表注释';
-- 查看表是否存在
SELECT COUNT(*) FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin' AND TABLE_NAME = 'table_name';-- 添加字段
ALTER TABLE table_name ADD COLUMN column_name VARCHAR(100) COMMENT '字段注释';
-- 修改字段类型
ALTER TABLE table_name MODIFY COLUMN column_name VARCHAR(200) COMMENT '新的注释';
-- 重命名字段
ALTER TABLE table_name CHANGE COLUMN old_name new_name VARCHAR(100) COMMENT '注释';
-- 删除字段(谨慎使用)
ALTER TABLE table_name DROP COLUMN column_name;
-- 修改字段位置
ALTER TABLE table_name MODIFY COLUMN column_name VARCHAR(100) AFTER other_column;-- 添加索引
ALTER TABLE table_name ADD INDEX idx_name (column_name);
-- 添加唯一索引
ALTER TABLE table_name ADD UNIQUE INDEX uk_name (column_name);
-- 添加复合索引
ALTER TABLE table_name ADD INDEX idx_name (col1, col2);
-- 删除索引
ALTER TABLE table_name DROP INDEX idx_name;
-- 查看索引使用情况(MySQL 8.0+)
SELECT * FROM sys.schema_unused_indexes WHERE object_schema = 'wladmin';-- 添加外键
ALTER TABLE table_name
ADD CONSTRAINT fk_name FOREIGN KEY (column_name) REFERENCES ref_table(ref_column) ON DELETE CASCADE;
-- 删除外键
ALTER TABLE table_name DROP FOREIGN KEY fk_name;
-- 查看外键
SHOW CREATE TABLE table_name;-- 添加约束(主键、唯一、外键等)
ALTER TABLE table_name ADD CONSTRAINT constraint_name constraint_type (column_name);
-- 删除约束
ALTER TABLE table_name DROP CONSTRAINT constraint_name;
-- 查看约束
SHOW CREATE TABLE table_name;表连接是 SQL 中最强大的特性之一,用于将多个表中的数据关联起来进行查询。MySQL 支持多种类型的表连接,每种连接类型都有其特定的使用场景。
内连接是最常用的连接类型,只返回两个表中匹配条件的行。
语法:
SELECT 列名
FROM 表1
INNER JOIN 表2
ON 表1.关联列 = 表2.关联列
WHERE 条件;示例 1:查询用户及其所属部门
-- 查询用户及其所属部门
SELECT
u.id,
u.username,
u.name,
d.id AS dept_id,
d.name AS dept_name
FROM user u
INNER JOIN department d ON u.dept_id = d.id
WHERE u.is_delete = 0 AND d.is_delete = 0;示例 2:查询用户及其角色
-- 查询用户及其角色
SELECT
u.id,
u.username,
r.id AS role_id,
r.name AS role_name
FROM user u
INNER JOIN user_role ur ON u.id = ur.user_id
INNER JOIN role r ON ur.role_id = r.id
WHERE u.is_delete = 0 AND r.is_delete = 0;左连接返回左表中的所有行,以及右表中匹配条件的行。如果右表中没有匹配的行,则返回 NULL。
语法:
SELECT 列名
FROM 表1
LEFT JOIN 表2
ON 表1.关联列 = 表2.关联列
WHERE 条件;示例 1:查询所有用户及其所属部门(包括未分配部门的用户)
-- 查询所有用户及其所属部门
SELECT
u.id,
u.username,
u.name,
d.id AS dept_id,
d.name AS dept_name
FROM user u
LEFT JOIN department d ON u.dept_id = d.id AND d.is_delete = 0
WHERE u.is_delete = 0;示例 2:查询用户及其登录日志(包括没有登录记录的用户)
-- 查询用户及其登录日志
SELECT
u.id,
u.username,
a.login_time,
a.ip_address
FROM user u
LEFT JOIN audit_logs a ON u.id = a.user_id AND a.action = 'login'
WHERE u.is_delete = 0
ORDER BY u.id, a.login_time DESC;右连接返回右表中的所有行,以及左表中匹配条件的行。如果左表中没有匹配的行,则返回 NULL。
语法:
SELECT 列名
FROM 表1
RIGHT JOIN 表2
ON 表1.关联列 = 表2.关联列
WHERE 条件;示例:查询所有部门及其用户(包括没有用户的部门)
-- 查询所有部门及其用户
SELECT
d.id AS dept_id,
d.name AS dept_name,
u.id,
u.username,
u.name
FROM user u
RIGHT JOIN department d ON u.dept_id = d.id AND u.is_delete = 0
WHERE d.is_delete = 0
ORDER BY d.id, u.id;MySQL 不直接支持 FULL JOIN,但可以通过 LEFT JOIN 和 RIGHT JOIN 的组合来实现。
语法:
SELECT 列名
FROM 表1
LEFT JOIN 表2
ON 表1.关联列 = 表2.关联列
UNION
SELECT 列名
FROM 表1
RIGHT JOIN 表2
ON 表1.关联列 = 表2.关联列
WHERE 表1.关联列 IS NULL;示例:查询所有用户和所有部门的关联关系
-- 查询所有用户和所有部门的关联关系
SELECT
u.id,
u.username,
d.id AS dept_id,
d.name AS dept_name
FROM user u
LEFT JOIN department d ON u.dept_id = d.id AND d.is_delete = 0
WHERE u.is_delete = 0
UNION
SELECT
NULL,
NULL,
d.id AS dept_id,
d.name AS dept_name
FROM department d
WHERE d.is_delete = 0 AND NOT EXISTS (
SELECT 1 FROM user u WHERE u.dept_id = d.id AND u.is_delete = 0
);交叉连接返回两个表的笛卡尔积,即左表中的每一行与右表中的每一行组合。
语法:
SELECT 列名
FROM 表1
CROSS JOIN 表2
WHERE 条件;示例:生成用户和角色的所有可能组合
-- 生成用户和角色的所有可能组合
SELECT
u.id AS user_id,
u.username,
r.id AS role_id,
r.name AS role_name
FROM user u
CROSS JOIN role r
WHERE u.is_delete = 0 AND r.is_delete = 0;自连接是指表与自身进行连接,通常用于处理层次结构或父子关系。
语法:
SELECT 列名
FROM 表1 t1
JOIN 表1 t2
ON t1.关联列 = t2.关联列
WHERE 条件;示例 1:查询部门及其上级部门
-- 查询部门及其上级部门
SELECT
d1.id AS dept_id,
d1.name AS dept_name,
d2.id AS parent_dept_id,
d2.name AS parent_dept_name
FROM department d1
LEFT JOIN department d2 ON d1.parent_id = d2.id
WHERE d1.is_delete = 0;示例 2:查询用户及其直接上级
-- 查询用户及其直接上级
SELECT
u1.id AS user_id,
u1.username AS user_name,
u2.id AS leader_id,
u2.username AS leader_name
FROM user u1
LEFT JOIN user u2 ON u1.leader_id = u2.id
WHERE u1.is_delete = 0;示例:使用 EXPLAIN 分析连接查询
-- 分析连接查询的执行计划
EXPLAIN SELECT
u.id,
u.username,
d.name AS dept_name
FROM user u
JOIN department d ON u.dept_id = d.id
WHERE u.is_delete = 0 AND d.is_delete = 0;-- 分析查询执行计划
EXPLAIN SELECT * FROM user WHERE username = 'admin';
-- 详细执行计划(MySQL 5.6+)
EXPLAIN FORMAT=JSON SELECT * FROM user WHERE username = 'admin';
-- 查看实际执行时间
EXPLAIN ANALYZE SELECT * FROM user WHERE username = 'admin';-- 查看慢查询日志(需要开启)
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
-- 查看当前慢查询
SHOW FULL PROCESSLIST;
-- 查看正在执行的查询
SELECT * FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep';-- 更新表的统计信息(优化查询性能)
ANALYZE TABLE table_name;
-- 优化表(重建索引、整理碎片)
OPTIMIZE TABLE table_name;
-- 检查表
CHECK TABLE table_name;视图是基于 SQL 语句的结果集的可视化的表,是一个虚拟表。视图可以简化复杂的查询,提供数据安全性和逻辑独立性。
-- 创建简单视图
CREATE VIEW view_user_list AS
SELECT id, username, name, email, status, created_at
FROM user
WHERE is_delete = 0;
-- 创建视图(如果不存在)
CREATE OR REPLACE VIEW view_user_list AS
SELECT id, username, name, email, status, created_at
FROM user
WHERE is_delete = 0;
-- 创建复杂视图(基于项目实际案例:监控数据联合视图)
CREATE VIEW monitor_combined_view AS
SELECT
d.id,
d.session_id,
d.user_id,
d.reporttype,
d.type,
d.category,
d.message,
d.page_url,
d.timestamp,
d.created_at,
NULL as performance_data,
NULL as behavior_data
FROM monitor_data d
UNION ALL
SELECT
p.id,
p.session_id,
p.user_id,
p.reporttype,
p.type,
p.category,
CONCAT('Performance: ', COALESCE(p.metric, p.type)) as message,
p.page_url,
p.timestamp,
p.created_at,
JSON_OBJECT(
'load_time', p.load_time,
'fcp', p.fcp,
'lcp', p.lcp
) as performance_data,
NULL as behavior_data
FROM monitor_performance p;-- 查看所有视图
SELECT TABLE_NAME AS '视图名', VIEW_DEFINITION AS '视图定义'
FROM information_schema.VIEWS
WHERE TABLE_SCHEMA = 'wladmin';
-- 查看视图定义
SHOW CREATE VIEW view_user_list;
-- 查看视图结构(同查看表结构)
DESC view_user_list;
SHOW COLUMNS FROM view_user_list;
-- 查看视图的所有字段
SELECT COLUMN_NAME, COLUMN_TYPE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'view_user_list';-- 查询视图(同查询表)
SELECT * FROM view_user_list WHERE status = 0;
-- 视图可以用于 JOIN
SELECT v.*, d.name AS dept_name
FROM view_user_list v
LEFT JOIN department d ON v.dept_id = d.id;-- 修改视图定义
ALTER VIEW view_user_list AS
SELECT id, username, name, email, status, created_at, department_id
FROM user
WHERE is_delete = 0;
-- 或者使用 CREATE OR REPLACE
CREATE OR REPLACE VIEW view_user_list AS
SELECT id, username, name, email, status, created_at, department_id
FROM user
WHERE is_delete = 0;-- 删除视图
DROP VIEW IF EXISTS view_user_list;
-- 删除多个视图
DROP VIEW IF EXISTS view_user_list, view_role_list;触发器是 MySQL 响应特定事件(INSERT、UPDATE、DELETE)而自动执行的一段 SQL 代码。
-- 创建触发器语法
DELIMITER //
CREATE TRIGGER trigger_name
BEFORE|AFTER INSERT|UPDATE|DELETE
ON table_name
FOR EACH ROW
BEGIN
-- 触发器逻辑
END //
DELIMITER ;
-- 示例:用户创建时自动记录创建时间(虽然可用 DEFAULT CURRENT_TIMESTAMP,这里仅作示例)
DELIMITER //
CREATE TRIGGER trg_user_before_insert
BEFORE INSERT ON user
FOR EACH ROW
BEGIN
IF NEW.created_at IS NULL THEN
SET NEW.created_at = NOW();
END IF;
END //
DELIMITER ;
-- 示例:删除用户时自动记录到审计表
DELIMITER //
CREATE TRIGGER trg_user_after_delete
AFTER DELETE ON user
FOR EACH ROW
BEGIN
INSERT INTO audit_logs (
user_id, username, action_type, module_code,
operation_desc, before_data, operation_time
) VALUES (
OLD.id, OLD.username, 'delete', 'user',
CONCAT('删除用户:', OLD.username),
JSON_OBJECT('id', OLD.id, 'username', OLD.username),
NOW()
);
END //
DELIMITER ;
-- 示例:更新部门时自动更新部门的 children_count
DELIMITER //
CREATE TRIGGER trg_department_after_insert
AFTER INSERT ON department
FOR EACH ROW
BEGIN
IF NEW.parent_id IS NOT NULL THEN
UPDATE department
SET children_count = children_count + 1
WHERE id = NEW.parent_id;
END IF;
END //
DELIMITER ;
DELIMITER //
CREATE TRIGGER trg_department_after_delete
AFTER DELETE ON department
FOR EACH ROW
BEGIN
IF OLD.parent_id IS NOT NULL THEN
UPDATE department
SET children_count = children_count - 1
WHERE id = OLD.parent_id;
END IF;
END //
DELIMITER ;-- 查看所有触发器
SHOW TRIGGERS;
-- 查看指定数据库的触发器
SHOW TRIGGERS FROM wladmin;
-- 查看指定表的触发器
SHOW TRIGGERS FROM wladmin LIKE 'user';
-- 从 information_schema 查询触发器
SELECT
TRIGGER_NAME AS '触发器名',
EVENT_MANIPULATION AS '事件类型',
EVENT_OBJECT_TABLE AS '表名',
ACTION_TIMING AS '触发时机',
ACTION_STATEMENT AS '触发器语句'
FROM information_schema.TRIGGERS
WHERE TRIGGER_SCHEMA = 'wladmin'
AND EVENT_OBJECT_TABLE = 'user';-- 删除触发器
DROP TRIGGER IF EXISTS trg_user_before_insert;存储过程是一组为了完成特定功能的 SQL 语句集合,经过编译后存储在数据库中。
-- 基本语法
DELIMITER //
CREATE PROCEDURE procedure_name([IN|OUT|INOUT parameter_name data_type])
BEGIN
-- 存储过程逻辑
END //
DELIMITER ;
-- 示例:清理过期监控数据(基于项目实际案例)
DELIMITER //
CREATE PROCEDURE clean_monitor_data(IN days_to_keep INT)
BEGIN
DECLARE expire_time BIGINT;
SET expire_time = UNIX_TIMESTAMP(NOW() - INTERVAL days_to_keep DAY) * 1000;
-- 清理过期监控数据
DELETE FROM monitor_data WHERE timestamp < expire_time;
-- 清理过期性能数据
DELETE FROM monitor_performance WHERE timestamp < expire_time;
-- 清理过期行为事件
DELETE FROM monitor_behavior_events WHERE timestamp < expire_time;
-- 返回影响的行数
SELECT
ROW_COUNT() AS affected_rows,
days_to_keep AS days_kept;
END //
DELIMITER ;
-- 示例:获取性能指标统计
DELIMITER //
CREATE PROCEDURE get_performance_summary(
IN start_time BIGINT,
IN end_time BIGINT
)
BEGIN
SELECT
'page_performance' as metric_group,
AVG(load_time) as avg_load_time,
AVG(ttfb) as avg_ttfb,
AVG(fcp) as avg_fcp,
AVG(lcp) as avg_lcp,
COUNT(*) as sample_count
FROM monitor_performance
WHERE timestamp BETWEEN start_time AND end_time
AND category = 'page_performance';
END //
DELIMITER ;
-- 示例:带输出参数的存储过程
DELIMITER //
CREATE PROCEDURE get_user_count(
OUT total_count INT,
OUT active_count INT
)
BEGIN
SELECT COUNT(*) INTO total_count FROM user WHERE is_delete = 0;
SELECT COUNT(*) INTO active_count FROM user WHERE is_delete = 0 AND status = 0;
END //
DELIMITER ;-- 调用存储过程(无参数)
CALL clean_monitor_data(30);
-- 调用存储过程(带输入参数)
SET @start_time = UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 7 DAY)) * 1000;
SET @end_time = UNIX_TIMESTAMP(NOW()) * 1000;
CALL get_performance_summary(@start_time, @end_time);
-- 调用存储过程(带输出参数)
CALL get_user_count(@total, @active);
SELECT @total AS '总用户数', @active AS '活跃用户数';-- 查看所有存储过程
SHOW PROCEDURE STATUS WHERE Db = 'wladmin';
-- 查看存储过程定义
SHOW CREATE PROCEDURE clean_monitor_data;
-- 从 information_schema 查询存储过程
SELECT
ROUTINE_NAME AS '存储过程名',
ROUTINE_TYPE AS '类型',
CREATED AS '创建时间',
LAST_ALTERED AS '修改时间'
FROM information_schema.ROUTINES
WHERE ROUTINE_SCHEMA = 'wladmin'
AND ROUTINE_TYPE = 'PROCEDURE';-- 修改存储过程(使用 ALTER PROCEDURE,只能修改特性,不能修改逻辑)
ALTER PROCEDURE clean_monitor_data COMMENT '清理过期监控数据存储过程';
-- 修改存储过程逻辑(使用 DROP 和 CREATE)
DROP PROCEDURE IF EXISTS clean_monitor_data;
-- 然后重新创建-- 删除存储过程
DROP PROCEDURE IF EXISTS clean_monitor_data;事务是一组 SQL 语句的集合,作为一个不可分割的工作单元执行。事务具有 ACID 特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
-- 开始事务
START TRANSACTION;
-- 或
BEGIN;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;
-- 设置保存点
SAVEPOINT savepoint_name;
-- 回滚到保存点
ROLLBACK TO savepoint_name;
-- 释放保存点
RELEASE SAVEPOINT savepoint_name;-- 示例:转账操作(原子性保证)
START TRANSACTION;
-- 从账户A扣款
UPDATE account SET balance = balance - 100 WHERE id = 1;
-- 向账户B加款
UPDATE account SET balance = balance + 100 WHERE id = 2;
-- 如果所有操作成功,提交事务
COMMIT;
-- 如果出错,回滚事务
-- ROLLBACK;
-- 示例:使用保存点
START TRANSACTION;
INSERT INTO user (username, name) VALUES ('user1', '用户1');
SAVEPOINT sp1;
INSERT INTO user (username, name) VALUES ('user2', '用户2');
SAVEPOINT sp2;
INSERT INTO user (username, name) VALUES ('user3', '用户3');
-- 回滚到保存点 sp2,user3 的插入会被撤销
ROLLBACK TO sp2;
-- 提交事务,user1 和 user2 会被保存
COMMIT;MySQL 支持四种事务隔离级别:
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- MySQL 8.0+
SELECT @@transaction_isolation;
-- MySQL 5.7
SELECT @@tx_isolation;
-- 设置会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- 设置全局隔离级别(需要 SUPER 权限)
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;隔离级别说明:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 说明 |
|---|---|---|---|---|
| READ UNCOMMITTED | ✅ 可能 | ✅ 可能 | ✅ 可能 | 最低隔离级别 |
| READ COMMITTED | ❌ 不可能 | ✅ 可能 | ✅ 可能 | 只能读取已提交的数据 |
| REPEATABLE READ | ❌ 不可能 | ❌ 不可能 | ✅ 可能 | MySQL 默认级别 |
| SERIALIZABLE | ❌ 不可能 | ❌ 不可能 | ❌ 不可能 | 最高隔离级别,性能最差 |
-- 查看自动提交状态
SELECT @@autocommit;
-- 关闭自动提交(每个语句都会开启一个事务)
SET autocommit = 0;
-- 开启自动提交(默认状态)
SET autocommit = 1;
-- 在自动提交关闭的情况下,每个语句都需要手动提交或回滚
SET autocommit = 0;
INSERT INTO user (username, name) VALUES ('user1', '用户1');
COMMIT; -- 必须手动提交
SET autocommit = 0;
INSERT INTO user (username, name) VALUES ('user2', '用户2');
ROLLBACK; -- 回滚,user2 不会被保存
-- 恢复自动提交
SET autocommit = 1;REPEATABLE READ 隔离级别临时表是 MySQL 提供的一种特殊表类型,只在当前会话中存在,会话结束后自动删除。临时表非常适合存储中间结果、优化复杂查询、进行数据转换等场景。
-- 创建临时表(基本语法)
CREATE TEMPORARY TABLE temp_table_name (
column1 datatype,
column2 datatype,
...
);
-- 创建临时表(完整语法,包含索引和约束)
CREATE TEMPORARY TABLE temp_user_summary (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
username VARCHAR(50) NOT NULL,
action_count INT DEFAULT 0,
last_action_time DATETIME,
PRIMARY KEY (id),
KEY idx_user_id (user_id),
KEY idx_last_action_time (last_action_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;-- 从查询结果创建临时表(最常用)
CREATE TEMPORARY TABLE temp_active_users AS
SELECT
u.id,
u.username,
u.name,
u.department_id,
COUNT(a.id) AS action_count,
MAX(a.operation_time) AS last_action_time
FROM user u
LEFT JOIN audit_logs a ON u.id = a.user_id
WHERE u.is_delete = 0
AND u.status = 0
AND a.operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY u.id, u.username, u.name, u.department_id;
-- 查看临时表数据
SELECT * FROM temp_active_users ORDER BY action_count DESC;-- 创建临时表(结构类似现有表)
CREATE TEMPORARY TABLE temp_user_backup LIKE user;
-- 插入数据到临时表
INSERT INTO temp_user_backup
SELECT * FROM user WHERE is_delete = 0 AND status = 0;-- 插入单条数据
INSERT INTO temp_user_summary (user_id, username, action_count, last_action_time)
VALUES (1, 'admin', 100, NOW());
-- 批量插入数据
INSERT INTO temp_user_summary (user_id, username, action_count, last_action_time)
SELECT
u.id,
u.username,
COUNT(a.id) AS action_count,
MAX(a.operation_time) AS last_action_time
FROM user u
LEFT JOIN audit_logs a ON u.id = a.user_id
WHERE u.is_delete = 0
GROUP BY u.id, u.username;-- 查询临时表(与普通表查询方式相同)
SELECT * FROM temp_user_summary WHERE action_count > 10;
-- 临时表与其他表关联查询
SELECT
t.*,
d.name AS dept_name
FROM temp_user_summary t
LEFT JOIN department d ON t.department_id = d.id
WHERE d.is_delete = 0;-- 更新临时表数据
UPDATE temp_user_summary
SET action_count = action_count + 1,
last_action_time = NOW()
WHERE user_id = 1;-- 删除临时表数据(不删除表结构)
DELETE FROM temp_user_summary WHERE action_count = 0;
-- 清空临时表
TRUNCATE TABLE temp_user_summary;-- 手动删除临时表(会话结束时会自动删除)
DROP TEMPORARY TABLE IF EXISTS temp_user_summary;
-- 删除多个临时表
DROP TEMPORARY TABLE IF EXISTS temp_user_summary, temp_dept_summary;-- 查看当前会话的所有临时表
SHOW TABLES LIKE 'temp_%';
-- 查看临时表结构
DESC temp_user_summary;
SHOW CREATE TABLE temp_user_summary;
-- 查看临时表的所有字段
SHOW COLUMNS FROM temp_user_summary;
-- 从 information_schema 查询临时表(MySQL 8.0+)
SELECT
TABLE_NAME AS '临时表名',
TABLE_ROWS AS '行数',
ROUND(DATA_LENGTH / 1024, 2) AS '大小(KB)',
ENGINE AS '存储引擎'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME LIKE 'temp_%';-- 创建 MEMORY 临时表(默认,速度快,但数据在内存中)
CREATE TEMPORARY TABLE temp_memory_table (
id INT PRIMARY KEY,
name VARCHAR(100)
) ENGINE=MEMORY;
-- 特点:
-- ✅ 速度快(数据在内存中)
-- ❌ 数据在服务器重启或表被删除时丢失
-- ❌ 不支持 TEXT/BLOB 类型
-- ❌ 不支持外键-- 创建 InnoDB 临时表(支持事务、外键等)
CREATE TEMPORARY TABLE temp_innodb_table (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED,
data JSON,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 特点:
-- ✅ 支持事务
-- ✅ 支持外键(虽然临时表很少用外键)
-- ✅ 支持 TEXT/BLOB/JSON 类型
-- ✅ 数据持久化到磁盘(会话期间)-- 创建带索引的临时表
CREATE TEMPORARY TABLE temp_indexed_table (
id INT PRIMARY KEY,
user_id INT,
action_time DATETIME,
INDEX idx_user_id (user_id),
INDEX idx_action_time (action_time),
INDEX idx_user_action (user_id, action_time)
) ENGINE=InnoDB;
-- 为已存在的临时表添加索引
CREATE TEMPORARY TABLE temp_table (
id INT,
name VARCHAR(100)
);
ALTER TABLE temp_table ADD INDEX idx_name (name);
ALTER TABLE temp_table ADD PRIMARY KEY (id);-- 场景:统计用户最近7天的操作次数和最后操作时间
-- 使用临时表存储中间结果,避免重复计算
-- 步骤1:创建临时表存储用户操作统计
CREATE TEMPORARY TABLE temp_user_stats AS
SELECT
u.id AS user_id,
u.username,
COUNT(a.id) AS action_count,
MAX(a.operation_time) AS last_action_time,
MIN(a.operation_time) AS first_action_time
FROM user u
LEFT JOIN audit_logs a ON u.id = a.user_id
WHERE u.is_delete = 0
AND a.operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY u.id, u.username;
-- 步骤2:基于临时表进行进一步查询
SELECT
t.*,
d.name AS dept_name,
CASE
WHEN t.action_count >= 100 THEN '活跃'
WHEN t.action_count >= 50 THEN '一般'
ELSE '不活跃'
END AS activity_level
FROM temp_user_stats t
LEFT JOIN user u ON t.user_id = u.id
LEFT JOIN department d ON u.department_id = d.id
WHERE d.is_delete = 0
ORDER BY t.action_count DESC;
-- 步骤3:清理临时表(可选,会话结束会自动删除)
DROP TEMPORARY TABLE temp_user_stats;-- 场景:批量更新用户状态,需要先验证数据
-- 步骤1:创建临时表存储待更新的用户ID
CREATE TEMPORARY TABLE temp_user_ids (
user_id BIGINT UNSIGNED PRIMARY KEY,
new_status TINYINT,
reason VARCHAR(255)
);
-- 步骤2:插入待更新的用户ID和状态
INSERT INTO temp_user_ids (user_id, new_status, reason)
VALUES
(1, 1, '长时间未登录'),
(2, 1, '账户异常'),
(3, 0, '恢复正常');
-- 步骤3:验证数据(检查用户是否存在)
SELECT
t.user_id,
t.new_status,
t.reason,
u.username,
u.status AS current_status,
CASE
WHEN u.id IS NULL THEN '用户不存在'
WHEN u.is_delete = 1 THEN '用户已删除'
ELSE '可以更新'
END AS validation_result
FROM temp_user_ids t
LEFT JOIN user u ON t.user_id = u.id;
-- 步骤4:执行批量更新(使用事务)
START TRANSACTION;
UPDATE user u
INNER JOIN temp_user_ids t ON u.id = t.user_id
SET u.status = t.new_status,
u.updated_at = NOW()
WHERE u.is_delete = 0
AND u.id IS NOT NULL;
-- 检查更新结果
SELECT ROW_COUNT() AS updated_rows;
COMMIT;
-- 步骤5:清理临时表
DROP TEMPORARY TABLE temp_user_ids;-- 场景:清洗重复的审计日志数据
-- 步骤1:创建临时表存储去重后的数据
CREATE TEMPORARY TABLE temp_audit_clean AS
SELECT
id,
user_id,
username,
action_type,
module_code,
operation_time,
ROW_NUMBER() OVER (
PARTITION BY user_id, action_type, module_code, DATE(operation_time)
ORDER BY operation_time DESC, id DESC
) AS rn
FROM audit_logs
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 30 DAY);
-- 步骤2:查看重复数据统计
SELECT
COUNT(*) AS total_records,
COUNT(CASE WHEN rn = 1 THEN 1 END) AS unique_records,
COUNT(*) - COUNT(CASE WHEN rn = 1 THEN 1 END) AS duplicate_records
FROM temp_audit_clean;
-- 步骤3:提取去重后的数据
CREATE TEMPORARY TABLE temp_audit_unique AS
SELECT
id,
user_id,
username,
action_type,
module_code,
operation_time
FROM temp_audit_clean
WHERE rn = 1;
-- 步骤4:使用去重后的数据进行统计
SELECT
action_type,
COUNT(*) AS count,
COUNT(DISTINCT user_id) AS unique_users
FROM temp_audit_unique
GROUP BY action_type
ORDER BY count DESC;
-- 清理临时表
DROP TEMPORARY TABLE temp_audit_clean;
DROP TEMPORARY TABLE temp_audit_unique;-- 场景:批量生成用户报表数据
-- 步骤1:创建临时表存储报表数据
CREATE TEMPORARY TABLE temp_user_report (
user_id BIGINT UNSIGNED PRIMARY KEY,
username VARCHAR(50),
dept_name VARCHAR(100),
login_count INT DEFAULT 0,
last_login_time DATETIME,
action_count INT DEFAULT 0,
last_action_time DATETIME
) ENGINE=InnoDB;
-- 步骤2:插入用户基本信息
INSERT INTO temp_user_report (user_id, username, dept_name)
SELECT
u.id,
u.username,
d.name AS dept_name
FROM user u
LEFT JOIN department d ON u.department_id = d.id
WHERE u.is_delete = 0
AND d.is_delete = 0;
-- 步骤3:更新登录统计
UPDATE temp_user_report t
INNER JOIN (
SELECT
user_id,
COUNT(*) AS login_count,
MAX(operation_time) AS last_login_time
FROM audit_logs
WHERE action_type = 'login'
AND operation_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY user_id
) a ON t.user_id = a.user_id
SET t.login_count = a.login_count,
t.last_login_time = a.last_login_time;
-- 步骤4:更新操作统计
UPDATE temp_user_report t
INNER JOIN (
SELECT
user_id,
COUNT(*) AS action_count,
MAX(operation_time) AS last_action_time
FROM audit_logs
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY user_id
) a ON t.user_id = a.user_id
SET t.action_count = a.action_count,
t.last_action_time = a.last_action_time;
-- 步骤5:查询报表数据
SELECT
username,
dept_name,
login_count,
last_login_time,
action_count,
last_action_time,
CASE
WHEN login_count >= 20 AND action_count >= 100 THEN '活跃'
WHEN login_count >= 10 AND action_count >= 50 THEN '一般'
ELSE '不活跃'
END AS activity_level
FROM temp_user_report
ORDER BY action_count DESC;
-- 清理临时表
DROP TEMPORARY TABLE temp_user_report;-- CTE:适合简单的中间结果,查询结束后自动消失
WITH latest_audit AS (
SELECT
a.*,
ROW_NUMBER() OVER (PARTITION BY a.user_id ORDER BY a.operation_time DESC) AS rn
FROM audit_logs a
WHERE a.operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
)
SELECT
la.user_id,
la.username,
la.operation_time AS last_action_time
FROM latest_audit la
WHERE la.rn = 1;-- 临时表:适合需要多次查询、更新、插入的复杂场景
CREATE TEMPORARY TABLE temp_latest_audit AS
SELECT
a.*,
ROW_NUMBER() OVER (PARTITION BY a.user_id ORDER BY a.operation_time DESC) AS rn
FROM audit_logs a
WHERE a.operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY);
-- 可以多次查询临时表
SELECT * FROM temp_latest_audit WHERE rn = 1;
SELECT COUNT(*) FROM temp_latest_audit WHERE rn = 1;
-- 可以更新临时表
UPDATE temp_latest_audit SET rn = 0 WHERE user_id = 1;
-- 可以与其他表关联
SELECT
t.*,
u.name AS user_name
FROM temp_latest_audit t
LEFT JOIN user u ON t.user_id = u.id
WHERE t.rn = 1;选择建议:
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 简单的中间结果,只查询一次 | CTE | 代码简洁,自动清理 |
| 需要多次查询中间结果 | 临时表 | 可以重复使用,提高性能 |
| 需要对中间结果进行更新 | 临时表 | CTE 是只读的 |
| 复杂的多步骤数据处理 | 临时表 | 可以分步骤处理,便于调试 |
-- 临时表可以与普通表同名,但优先访问临时表
CREATE TABLE user_summary (
id INT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TEMPORARY TABLE user_summary (
id INT PRIMARY KEY,
name VARCHAR(100)
);
-- 查询时会优先访问临时表
SELECT * FROM user_summary; -- 访问临时表
-- 删除临时表后,会访问普通表
DROP TEMPORARY TABLE user_summary;
SELECT * FROM user_summary; -- 访问普通表-- 会话A创建临时表
CREATE TEMPORARY TABLE temp_session_a (id INT);
-- 会话B无法看到会话A的临时表
-- SELECT * FROM temp_session_a; -- 错误:表不存在
-- 会话B可以创建同名临时表
CREATE TEMPORARY TABLE temp_session_a (id INT, name VARCHAR(100));
-- 两个会话的临时表互不影响-- 临时表在以下情况会自动删除:
-- 1. 会话正常结束
-- 2. 会话异常断开
-- 3. 服务器重启
-- 4. 手动执行 DROP TEMPORARY TABLE
-- 注意:不要在应用代码中依赖临时表的持久化
-- 每次使用前应该检查临时表是否存在,或重新创建-- 1. 临时表适合存储大量中间结果,避免重复计算
-- 2. 为临时表创建适当的索引,提高查询性能
-- 3. 使用 MEMORY 引擎可以提高速度,但注意内存限制
-- 4. 临时表不会记录到 binlog,不会影响主从复制
-- 示例:为临时表创建索引
CREATE TEMPORARY TABLE temp_large_table (
id INT,
user_id INT,
action_time DATETIME,
INDEX idx_user_id (user_id),
INDEX idx_action_time (action_time)
) ENGINE=InnoDB;-- 推荐:使用 temp_ 前缀,便于识别和管理
CREATE TEMPORARY TABLE temp_user_stats (...);
CREATE TEMPORARY TABLE temp_dept_summary (...);
-- 不推荐:与普通表同名(容易混淆)
CREATE TEMPORARY TABLE user_stats (...);-- 虽然临时表会自动删除,但显式删除是好的实践
-- 特别是在存储过程中或长时间运行的会话中
CREATE TEMPORARY TABLE temp_data (...);
-- ... 使用临时表 ...
DROP TEMPORARY TABLE IF EXISTS temp_data; -- 显式删除-- 在存储过程中使用临时表时,注意错误处理
DELIMITER //
CREATE PROCEDURE process_user_data()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- 发生错误时清理临时表
DROP TEMPORARY TABLE IF EXISTS temp_user_data;
ROLLBACK;
RESIGNAL;
END;
-- 创建临时表
CREATE TEMPORARY TABLE temp_user_data (...);
-- 使用临时表
-- ...
-- 清理临时表
DROP TEMPORARY TABLE IF EXISTS temp_user_data;
END //
DELIMITER ;-- 为临时表创建必要的索引,提高查询性能
CREATE TEMPORARY TABLE temp_user_actions (
id INT PRIMARY KEY,
user_id INT,
action_time DATETIME,
INDEX idx_user_id (user_id), -- 用于关联查询
INDEX idx_action_time (action_time) -- 用于排序和过滤
) ENGINE=InnoDB;| 特性 | 临时表 | 普通表 |
|---|---|---|
| 生命周期 | 会话期间 | 永久存在 |
| 可见性 | 仅当前会话 | 所有会话可见 |
| 自动删除 | ✅ 会话结束自动删除 | ❌ 需要手动删除 |
| 命名冲突 | ✅ 可与普通表同名 | ❌ 不能重复 |
| 索引支持 | ✅ 支持 | ✅ 支持 |
| 事务支持 | ✅ 支持(InnoDB) | ✅ 支持(InnoDB) |
| 外键支持 | ✅ 支持(InnoDB) | ✅ 支持(InnoDB) |
| Binlog记录 | ❌ 不记录 | ✅ 记录 |
| 主从复制 | ❌ 不复制 | ✅ 复制 |
存储引擎限制:
复制限制:
权限限制:
命名限制:
-- 查看所有表的基本信息(表名、注释、行数、大小)
SELECT
TABLE_NAME AS '表名',
TABLE_COMMENT AS '表注释',
TABLE_ROWS AS '行数',
ROUND(DATA_LENGTH / 1024 / 1024, 2) AS '数据大小(MB)',
ROUND(INDEX_LENGTH / 1024 / 1024, 2) AS '索引大小(MB)',
ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS '总大小(MB)',
ENGINE AS '存储引擎',
TABLE_COLLATION AS '排序规则'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin'
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;-- 方法1:使用 DESC 或 DESCRIBE(最简单)
DESC user;
DESCRIBE user;
-- 方法2:使用 SHOW COLUMNS
SHOW COLUMNS FROM user;
SHOW FULL COLUMNS FROM user; -- 包含更多信息(排序规则等)
-- 方法3:从 information_schema 查询(推荐用于程序)
SELECT
COLUMN_NAME AS '字段名',
COLUMN_TYPE AS '字段类型',
IS_NULLABLE AS '是否可空',
COLUMN_DEFAULT AS '默认值',
COLUMN_KEY AS '键类型',
EXTRA AS '额外信息',
COLUMN_COMMENT AS '字段注释',
CHARACTER_SET_NAME AS '字符集',
COLLATION_NAME AS '排序规则'
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
ORDER BY ORDINAL_POSITION;
-- 方法4:只查看字段名称列表
SELECT COLUMN_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
ORDER BY ORDINAL_POSITION;
-- 方法5:字段名称列表(逗号分隔)
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY ORDINAL_POSITION SEPARATOR ', ') AS '字段列表'
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user';-- 查看指定表的所有索引
SELECT
INDEX_NAME AS '索引名',
GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX SEPARATOR ', ') AS '索引字段',
NON_UNIQUE AS '是否非唯一',
INDEX_TYPE AS '索引类型',
INDEX_COMMENT AS '索引注释'
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user'
GROUP BY INDEX_NAME, NON_UNIQUE, INDEX_TYPE, INDEX_COMMENT
ORDER BY INDEX_NAME;-- 查看指定表的所有外键
SELECT
rc.CONSTRAINT_NAME AS '外键名',
kcu.COLUMN_NAME AS '字段名',
rc.REFERENCED_TABLE_NAME AS '引用表名',
kcu.REFERENCED_COLUMN_NAME AS '引用字段名',
rc.UPDATE_RULE AS '更新规则',
rc.DELETE_RULE AS '删除规则'
FROM information_schema.REFERENTIAL_CONSTRAINTS rc
JOIN information_schema.KEY_COLUMN_USAGE kcu
ON rc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
AND rc.TABLE_SCHEMA = kcu.TABLE_SCHEMA
WHERE rc.CONSTRAINT_SCHEMA = 'wladmin'
AND rc.TABLE_NAME = 'user';-- 查看建表语句(可以复制后稍作修改创建新表)
SHOW CREATE TABLE user;
-- 或者使用 mysqldump 命令导出
-- mysqldump -u root -p --no-data wladmin user > user_table.sql-- 查看数据库中所有表的字段清单
SELECT
t.TABLE_NAME AS '表名',
t.TABLE_COMMENT AS '表注释',
GROUP_CONCAT(
CONCAT(
c.COLUMN_NAME, ' (', c.COLUMN_TYPE, ')',
IF(c.COLUMN_COMMENT != '', CONCAT(' - ', c.COLUMN_COMMENT), '')
)
ORDER BY c.ORDINAL_POSITION
SEPARATOR '\n'
) AS '字段列表'
FROM information_schema.TABLES t
LEFT JOIN information_schema.COLUMNS c
ON t.TABLE_SCHEMA = c.TABLE_SCHEMA
AND t.TABLE_NAME = c.TABLE_NAME
WHERE t.TABLE_SCHEMA = 'wladmin'
GROUP BY t.TABLE_NAME, t.TABLE_COMMENT
ORDER BY t.TABLE_NAME;| 类型 | 范围(有符号) | 范围(无符号) | 适用场景 |
|---|---|---|---|
| TINYINT | -128 到 127 | 0 到 255 | 状态值、布尔值 |
| SMALLINT | -32768 到 32767 | 0 到 65535 | 小范围数值 |
| MEDIUMINT | -8388608 到 8388607 | 0 到 16777215 | 中等范围数值 |
| INT | -2147483648 到 2147483647 | 0 到 4294967295 | 常用整数、排序号 |
| BIGINT | -9223372036854775808 到 9223372036854775807 | 0 到 18446744073709551615 | 主键ID、外键ID |
项目推荐:
bigint(20) unsigned(支持大量数据)bigint(20) unsigned(与主键保持一致)tinyint(4)(节省空间)int(11)(足够使用)int(11)(树形结构层级)| 类型 | 最大长度 | 适用场景 |
|---|---|---|
| CHAR | 255 字符(固定长度) | 固定长度的字符串(较少使用) |
| VARCHAR | 65535 字节(utf8mb4 最多16383字符) | 变长字符串(最常用) |
| TEXT | 65,535 字节 | 中等长度文本 |
| MEDIUMTEXT | 16,777,215 字节(约16MB) | 较长文本(内容、备注) |
| LONGTEXT | 4,294,967,295 字节(约4GB) | 超长文本(很少使用) |
项目常用长度:
VARCHAR(20):手机号、电话VARCHAR(50):用户名、邮箱VARCHAR(64):编码、模块代码VARCHAR(100):姓名、标题VARCHAR(255):地址、URL、描述VARCHAR(500):详细描述、备注VARCHAR(2500):路径字符串| 类型 | 范围 | 精度 | 适用场景 |
|---|---|---|---|
| DATE | 1000-01-01 到 9999-12-31 | 日期 | 生日、日期 |
| TIME | -838:59:59 到 838:59:59 | 时间 | 时间(较少使用) |
| DATETIME | 1000-01-01 00:00:00 到 9999-12-31 23:59:59 | 秒 | ✅ 推荐:创建时间、更新时间 |
| TIMESTAMP | 1970-01-01 00:00:01 到 2038-01-19 03:14:07 | 秒 | ❌ 不推荐:时区问题 |
项目推荐:统一使用 DATETIME 类型。
-- JSON 类型(MySQL 5.7.8+)
before_data json DEFAULT NULL COMMENT '操作前数据'
after_data json DEFAULT NULL COMMENT '操作后数据'
-- JSON 查询示例
SELECT id, JSON_EXTRACT(before_data, '$.username') AS username
FROM audit_logs
WHERE JSON_EXTRACT(before_data, '$.status') = '1';
-- 或者使用 ->> 操作符(MySQL 5.7.13+)
SELECT id, before_data->>'$.username' AS username
FROM audit_logs
WHERE before_data->>'$.status' = '1';utf8mb4 字符集和 utf8mb4_unicode_ci 排序规则InnoDB 存储引擎bigint(20) unsigned AUTO_INCREMENTcreated_at 和 updated_at 时间字段is_delete 软删除字段create_by 和 update_by 审计字段COMMENT 注释COMMENT 注释is_delete)NOT NULL 约束使用是否合理is_delete 字段是否有索引created_at)idx_, uk_ 前缀)fk_ 前缀)ON DELETE 规则是否合理ON UPDATE 规则是否合理-- 1. 查看所有表
SHOW TABLES;
-- 2. 查看表结构
DESC table_name;
SHOW CREATE TABLE table_name;
-- 3. 查看表的所有字段
SHOW COLUMNS FROM table_name;
SELECT * FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin' AND TABLE_NAME = 'table_name';
-- 4. 查看表的所有索引
SHOW INDEX FROM table_name;
-- 5. 查看表的所有外键
SELECT * FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'table_name'
AND REFERENCED_TABLE_NAME IS NOT NULL;
-- 6. 查看表的行数
SELECT COUNT(*) FROM table_name;
-- 7. 查看表的大小
SELECT
TABLE_NAME,
ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS '大小(MB)'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin' AND TABLE_NAME = 'table_name';-- 创建数据库
CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建表
CREATE TABLE table_name (...) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 添加字段
ALTER TABLE table_name ADD COLUMN column_name VARCHAR(100) COMMENT '字段注释';
-- 修改字段
ALTER TABLE table_name MODIFY COLUMN column_name VARCHAR(200) COMMENT '新注释';
-- 删除字段
ALTER TABLE table_name DROP COLUMN column_name;
-- 添加索引
ALTER TABLE table_name ADD INDEX idx_name (column_name);
-- 添加唯一索引
ALTER TABLE table_name ADD UNIQUE INDEX uk_name (column_name);
-- 删除索引
ALTER TABLE table_name DROP INDEX idx_name;
-- 添加外键
ALTER TABLE table_name ADD CONSTRAINT fk_name
FOREIGN KEY (column_name) REFERENCES ref_table(ref_column) ON DELETE CASCADE;
-- 删除外键
ALTER TABLE table_name DROP FOREIGN KEY fk_name;
-- 修改表注释
ALTER TABLE table_name COMMENT '新注释';
-- 修改字段注释
ALTER TABLE table_name MODIFY COLUMN column_name VARCHAR(100) COMMENT '新注释';
-- 删除表
DROP TABLE IF EXISTS table_name;
-- 清空表数据
TRUNCATE TABLE table_name;
-- 重命名表
ALTER TABLE old_name RENAME TO new_name;-- 字符串函数
SELECT CONCAT('Hello', ' ', 'World'); -- 连接字符串
SELECT SUBSTRING('Hello World', 1, 5); -- 截取字符串
SELECT LENGTH('Hello'); -- 字符串长度
SELECT UPPER('hello'); -- 转大写
SELECT LOWER('HELLO'); -- 转小写
SELECT TRIM(' hello '); -- 去除空格
SELECT REPLACE('Hello World', 'World', 'MySQL'); -- 替换字符串
-- 数值函数
SELECT ABS(-10); -- 绝对值
SELECT ROUND(3.14159, 2); -- 四舍五入
SELECT FLOOR(3.7); -- 向下取整
SELECT CEIL(3.2); -- 向上取整
SELECT MOD(10, 3); -- 取余
SELECT POW(2, 3); -- 幂运算
-- 日期时间函数
SELECT NOW(); -- 当前日期时间
SELECT CURDATE(); -- 当前日期
SELECT CURTIME(); -- 当前时间
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'); -- 格式化日期
SELECT DATE_ADD(NOW(), INTERVAL 1 DAY); -- 日期加1天
SELECT DATE_SUB(NOW(), INTERVAL 1 MONTH); -- 日期减1月
SELECT DATEDIFF('2024-01-02', '2024-01-01'); -- 日期差(天数)
SELECT YEAR(NOW()); -- 获取年份
SELECT MONTH(NOW()); -- 获取月份
SELECT DAY(NOW()); -- 获取日期
-- 条件函数
SELECT IF(1 > 0, 'Yes', 'No'); -- 条件判断
SELECT IFNULL(NULL, 'Default'); -- 空值处理
SELECT COALESCE(NULL, NULL, 'Value'); -- 返回第一个非空值
SELECT CASE WHEN 1 > 0 THEN 'Yes' ELSE 'No' END; -- 多条件判断
-- 聚合函数
SELECT COUNT(*) FROM table_name; -- 计数
SELECT SUM(column_name) FROM table_name; -- 求和
SELECT AVG(column_name) FROM table_name; -- 平均值
SELECT MAX(column_name) FROM table_name; -- 最大值
SELECT MIN(column_name) FROM table_name; -- 最小值
SELECT GROUP_CONCAT(column_name) FROM table_name; -- 分组连接
-- JSON 函数(MySQL 5.7+)
SELECT JSON_EXTRACT(json_column, '$.key'); -- 提取JSON值
SELECT json_column->>'$.key'; -- 提取并转换为字符串
SELECT JSON_OBJECT('key', 'value'); -- 创建JSON对象
SELECT JSON_ARRAY('value1', 'value2'); -- 创建JSON数组
SELECT JSON_SET(json_column, '$.key', 'value'); -- 设置JSON值# 备份整个数据库
mysqldump -u root -p wladmin > wladmin_backup.sql
# 备份特定表
mysqldump -u root -p wladmin user role > tables_backup.sql
# 备份数据库结构(不包含数据)
mysqldump -u root -p --no-data wladmin > wladmin_structure.sql
# 备份数据库数据(不包含结构)
mysqldump -u root -p --no-create-info wladmin > wladmin_data.sql
# 压缩备份
mysqldump -u root -p wladmin | gzip > wladmin_backup.sql.gz
# 备份时排除特定表
mysqldump -u root -p wladmin --ignore-table=wladmin.log_table > backup.sql
# 备份多个数据库
mysqldump -u root -p --databases db1 db2 > multi_db_backup.sql
# 备份所有数据库
mysqldump -u root -p --all-databases > all_databases_backup.sql# 恢复整个数据库
mysql -u root -p wladmin < wladmin_backup.sql
# 恢复压缩的备份
gunzip < wladmin_backup.sql.gz | mysql -u root -p wladmin
# 恢复特定表
mysql -u root -p wladmin < tables_backup.sql
# 恢复数据库结构
mysql -u root -p wladmin < wladmin_structure.sql
# 恢复数据库数据
mysql -u root -p wladmin < wladmin_data.sql# 备份(在容器中执行)
docker compose exec mysql mysqldump -uroot -p${MYSQL_ROOT_PASSWORD} wladmin > backup.sql
# 恢复(在容器中执行)
docker compose exec -T mysql mysql -uroot -p${MYSQL_ROOT_PASSWORD} wladmin < backup.sql
# 备份到宿主机
docker compose exec mysql mysqldump -uroot -p${MYSQL_ROOT_PASSWORD} wladmin > /backup/wladmin_$(date +%Y%m%d).sql
# 从宿主机恢复
docker compose exec -T mysql mysql -uroot -p${MYSQL_ROOT_PASSWORD} wladmin < /backup/wladmin_20240101.sql
# 备份并压缩
docker compose exec mysql mysqldump -uroot -p${MYSQL_ROOT_PASSWORD} wladmin | gzip > wladmin_$(date +%Y%m%d).sql.gz
# 从压缩文件恢复
gunzip < wladmin_20240101.sql.gz | docker compose exec -T mysql mysql -uroot -p${MYSQL_ROOT_PASSWORD} wladmin-- 查找重复的用户名
SELECT username, COUNT(*) AS count
FROM user
WHERE is_delete = 0
GROUP BY username
HAVING COUNT(*) > 1;
-- 查找重复记录(保留ID最小的)
SELECT u1.*
FROM user u1
INNER JOIN (
SELECT username, MIN(id) AS min_id
FROM user
WHERE is_delete = 0
GROUP BY username
HAVING COUNT(*) > 1
) u2 ON u1.username = u2.username AND u1.id > u2.min_id
WHERE u1.is_delete = 0;-- 批量更新数据(使用事务)
START TRANSACTION;
UPDATE user SET status = 0 WHERE status IS NULL;
UPDATE role SET is_delete = 0 WHERE is_delete IS NULL;
-- 检查更新结果
SELECT COUNT(*) FROM user WHERE status = 0;
SELECT COUNT(*) FROM role WHERE is_delete = 0;
-- 确认无误后提交
COMMIT;
-- 或回滚
-- ROLLBACK;
-- 批量插入数据
INSERT INTO user (username, name, status, created_at, updated_at)
SELECT
CONCAT('user_', id) AS username,
CONCAT('用户', id) AS name,
0 AS status,
NOW() AS created_at,
NOW() AS updated_at
FROM (SELECT @row := @row + 1 AS id FROM (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t1, (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t2, (SELECT @row := 0) t3) numbers
WHERE id <= 100;-- 统计各状态用户数量
SELECT
status,
CASE status
WHEN 0 THEN '启用'
WHEN 1 THEN '停用'
ELSE '未知'
END AS status_name,
COUNT(*) AS count
FROM user
WHERE is_delete = 0
GROUP BY status;
-- 按日期统计创建用户数
SELECT
DATE(created_at) AS date,
COUNT(*) AS user_count
FROM user
WHERE is_delete = 0
AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DATE(created_at)
ORDER BY date DESC;
-- 统计表的数据增长趋势
SELECT
TABLE_NAME AS '表名',
TABLE_ROWS AS '当前行数',
ROUND(DATA_LENGTH / 1024 / 1024, 2) AS '数据大小(MB)',
ROUND(INDEX_LENGTH / 1024 / 1024, 2) AS '索引大小(MB)',
ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS '总大小(MB)'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin'
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;-- 查看未使用的索引(MySQL 8.0+)
SELECT
OBJECT_SCHEMA,
OBJECT_NAME,
INDEX_NAME
FROM sys.schema_unused_indexes
WHERE OBJECT_SCHEMA = 'wladmin';
-- 查看索引使用统计
SELECT
OBJECT_SCHEMA,
OBJECT_NAME,
INDEX_NAME,
COUNT_FETCH,
COUNT_INSERT,
COUNT_UPDATE,
COUNT_DELETE
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE OBJECT_SCHEMA = 'wladmin'
ORDER BY COUNT_FETCH DESC;
-- 查看慢查询(需要开启慢查询日志)
SELECT
sql_text,
exec_count,
avg_timer_wait / 1000000000000 AS avg_time_sec,
sum_timer_wait / 1000000000000 AS total_time_sec
FROM performance_schema.events_statements_summary_by_digest
WHERE SCHEMA_NAME = 'wladmin'
ORDER BY sum_timer_wait DESC
LIMIT 10;-- 清理软删除的数据(谨慎使用)
-- 先查看要删除的数据量
SELECT COUNT(*) FROM user WHERE is_delete = 1;
-- 备份要删除的数据
CREATE TABLE user_deleted_backup AS
SELECT * FROM user WHERE is_delete = 1;
-- 删除软删除的数据(使用事务)
START TRANSACTION;
DELETE FROM user WHERE is_delete = 1 AND created_at < DATE_SUB(NOW(), INTERVAL 90 DAY);
-- 检查删除结果
SELECT ROW_COUNT();
COMMIT;
-- 清理过期数据(如监控数据)
DELETE FROM monitor_data
WHERE timestamp < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY)) * 1000;
-- 优化表(重建索引,整理碎片)
OPTIMIZE TABLE user;
OPTIMIZE TABLE role;
OPTIMIZE TABLE department;-- 验证数据完整性:检查孤立的外键数据
SELECT
'user.department_id' AS foreign_key,
COUNT(*) AS orphan_count
FROM user u
LEFT JOIN department d ON u.department_id = d.id
WHERE u.department_id IS NOT NULL
AND d.id IS NULL
AND u.is_delete = 0
UNION ALL
SELECT
'role.dept_id' AS foreign_key,
COUNT(*) AS orphan_count
FROM role r
LEFT JOIN department d ON r.dept_id = d.id
WHERE r.dept_id IS NOT NULL
AND d.id IS NULL
AND r.is_delete = 0;
-- 验证必填字段是否有空值
SELECT
'user' AS table_name,
COUNT(*) AS null_count,
'username' AS column_name
FROM user
WHERE username IS NULL OR username = ''
UNION ALL
SELECT
'user' AS table_name,
COUNT(*) AS null_count,
'name' AS column_name
FROM user
WHERE name IS NULL OR name = '';
-- 验证日期字段的合理性
SELECT
id,
username,
created_at,
updated_at
FROM user
WHERE created_at > NOW()
OR updated_at > NOW()
OR updated_at < created_at;# my.cnf 或 my.ini
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
[client]
default-character-set=utf8mb4# InnoDB 配置
innodb_buffer_pool_size = 1G # 根据服务器内存调整
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
innodb_file_per_table = 1
# 查询缓存(MySQL 5.7及以下)
query_cache_type = 1
query_cache_size = 64M
# 连接配置
max_connections = 200
wait_timeout = 600
interactive_timeout = 600# 开启慢查询日志
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow-query.log
long_query_time = 2 # 超过2秒的查询记录
log_queries_not_using_indexes = 1 # 记录未使用索引的查询CTE(Common Table Expression,公用表表达式)是 MySQL 8.0+ 引入的强大特性,可以创建临时的命名结果集,在查询中多次引用,提高查询的可读性和性能。
-- 基本语法
WITH cte_name AS (
SELECT ...
)
SELECT * FROM cte_name;
-- 多个CTE
WITH cte1 AS (
SELECT ...
),
cte2 AS (
SELECT ...
)
SELECT * FROM cte1 JOIN cte2 ON ...;-- 场景:查询用户及其最近一次操作和操作次数
WITH latest_audit AS (
SELECT
a.*,
ROW_NUMBER() OVER (PARTITION BY a.user_id ORDER BY a.operation_time DESC) AS rn
FROM audit_logs a
WHERE a.operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
),
user_action_count AS (
SELECT
user_id,
COUNT(*) AS action_count
FROM audit_logs
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY user_id
)
SELECT
u.id,
u.username,
u.name,
la.operation_time AS last_action_time,
la.action_type AS last_action_type,
uac.action_count
FROM user u
LEFT JOIN latest_audit la ON u.id = la.user_id AND la.rn = 1
LEFT JOIN user_action_count uac ON u.id = uac.user_id
WHERE u.is_delete = 0
ORDER BY uac.action_count DESC;-- 使用子查询(复杂且难以理解)
SELECT
u.id,
u.username,
(SELECT COUNT(*) FROM audit_logs WHERE user_id = u.id) AS action_count,
(SELECT MAX(operation_time) FROM audit_logs WHERE user_id = u.id) AS last_action_time
FROM user u
WHERE u.is_delete = 0;
-- 使用CTE(清晰易懂)
WITH user_stats AS (
SELECT
user_id,
COUNT(*) AS action_count,
MAX(operation_time) AS last_action_time
FROM audit_logs
GROUP BY user_id
)
SELECT
u.id,
u.username,
COALESCE(us.action_count, 0) AS action_count,
us.last_action_time
FROM user u
LEFT JOIN user_stats us ON u.id = us.user_id
WHERE u.is_delete = 0;递归 CTE 用于处理树形结构数据,如部门层级、菜单层级等。
-- 查询指定部门及其所有子部门
WITH RECURSIVE dept_tree AS (
-- 基础查询:根部门
SELECT
id,
name,
parent_id,
code,
0 AS level,
CAST(id AS CHAR(1000)) AS path
FROM department
WHERE id = 1 AND is_delete = 0
UNION ALL
-- 递归查询:子部门
SELECT
d.id,
d.name,
d.parent_id,
d.code,
dt.level + 1,
CONCAT(dt.path, '->', d.id)
FROM department d
INNER JOIN dept_tree dt ON d.parent_id = dt.id
WHERE d.is_delete = 0
)
SELECT * FROM dept_tree ORDER BY level, id;-- 查询指定部门到根部门的完整路径
WITH RECURSIVE dept_path AS (
-- 基础查询:当前部门
SELECT
id,
name,
parent_id,
CAST(name AS CHAR(1000)) AS path
FROM department
WHERE id = 10 AND is_delete = 0
UNION ALL
-- 递归查询:父部门
SELECT
d.id,
d.name,
d.parent_id,
CONCAT(d.name, ' / ', dp.path)
FROM department d
INNER JOIN dept_path dp ON d.id = dp.parent_id
WHERE d.is_delete = 0
)
SELECT * FROM dept_path ORDER BY id;-- 查询完整的菜单树结构
WITH RECURSIVE menu_tree AS (
-- 基础查询:根菜单(parent_id为0或NULL)
SELECT
id,
name,
parent_id,
route_path,
icon,
0 AS level,
CAST(id AS CHAR(1000)) AS path,
CAST(name AS CHAR(1000)) AS full_path
FROM menu
WHERE (parent_id = 0 OR parent_id IS NULL) AND is_delete = 0
UNION ALL
-- 递归查询:子菜单
SELECT
m.id,
m.name,
m.parent_id,
m.route_path,
m.icon,
mt.level + 1,
CONCAT(mt.path, '->', m.id),
CONCAT(mt.full_path, ' > ', m.name)
FROM menu m
INNER JOIN menu_tree mt ON m.parent_id = mt.id
WHERE m.is_delete = 0
)
SELECT
id,
name,
parent_id,
route_path,
icon,
level,
path,
full_path
FROM menu_tree
ORDER BY path;| 特性 | CTE | 临时表 |
|---|---|---|
| 生命周期 | 单次查询 | 会话期间 |
| 可重用性 | 仅在同一查询中 | 可在多个查询中使用 |
| 可更新 | ❌ 只读 | ✅ 可更新 |
| 索引 | ❌ 不支持 | ✅ 支持 |
| 性能 | 查询优化器优化 | 需要手动优化 |
| 适用场景 | 复杂查询简化 | 多次查询、数据转换 |
窗口函数是 MySQL 8.0+ 引入的强大功能,可以在不减少行数的情况下对数据进行聚合、排序、排名等操作。
-- 基本语法
SELECT
column1,
window_function() OVER (
[PARTITION BY column]
[ORDER BY column]
[ROWS/RANGE BETWEEN ... AND ...]
) AS alias
FROM table_name;-- 为每行分配唯一的序号
SELECT
id,
username,
action_count,
ROW_NUMBER() OVER (ORDER BY action_count DESC) AS rank_num
FROM (
SELECT
u.id,
u.username,
COUNT(a.id) AS action_count
FROM user u
LEFT JOIN audit_logs a ON u.id = a.user_id
WHERE u.is_delete = 0
GROUP BY u.id, u.username
) user_stats;-- RANK(): 相同值排名相同,但会跳过后续排名
-- DENSE_RANK(): 相同值排名相同,不跳过后续排名
SELECT
id,
username,
action_count,
RANK() OVER (ORDER BY action_count DESC) AS rank_with_gaps,
DENSE_RANK() OVER (ORDER BY action_count DESC) AS rank_no_gaps
FROM (
SELECT
u.id,
u.username,
COUNT(a.id) AS action_count
FROM user u
LEFT JOIN audit_logs a ON u.id = a.user_id
WHERE u.is_delete = 0
GROUP BY u.id, u.username
) user_stats;-- PERCENT_RANK(): 返回排名百分比(0-1)
-- CUME_DIST(): 返回累积分布(0-1)
SELECT
id,
username,
action_count,
PERCENT_RANK() OVER (ORDER BY action_count DESC) AS percent_rank,
CUME_DIST() OVER (ORDER BY action_count DESC) AS cumulative_dist
FROM user_stats;-- 计算累计和
SELECT
DATE(operation_time) AS date,
COUNT(*) AS daily_count,
SUM(COUNT(*)) OVER (ORDER BY DATE(operation_time)) AS cumulative_count
FROM audit_logs
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DATE(operation_time)
ORDER BY date;-- 计算移动平均
SELECT
DATE(operation_time) AS date,
COUNT(*) AS daily_count,
AVG(COUNT(*)) OVER (
ORDER BY DATE(operation_time)
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS moving_avg_7days
FROM audit_logs
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DATE(operation_time)
ORDER BY date;-- 计算分区内的行数
SELECT
user_id,
operation_time,
action_type,
COUNT(*) OVER (PARTITION BY user_id) AS user_total_actions,
COUNT(*) OVER (PARTITION BY user_id, DATE(operation_time)) AS user_daily_actions
FROM audit_logs
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
ORDER BY user_id, operation_time;-- LAG(): 获取前一行数据
-- LEAD(): 获取后一行数据
SELECT
DATE(operation_time) AS date,
COUNT(*) AS daily_count,
LAG(COUNT(*)) OVER (ORDER BY DATE(operation_time)) AS prev_day_count,
LEAD(COUNT(*)) OVER (ORDER BY DATE(operation_time)) AS next_day_count,
COUNT(*) - LAG(COUNT(*)) OVER (ORDER BY DATE(operation_time)) AS day_diff
FROM audit_logs
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DATE(operation_time)
ORDER BY date;-- FIRST_VALUE(): 获取分区内的第一个值
-- LAST_VALUE(): 获取分区内的最后一个值
SELECT
user_id,
operation_time,
action_type,
FIRST_VALUE(action_type) OVER (
PARTITION BY user_id
ORDER BY operation_time
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS first_action,
LAST_VALUE(action_type) OVER (
PARTITION BY user_id
ORDER BY operation_time
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
) AS last_action
FROM audit_logs
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
ORDER BY user_id, operation_time;-- ROWS: 基于行数
SELECT
date,
daily_count,
SUM(daily_count) OVER (
ORDER BY date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS sum_3days
FROM daily_stats;
-- RANGE: 基于值范围
SELECT
date,
daily_count,
SUM(daily_count) OVER (
ORDER BY date
RANGE BETWEEN INTERVAL 2 DAY PRECEDING AND CURRENT ROW
) AS sum_3days_range
FROM daily_stats;
-- UNBOUNDED: 无界
SELECT
date,
daily_count,
SUM(daily_count) OVER (
ORDER BY date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cumulative_sum
FROM daily_stats;-- 使用窗口函数获取每个用户的最新操作
WITH ranked_audit AS (
SELECT
a.*,
ROW_NUMBER() OVER (
PARTITION BY a.user_id
ORDER BY a.operation_time DESC, a.id DESC
) AS rn
FROM audit_logs a
WHERE a.operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
)
SELECT
ra.user_id,
ra.username,
ra.operation_time AS last_action_time,
ra.action_type AS last_action_type,
ra.url AS last_url
FROM ranked_audit ra
WHERE ra.rn = 1;-- 计算日活跃用户的同比和环比
WITH daily_active_users AS (
SELECT
DATE(operation_time) AS date,
COUNT(DISTINCT user_id) AS active_users
FROM audit_logs
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 60 DAY)
GROUP BY DATE(operation_time)
)
SELECT
date,
active_users,
LAG(active_users, 1) OVER (ORDER BY date) AS prev_day,
LAG(active_users, 7) OVER (ORDER BY date) AS prev_week,
ROUND(
(active_users - LAG(active_users, 1) OVER (ORDER BY date)) * 100.0 /
LAG(active_users, 1) OVER (ORDER BY date),
2
) AS day_over_day_pct,
ROUND(
(active_users - LAG(active_users, 7) OVER (ORDER BY date)) * 100.0 /
LAG(active_users, 7) OVER (ORDER BY date),
2
) AS week_over_week_pct
FROM daily_active_users
ORDER BY date DESC;子查询是嵌套在主查询中的查询,可以用于 WHERE、FROM、SELECT 等子句中。
-- 返回单个值的子查询
SELECT
id,
username,
(SELECT COUNT(*) FROM audit_logs WHERE user_id = u.id) AS action_count,
(SELECT MAX(operation_time) FROM audit_logs WHERE user_id = u.id) AS last_action_time
FROM user u
WHERE u.is_delete = 0;-- 子查询引用外部查询的列
SELECT
u.id,
u.username,
(
SELECT COUNT(*)
FROM audit_logs a
WHERE a.user_id = u.id
AND a.operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
) AS recent_action_count
FROM user u
WHERE u.is_delete = 0;-- EXISTS: 检查子查询是否返回行
SELECT
u.id,
u.username
FROM user u
WHERE u.is_delete = 0
AND EXISTS (
SELECT 1
FROM audit_logs a
WHERE a.user_id = u.id
AND a.operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
);
-- NOT EXISTS: 检查子查询是否不返回行
SELECT
u.id,
u.username
FROM user u
WHERE u.is_delete = 0
AND NOT EXISTS (
SELECT 1
FROM audit_logs a
WHERE a.user_id = u.id
AND a.operation_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
);-- IN: 检查值是否在子查询结果中
SELECT
u.id,
u.username
FROM user u
WHERE u.is_delete = 0
AND u.id IN (
SELECT DISTINCT user_id
FROM audit_logs
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
);
-- NOT IN: 检查值是否不在子查询结果中(注意NULL值)
SELECT
u.id,
u.username
FROM user u
WHERE u.is_delete = 0
AND u.id NOT IN (
SELECT user_id
FROM audit_logs
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
AND user_id IS NOT NULL -- 重要:排除NULL值
);-- ANY/SOME: 只要有一个值满足条件即可
SELECT
u.id,
u.username
FROM user u
WHERE u.is_delete = 0
AND u.id = ANY (
SELECT user_id
FROM audit_logs
WHERE action_type = 'login'
);
-- ALL: 所有值都必须满足条件
SELECT
u.id,
u.username
FROM user u
WHERE u.is_delete = 0
AND u.id = ALL (
SELECT user_id
FROM audit_logs
WHERE action_type = 'login'
GROUP BY user_id
HAVING COUNT(*) > 10
);-- ROLLUP: 生成小计和总计
SELECT
COALESCE(d.name, '总计') AS dept_name,
COALESCE(CAST(u.status AS CHAR), '小计') AS status,
COUNT(*) AS user_count
FROM user u
LEFT JOIN department d ON u.department_id = d.id
WHERE u.is_delete = 0
GROUP BY d.name, u.status WITH ROLLUP
ORDER BY d.name, u.status;-- GROUPING(): 判断是否为汇总行
SELECT
CASE
WHEN GROUPING(d.name) = 1 THEN '总计'
ELSE COALESCE(d.name, '未分配')
END AS dept_name,
CASE
WHEN GROUPING(u.status) = 1 THEN '小计'
ELSE CAST(u.status AS CHAR)
END AS status,
COUNT(*) AS user_count
FROM user u
LEFT JOIN department d ON u.department_id = d.id
WHERE u.is_delete = 0
GROUP BY d.name, u.status WITH ROLLUP
ORDER BY d.name, u.status;-- HAVING: 对分组结果进行过滤
SELECT
u.department_id,
d.name AS dept_name,
COUNT(*) AS user_count,
AVG(TIMESTAMPDIFF(DAY, u.created_at, NOW())) AS avg_days_since_created
FROM user u
LEFT JOIN department d ON u.department_id = d.id
WHERE u.is_delete = 0
GROUP BY u.department_id, d.name
HAVING user_count > 5
AND avg_days_since_created > 30
ORDER BY user_count DESC;-- GROUP_CONCAT: 将分组内的值连接成字符串
SELECT
r.id,
r.name,
GROUP_CONCAT(u.username ORDER BY u.username SEPARATOR ', ') AS users
FROM role r
LEFT JOIN user_role ur ON r.id = ur.role_id
LEFT JOIN user u ON ur.user_id = u.id AND u.is_delete = 0
WHERE r.is_delete = 0
GROUP BY r.id, r.name;
-- 使用 DISTINCT 去重
SELECT
d.id,
d.name,
GROUP_CONCAT(DISTINCT u.username ORDER BY u.username SEPARATOR ', ') AS users
FROM department d
LEFT JOIN user u ON d.id = u.department_id AND u.is_delete = 0
WHERE d.is_delete = 0
GROUP BY d.id, d.name;-- UNION: 合并结果集并去重
SELECT id, username, 'user' AS type FROM user WHERE is_delete = 0
UNION
SELECT id, name, 'role' AS type FROM role WHERE is_delete = 0;
-- UNION ALL: 合并结果集不去重(性能更好)
SELECT id, username, 'user' AS type FROM user WHERE is_delete = 0
UNION ALL
SELECT id, name, 'role' AS type FROM role WHERE is_delete = 0;-- 场景:合并不同来源的数据
SELECT
'active' AS status,
COUNT(*) AS count
FROM user
WHERE is_delete = 0 AND status = 0
UNION ALL
SELECT
'inactive' AS status,
COUNT(*) AS count
FROM user
WHERE is_delete = 0 AND status = 1
UNION ALL
SELECT
'total' AS status,
COUNT(*) AS count
FROM user
WHERE is_delete = 0;-- JSON_EXTRACT / ->: 提取JSON值
SELECT
id,
before_data->'$.username' AS username,
JSON_EXTRACT(after_data, '$.status') AS status
FROM audit_logs
WHERE before_data IS NOT NULL;
-- ->>: 提取并转换为字符串
SELECT
id,
before_data->>'$.username' AS username,
after_data->>'$.status' AS status
FROM audit_logs;
-- JSON_OBJECT: 创建JSON对象
SELECT
id,
JSON_OBJECT(
'id', id,
'username', username,
'status', status
) AS user_json
FROM user
WHERE is_delete = 0
LIMIT 10;
-- JSON_ARRAY: 创建JSON数组
SELECT
d.id,
d.name,
JSON_ARRAY(
SELECT u.username
FROM user u
WHERE u.department_id = d.id AND u.is_delete = 0
) AS users
FROM department d
WHERE d.is_delete = 0;-- JSON_CONTAINS: 检查JSON是否包含值
SELECT *
FROM audit_logs
WHERE JSON_CONTAINS(before_data, '{"status": "1"}');
-- JSON_SEARCH: 搜索JSON中的值
SELECT *
FROM audit_logs
WHERE JSON_SEARCH(before_data, 'one', 'admin') IS NOT NULL;
-- JSON_SET: 设置JSON值
UPDATE audit_logs
SET before_data = JSON_SET(before_data, '$.new_field', 'new_value')
WHERE id = 1;
-- JSON_REMOVE: 删除JSON字段
UPDATE audit_logs
SET before_data = JSON_REMOVE(before_data, '$.old_field')
WHERE id = 1;-- 访问嵌套对象
SELECT
id,
data->'$.user.profile.name' AS user_name,
data->'$.user.profile.email' AS user_email
FROM table_with_json;
-- 访问数组元素
SELECT
id,
data->'$.items[0].name' AS first_item,
data->'$.items[*].name' AS all_items
FROM table_with_json;本章节汇总了在实际使用 MySQL 过程中遇到的常见问题及其排查和解决方法。
症状:插入或查询中文字符时显示为乱码(如 ??? 或 ``)。
排查步骤:
-- 1. 检查数据库字符集
SHOW CREATE DATABASE wladmin;
-- 2. 检查表字符集
SHOW CREATE TABLE table_name;
-- 3. 检查字段字符集
SELECT
COLUMN_NAME,
CHARACTER_SET_NAME,
COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'table_name'
AND CHARACTER_SET_NAME IS NOT NULL;
-- 4. 检查连接字符集
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';解决方法:
-- 修改数据库字符集(谨慎使用,会影响已存在的数据)
ALTER DATABASE wladmin CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 修改表字符集
ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 修改字段字符集
ALTER TABLE table_name MODIFY COLUMN column_name VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;预防措施:
CREATE DATABASE ... CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ciCREATE TABLE ... DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci症状:JOIN 查询执行缓慢,EXPLAIN 显示未使用索引。
排查步骤:
-- 查看字符集不一致的字段
SELECT
TABLE_NAME,
COLUMN_NAME,
CHARACTER_SET_NAME,
COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wladmin'
AND COLLATION_NAME != 'utf8mb4_unicode_ci'
AND CHARACTER_SET_NAME IS NOT NULL;
-- 检查 JOIN 字段的字符集是否一致
SELECT
t1.TABLE_NAME AS table1,
t1.COLUMN_NAME AS column1,
t1.COLLATION_NAME AS collation1,
t2.TABLE_NAME AS table2,
t2.COLUMN_NAME AS column2,
t2.COLLATION_NAME AS collation2
FROM information_schema.COLUMNS t1
JOIN information_schema.COLUMNS t2
ON t1.TABLE_SCHEMA = t2.TABLE_SCHEMA
WHERE t1.TABLE_SCHEMA = 'wladmin'
AND t1.COLLATION_NAME != t2.COLLATION_NAME
AND t1.CHARACTER_SET_NAME IS NOT NULL
AND t2.CHARACTER_SET_NAME IS NOT NULL;解决方法:统一字符集和排序规则,确保 JOIN 字段的字符集一致。
症状:查询执行时间很长,EXPLAIN 显示 type=ALL(全表扫描)。
排查步骤:
-- 1. 使用 EXPLAIN 分析查询
EXPLAIN SELECT * FROM user WHERE name = 'test';
-- 2. 检查索引是否存在
SHOW INDEX FROM user;
-- 3. 检查索引统计信息
SHOW INDEX FROM user WHERE Key_name = 'idx_name';
-- 4. 查看索引使用情况(MySQL 8.0+)
SELECT
OBJECT_SCHEMA,
OBJECT_NAME,
INDEX_NAME,
COUNT_FETCH,
COUNT_INSERT,
COUNT_UPDATE,
COUNT_DELETE
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE OBJECT_SCHEMA = 'wladmin'
AND OBJECT_NAME = 'user'
ORDER BY COUNT_FETCH DESC;解决方法:
-- 1. 创建缺失的索引
ALTER TABLE user ADD INDEX idx_name (name);
-- 2. 更新索引统计信息
ANALYZE TABLE user;
-- 3. 检查查询条件是否使用了函数(会导致索引失效)
-- 错误示例:WHERE DATE(created_at) = '2024-01-01'
-- 正确示例:WHERE created_at >= '2024-01-01' AND created_at < '2024-01-02'
-- 4. 检查是否使用了 LIKE '%value%'(前导通配符会导致索引失效)
-- 如果必须使用,考虑全文索引或搜索引擎症状:表中有多个功能相同的索引,占用存储空间,影响写入性能。
排查步骤:
-- 查找重复索引
SELECT
s1.TABLE_NAME,
s1.COLUMN_NAME,
s1.INDEX_NAME AS index1,
s2.INDEX_NAME AS index2,
s1.SEQ_IN_INDEX AS seq1,
s2.SEQ_IN_INDEX AS seq2
FROM information_schema.STATISTICS s1
JOIN information_schema.STATISTICS s2
ON s1.TABLE_SCHEMA = s2.TABLE_SCHEMA
AND s1.TABLE_NAME = s2.TABLE_NAME
AND s1.COLUMN_NAME = s2.COLUMN_NAME
AND s1.INDEX_NAME < s2.INDEX_NAME
WHERE s1.TABLE_SCHEMA = 'wladmin'
ORDER BY s1.TABLE_NAME, s1.COLUMN_NAME;解决方法:
-- 删除重复的索引(保留更合适的索引)
ALTER TABLE user DROP INDEX idx_name_old;症状:插入或更新数据时提示外键约束错误。
排查步骤:
-- 查看外键约束详细信息
SELECT
rc.CONSTRAINT_NAME AS '外键名',
rc.TABLE_NAME AS '表名',
kcu.COLUMN_NAME AS '字段名',
rc.REFERENCED_TABLE_NAME AS '引用表名',
kcu.REFERENCED_COLUMN_NAME AS '引用字段名',
rc.UPDATE_RULE AS '更新规则',
rc.DELETE_RULE AS '删除规则'
FROM information_schema.REFERENTIAL_CONSTRAINTS rc
JOIN information_schema.KEY_COLUMN_USAGE kcu
ON rc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
AND rc.TABLE_SCHEMA = kcu.TABLE_SCHEMA
WHERE rc.CONSTRAINT_SCHEMA = 'wladmin'
AND rc.TABLE_NAME = 'table_name';
-- 检查外键值是否存在
SELECT
u.id,
u.department_id,
d.id AS dept_exists
FROM user u
LEFT JOIN department d ON u.department_id = d.id
WHERE u.department_id IS NOT NULL
AND d.id IS NULL;解决方法:
-- 1. 修复数据:更新或删除无效的外键值
UPDATE user
SET department_id = NULL
WHERE department_id NOT IN (SELECT id FROM department WHERE is_delete = 0);
-- 2. 临时禁用外键检查(谨慎使用)
SET FOREIGN_KEY_CHECKS = 0;
-- 执行操作
SET FOREIGN_KEY_CHECKS = 1;
-- 3. 修改外键约束规则
ALTER TABLE user
DROP FOREIGN KEY fk_user_department,
ADD CONSTRAINT fk_user_department
FOREIGN KEY (department_id) REFERENCES department(id)
ON DELETE SET NULL; -- 改为 SET NULL 而不是 RESTRICT症状:删除记录时提示外键约束错误,无法删除。
排查步骤:
-- 查看哪些表引用了当前表
SELECT
CONSTRAINT_NAME AS '外键名',
TABLE_NAME AS '约束表',
COLUMN_NAME AS '约束字段',
REFERENCED_TABLE_NAME AS '引用表',
REFERENCED_COLUMN_NAME AS '引用字段',
UPDATE_RULE AS '更新规则',
DELETE_RULE AS '删除规则'
FROM information_schema.REFERENTIAL_CONSTRAINTS rc
JOIN information_schema.KEY_COLUMN_USAGE kcu
ON rc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
AND rc.TABLE_SCHEMA = kcu.TABLE_SCHEMA
WHERE rc.CONSTRAINT_SCHEMA = 'wladmin'
AND rc.REFERENCED_TABLE_NAME = 'user';
-- 查看引用当前记录的子记录
SELECT
'user_role' AS table_name,
COUNT(*) AS reference_count
FROM user_role
WHERE user_id = 1
UNION ALL
SELECT
'audit_logs' AS table_name,
COUNT(*) AS reference_count
FROM audit_logs
WHERE user_id = 1;解决方法:
-- 1. 先删除子记录(如果 DELETE_RULE 是 RESTRICT)
DELETE FROM user_role WHERE user_id = 1;
DELETE FROM audit_logs WHERE user_id = 1;
DELETE FROM user WHERE id = 1;
-- 2. 使用 CASCADE 规则(自动删除子记录)
-- 需要修改外键约束规则
-- 3. 使用 SET NULL 规则(将子记录的外键设为 NULL)
-- 需要修改外键约束规则症状:查询执行时间超过预期,影响用户体验。
排查步骤:
-- 1. 使用 EXPLAIN 分析查询
EXPLAIN SELECT * FROM user WHERE username = 'admin';
-- 2. 查看详细执行计划(MySQL 5.6+)
EXPLAIN FORMAT=JSON SELECT * FROM user WHERE username = 'admin';
-- 3. 查看实际执行时间(MySQL 8.0+)
EXPLAIN ANALYZE SELECT * FROM user WHERE username = 'admin';
-- 4. 查看表的大小和行数
SELECT
TABLE_NAME AS '表名',
ROUND(DATA_LENGTH / 1024 / 1024, 2) AS '数据大小(MB)',
ROUND(INDEX_LENGTH / 1024 / 1024, 2) AS '索引大小(MB)',
ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS '总大小(MB)',
TABLE_ROWS AS '行数'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin'
AND TABLE_NAME = 'user';
-- 5. 查看表的碎片情况
SHOW TABLE STATUS LIKE 'user';
-- 6. 查看慢查询日志
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';解决方法:
-- 1. 创建或优化索引
ALTER TABLE user ADD INDEX idx_username (username);
ANALYZE TABLE user;
-- 2. 优化表(重建索引,整理碎片)
OPTIMIZE TABLE user;
-- 3. 更新统计信息
ANALYZE TABLE user;
-- 4. 检查查询条件,避免全表扫描
-- 使用索引字段作为查询条件
-- 避免在 WHERE 子句中使用函数
-- 5. 限制返回行数
SELECT * FROM user WHERE ... LIMIT 100;
-- 6. 使用覆盖索引(只查询索引字段)
SELECT id, username FROM user WHERE username = 'admin';症状:执行更新或删除操作时提示锁等待超时。
排查步骤:
-- 1. 查看当前锁等待情况
SELECT
r.trx_id AS waiting_trx_id,
r.trx_mysql_thread_id AS waiting_thread,
r.trx_query AS waiting_query,
b.trx_id AS blocking_trx_id,
b.trx_mysql_thread_id AS blocking_thread,
b.trx_query AS blocking_query
FROM information_schema.innodb_lock_waits w
INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;
-- 2. 查看当前正在执行的查询
SHOW FULL PROCESSLIST;
-- 3. 查看事务状态
SELECT * FROM information_schema.innodb_trx;解决方法:
-- 1. 杀死阻塞的查询
KILL [thread_id];
-- 2. 优化事务,减少锁定时间
-- 将长事务拆分为多个短事务
-- 在事务中只锁定必要的行
-- 3. 使用合适的隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 4. 使用 NOWAIT 或 SKIP LOCKED(MySQL 8.0+)
SELECT * FROM user WHERE id = 1 FOR UPDATE NOWAIT;症状:外键字段指向不存在的记录,导致数据不一致。
排查步骤:
-- 检查用户表中部门ID不存在的记录
SELECT
u.id,
u.username,
u.department_id,
'部门不存在' AS issue
FROM user u
LEFT JOIN department d ON u.department_id = d.id
WHERE u.department_id IS NOT NULL
AND d.id IS NULL
AND u.is_delete = 0;
-- 检查角色表中部门ID不存在的记录
SELECT
r.id,
r.name,
r.dept_id,
'部门不存在' AS issue
FROM role r
LEFT JOIN department d ON r.dept_id = d.id
WHERE r.dept_id IS NOT NULL
AND d.id IS NULL
AND r.is_delete = 0;
-- 检查用户角色关联表中用户ID不存在的记录
SELECT
ur.user_id,
ur.role_id,
'用户不存在' AS issue
FROM user_role ur
LEFT JOIN user u ON ur.user_id = u.id
WHERE u.id IS NULL;
-- 检查用户角色关联表中角色ID不存在的记录
SELECT
ur.user_id,
ur.role_id,
'角色不存在' AS issue
FROM user_role ur
LEFT JOIN role r ON ur.role_id = r.id
WHERE r.id IS NULL OR r.is_delete = 1;解决方法:
-- 1. 修复孤立数据:将无效外键设为 NULL
UPDATE user
SET department_id = NULL
WHERE department_id IS NOT NULL
AND department_id NOT IN (
SELECT id FROM department WHERE is_delete = 0
);
-- 2. 删除无效的关联记录
DELETE FROM user_role
WHERE user_id NOT IN (SELECT id FROM user WHERE is_delete = 0)
OR role_id NOT IN (SELECT id FROM role WHERE is_delete = 0);
-- 3. 添加外键约束(如果还没有)
ALTER TABLE user
ADD CONSTRAINT fk_user_department
FOREIGN KEY (department_id) REFERENCES department(id)
ON DELETE SET NULL;症状:表中存在重复的记录,违反唯一性约束。
排查步骤:
-- 查找重复的用户名
SELECT
username,
COUNT(*) AS count,
GROUP_CONCAT(id ORDER BY id) AS ids
FROM user
WHERE is_delete = 0
GROUP BY username
HAVING COUNT(*) > 1;
-- 查找重复的记录(保留ID最小的)
SELECT u1.*
FROM user u1
INNER JOIN (
SELECT username, MIN(id) AS min_id
FROM user
WHERE is_delete = 0
GROUP BY username
HAVING COUNT(*) > 1
) u2 ON u1.username = u2.username AND u1.id > u2.min_id
WHERE u1.is_delete = 0;解决方法:
-- 1. 删除重复记录(保留ID最小的)
DELETE u1 FROM user u1
INNER JOIN (
SELECT username, MIN(id) AS min_id
FROM user
WHERE is_delete = 0
GROUP BY username
HAVING COUNT(*) > 1
) u2 ON u1.username = u2.username AND u1.id > u2.min_id
WHERE u1.is_delete = 0;
-- 2. 添加唯一索引(防止未来重复)
ALTER TABLE user ADD UNIQUE INDEX uk_username_delete (username, is_delete);排查步骤:
-- 查看数据库大小
SELECT
TABLE_SCHEMA AS '数据库',
ROUND(SUM(DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS '大小(MB)'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin'
GROUP BY TABLE_SCHEMA;
-- 查看各表的大小
SELECT
TABLE_NAME AS '表名',
ROUND(DATA_LENGTH / 1024 / 1024, 2) AS '数据大小(MB)',
ROUND(INDEX_LENGTH / 1024 / 1024, 2) AS '索引大小(MB)',
ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS '总大小(MB)',
TABLE_ROWS AS '行数',
ROUND((DATA_LENGTH + INDEX_LENGTH) / TABLE_ROWS / 1024, 2) AS '平均行大小(KB)'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wladmin'
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;解决方法:
-- 1. 清理过期数据
DELETE FROM audit_logs
WHERE operation_time < DATE_SUB(NOW(), INTERVAL 90 DAY);
-- 2. 优化表(整理碎片)
OPTIMIZE TABLE table_name;
-- 3. 归档历史数据到归档表
CREATE TABLE audit_logs_archive LIKE audit_logs;
INSERT INTO audit_logs_archive
SELECT * FROM audit_logs
WHERE operation_time < DATE_SUB(NOW(), INTERVAL 90 DAY);
DELETE FROM audit_logs
WHERE operation_time < DATE_SUB(NOW(), INTERVAL 90 DAY);排查步骤:
-- 查看当前连接数
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';
-- 查看最大连接数
SHOW VARIABLES LIKE 'max_connections';
-- 查看当前所有连接
SHOW PROCESSLIST;
-- 查看连接详情
SELECT
ID,
USER,
HOST,
DB,
COMMAND,
TIME,
STATE,
INFO
FROM information_schema.PROCESSLIST
WHERE COMMAND != 'Sleep'
ORDER BY TIME DESC;解决方法:
-- 1. 增加最大连接数(需要重启或修改配置文件)
SET GLOBAL max_connections = 500;
-- 2. 杀死长时间运行的查询
KILL [thread_id];
-- 3. 优化应用代码,使用连接池
-- 4. 检查是否有连接泄漏涵盖常用的 SQL 操作和最佳实践