一、视图(View)
1.1 什么是视图
视图是一种虚拟表 ,它本身不存储数据,只存储一条预定义的 SELECT 查询语句。当你查询视图时,MySQL 会动态执行这条 SELECT 语句,把结果当作一张表返回给你。
注意:这里的"视图"和事务 MVCC 中的"ReadView(读视图)"完全是两个概念,没有任何关系。
- 本章的视图(View):数据库对象,是 SELECT 语句的别名/封装,是持久化的虚拟表。
- 事务中的 ReadView:内存中的数据结构,用于 MVCC 判断数据版本可见性,事务结束就销毁。
1.2 创建视图
语法
CREATE VIEW 视图名 AS SELECT语句;
示例
CREATE VIEW myview AS
SELECT EMP.ename, DEPT.deptno, DEPT.dname
FROM EMP
INNER JOIN DEPT ON EMP.deptno = DEPT.deptno;
创建后,myview 就可以像普通表一样查询:
SELECT * FROM myview;
这等价于直接执行那条复杂的多表连接查询。
1.3 视图的本质:不存数据,只存定义
视图的核心特点是只存储 SQL 定义,不存储查询结果数据。
- 视图的数据来自基表(底层的真实表),是查询时动态计算出来的;
- 基表数据变化时,视图的查询结果也会随之变化;
- 对视图执行 DML(INSERT/UPDATE/DELETE),本质上是在修改基表的数据------因为视图本身没有数据可改。
视图不存储数据,对视图的修改最终落到基表上;基表变了,查询视图的结果自然也变了。 但不是所有视图都能修改,见 1.5 节。
1.4 视图的优点与用途
| 优点 | 说明 |
|---|---|
| 简化复杂查询 | 把多表连接、子查询等复杂 SQL 封装成视图,使用时只需 SELECT * FROM 视图名 |
| 权限控制 | 只给用户开放视图的查询权限,不开放基表,从而隐藏敏感字段(比如工资、身份证号) |
| 数据独立性 | 基表结构变化时,可以通过修改视图定义保持上层应用不变,实现逻辑数据独立性 |
| 逻辑清晰 | 给复杂查询起一个有意义的名字,提升 SQL 可读性和可维护性 |
1.5 可更新视图与不可更新视图
不是所有视图都能执行 INSERT/UPDATE/DELETE。
可更新视图必须同时满足
- 没有聚合函数(
SUM、COUNT、MAX、MIN、AVG等) - 没有
DISTINCT - 没有
GROUP BY/HAVING - 没有
UNION/UNION ALL - SELECT 列表和 WHERE 子句中没有子查询
- 基于单表(多表连接的视图一般不可更新)
- 没有引用其他不可更新的视图
示例
-- 可更新视图(基于单表,无聚合)
CREATE VIEW v_emp10 AS
SELECT empno, ename, sal FROM EMP WHERE deptno = 10;
-- 可以修改(本质修改基表)
UPDATE v_emp10 SET sal = sal + 100 WHERE empno = 7782;
-- 不可更新视图(有聚合 + GROUP BY)
CREATE VIEW v_dept_avg AS
SELECT deptno, AVG(sal) AS avg_sal FROM EMP GROUP BY deptno;
-- 对 v_dept_avg 执行 UPDATE 会报错
1.6 视图的其他操作
-- 查看所有视图(视图也是表,show tables 能看到)
SHOW TABLES;
-- 查看视图定义
SHOW CREATE VIEW 视图名;
-- 修改视图定义
CREATE OR REPLACE VIEW 视图名 AS 新的SELECT语句;
-- 或
ALTER VIEW 视图名 AS 新的SELECT语句;
-- 删除视图
DROP VIEW 视图名;
-- 同时删除多个视图
DROP VIEW 视图1, 视图2;
二、用户管理
2.1 用户数据存储在哪里
MySQL 是网络数据库服务,登录需要账号密码。用户信息本身也是数据,存储在 MySQL 自带的系统库 mysql 中。
-- 切换到 mysql 系统库
USE mysql;
-- 查看所有表
SHOW TABLES;
其中最核心的是 user 表,存储所有用户的账号、密码、全局权限等信息。
-- 查看表结构
DESC user;
-- 查看所有用户(垂直显示更清晰)
SELECT * FROM user \G;
2.2 MySQL 用户的唯一标识:'用户名'@'主机'
MySQL 的用户不是只由用户名唯一标识 ,而是由 '用户名'@'主机名' 共同唯一标识。
'root'@'localhost' 和 'root'@'%' 是两个完全不同的用户!
它们可以有不同的密码、不同的权限。登录时,MySQL 根据你连接的来源 IP,在 user 表中找最精确匹配的那一条记录(host 越具体优先级越高),而不是模糊匹配。
Host 字段的常见形式
| Host 值 | 含义 |
|---|---|
localhost |
仅允许本地连接(Unix socket 或本地回环 TCP) |
192.168.1.100 |
仅允许指定的单个 IP 连接 |
192.168.1.% |
允许 192.168.1 网段的所有 IP 连接 |
%.example.com |
允许指定域名后缀的主机连接 |
% |
允许任意 IP 连接(最宽松) |
关于内网/公网登录的正确理解
原文关于内网 IP 的解释有错误,这里修正:
- 如果客户端和 MySQL 服务器在同一个内网 ,客户端连接时 MySQL 看到的来源 IP 就是客户端的内网 IP 本身 (不经过公网和 NAT)。此时可以把 host 精确设为内网 IP 或网段(如
'192.168.1.%'),不需要设为%。 - 如果客户端跨公网连接 MySQL 服务器,请求经过路由器 NAT 后,MySQL 看到的来源 IP 是公网 IP 。此时需要把 host 设为对应的公网 IP,或者设为
%(任意 IP)。 %的含义是"任意 IP 都能连接",和内网/公网无关。设为%意味着全网可达,生产环境慎用,应尽量精确限制来源 IP。
2.3 user 表核心字段解读
user 表有几十个字段,重点关注以下几类:
| 字段 | 含义 |
|---|---|
Host |
允许登录的主机/IP(见 2.2) |
User |
账号名 |
authentication_string |
密码的哈希摘要(不是明文密码) |
plugin |
认证插件。MySQL 8.0 默认 caching_sha2_password,5.7 默认 mysql_native_password |
Select_priv、Insert_priv、Update_priv、Delete_priv |
全局级别的增删改查权限,Y 表示拥有 |
Create_priv、Drop_priv、Alter_priv、Grant_priv |
全局级别的 DDL 和授权权限 |
Super_priv |
超级权限(kill 线程、修改全局变量等) |
password_expired |
密码是否已过期(Y/N) |
password_last_changed |
密码最后修改时间 |
password_lifetime |
密码有效期(天) |
account_locked |
账户是否被锁定(Y 表示锁定,无法登录) |
其余字段大多是各类全局权限位(
Y/N)、SSL 连接配置、资源限制(每小时最大查询数/连接数等)。
2.4 创建用户
语法
CREATE USER '用户名'@'主机' IDENTIFIED BY '密码';
示例
-- 创建一个只能本地登录的用户
CREATE USER 'zhangsan'@'localhost' IDENTIFIED BY '123456';
-- 创建一个允许任意 IP 登录的用户
CREATE USER 'lisi'@'%' IDENTIFIED BY '123456';
-- 创建一个允许指定网段登录的用户
CREATE USER 'wangwu'@'192.168.1.%' IDENTIFIED BY '123456';
注意:MySQL 8.0 中,
CREATE USER创建的用户默认没有任何权限 ,只能登录,连库都看不到。需要额外用GRANT授权。
2.5 删除用户
语法
DROP USER '用户名'@'主机';
示例
DROP USER 'zhangsan'@'localhost';
注意:必须写完整的
'用户名'@'主机',因为用户是由两者共同标识的。
2.6 修改密码
MySQL 8.0 推荐写法(标准)
-- 修改指定用户密码
ALTER USER '用户名'@'主机' IDENTIFIED BY '新密码';
-- 修改当前登录用户自己的密码
ALTER USER CURRENT_USER() IDENTIFIED BY '新密码';
也可以用 SET PASSWORD
SET PASSWORD FOR '用户名'@'主机' = '新密码';
已经废弃的写法
-- ❌ MySQL 8.0 中已废弃/移除,会报错
SET PASSWORD FOR '用户名'@'主机' = PASSWORD('新密码');
PASSWORD() 函数在 MySQL 8.0 中已被移除,8.0 中直接写明文密码即可,MySQL 会自动用认证插件哈希后存储。
2.7 权限管理
2.7.1 权限的四个层级
MySQL 权限从粗到细分为四个层级:
| 层级 | 语法范围 | 作用范围 |
|---|---|---|
| 全局权限 | *.* |
所有库的所有表 |
| 数据库权限 | 库名.* |
指定库的所有表 |
| 表权限 | 库名.表名 |
指定库的指定表 |
| 列权限 | 库名.表名(列1,列2) |
指定表的指定列 |
2.7.2 常用权限列表
| 权限 | 作用 |
|---|---|
ALL PRIVILEGES / ALL |
除 GRANT OPTION 和 PROXY 外的所有权限 |
SELECT |
查询数据 |
INSERT |
插入数据 |
UPDATE |
更新数据 |
DELETE |
删除数据 |
CREATE |
创建数据库/表 |
DROP |
删除数据库/表 |
ALTER |
修改表结构 |
INDEX |
创建/删除索引 |
CREATE VIEW |
创建视图 |
SHOW VIEW |
查看视图定义 |
CREATE ROUTINE |
创建存储过程/函数 |
ALTER ROUTINE |
修改/删除存储过程/函数 |
EXECUTE |
执行存储过程/函数 |
CREATE USER |
创建/删除/重命名用户 |
CREATE TABLESPACE |
创建/删除/修改表空间 |
SHOW DATABASES |
查看所有数据库 |
SUPER |
超级权限(kill 线程、修改全局变量、关闭服务器等) |
GRANT OPTION |
将自己拥有的权限再授予其他用户 |
PROCESS |
查看所有会话线程(SHOW PROCESSLIST) |
RELOAD |
执行 FLUSH 操作 |
SHUTDOWN |
关闭 MySQL 服务器 |
FILE |
服务器主机文件读写(LOAD DATA INFILE 等) |
LOCK TABLES |
显式锁表 |
CREATE TEMPORARY TABLES |
创建临时表 |
REPLICATION SLAVE |
从库复制权限 |
REPLICATION CLIENT |
查询主从状态(SHOW MASTER/SLAVE STATUS) |
EVENT |
创建/删除/修改事件调度器 |
TRIGGER |
创建/删除触发器 |
REFERENCES |
创建外键约束 |
2.7.3 授权语法(GRANT)
GRANT 权限列表 ON 库.对象 TO '用户名'@'主机';
示例
2.7.4 回收权限(REVOKE)
REVOKE 权限列表 ON 库.对象 FROM '用户名'@'主机';
示例
-- 回收指定库的删除权限
REVOKE DELETE ON company.* FROM 'zhangsan'@'localhost';
-- 回收所有权限(不含 GRANT OPTION)
REVOKE ALL PRIVILEGES ON *.* FROM 'lisi'@'%';
-- 回收 GRANT OPTION 权限
REVOKE GRANT OPTION ON *.* FROM 'wangwu'@'%';
2.7.5 查看用户权限
-- 查看指定用户的权限
SHOW GRANTS FOR '用户名'@'主机';
-- 查看当前登录用户自己的权限
SHOW GRANTS FOR CURRENT_USER();
2.8 FLUSH PRIVILEGES 什么时候用
FLUSH PRIVILEGES 的作用是重新加载 mysql 库中的权限表到内存。
| 操作方式 | 是否需要 FLUSH PRIVILEGES |
|---|---|
用 CREATE USER / DROP USER / GRANT / REVOKE / ALTER USER 等标准语句管理用户 |
❌ 不需要,MySQL 自动刷新权限缓存 |
直接用 INSERT / UPDATE / DELETE 手动修改 mysql.user 等权限表 |
✅ 必须执行 FLUSH PRIVILEGES,否则修改不生效 |
生产环境推荐始终使用标准的用户管理语句(CREATE USER / GRANT 等),不要直接修改 mysql.user 表,避免权限不一致。
2.9 扩展:角色(Role)------ MySQL 8.0+
MySQL 8.0 引入了角色(Role)机制,可以把一组权限打包成一个角色,再把角色授予用户,简化权限管理。
-- 创建角色
CREATE ROLE 'app_developer', 'app_reader';
-- 给角色授权
GRANT ALL PRIVILEGES ON app_db.* TO 'app_developer';
GRANT SELECT ON app_db.* TO 'app_reader';
-- 把角色授予用户
GRANT 'app_developer' TO 'zhangsan'@'localhost';
-- 用户启用角色(会话级)
SET ROLE 'app_developer';
-- 查看当前启用的角色
SELECT CURRENT_ROLE();