mysql 8.0.16起才真正支持列级授权,5.7及更早版本仅语法兼容但无效;必须用grant ... on db.tbl(col)显式指定列名,不支持通配符,且权限检查以最严格为准。

MySQL 8.0+ 才支持真正的列级授权
MySQL 5.7 及更早版本不支持对单列执行 GRANT;所谓“列权限”只是语法上允许写,实际授权后仍无法生效——连接后执行对应列的 SELECT 会直接报 ERROR 1142 (42000): SELECT command denied。只有 MySQL 8.0.16 起才真正实现列级权限检查,且必须用 GRANT ... ON database.table(column_name) 显式指定列名。
常见错误现象:GRANT SELECT (name) ON test.users TO 'u1'@'%'; 在 5.7 上能执行成功,但 u1 登录后 SELECT name FROM test.users 依然被拒绝。
- 确认版本:运行
SELECT VERSION();,低于8.0.16的别折腾列授权 - 列名必须明确列出,不支持通配符(如
(*)或(name, *)) - 同一用户对同一张表可混合授予不同列的权限,例如:先授
SELECT (id, name),再授UPDATE (email)
GRANT 语句里列名写错位置就完全失效
列权限只能在 ON 子句中、紧跟表名后用括号声明,不能放在 GRANT 动词后面,也不能塞进引号或变量里。写成 GRANT SELECT(name) ON test.users 是合法的;但 GRANT SELECT ON test.users(name) 或 GRANT SELECT (name) ON test.`users`(反引号包表名但漏了列括号)都会被 MySQL 当作语法错误或降级为表级授权。
- 正确格式:
GRANT SELECT (id, email) ON mydb.t1 TO 'u2'@'localhost'; - 错误示例:
GRANT SELECT ON mydb.t1(id)→ 报错ERROR 1064 (42000) - 错误示例:
GRANT SELECT ('name') ON mydb.t1→ 单引号导致列名被当字符串字面量,权限不生效 - 注意大小写:列名区分大小写(取决于
lower_case_table_names设置,但列名本身按定义时的大小写匹配)
列权限和表权限叠加时,以“最严格”为准
如果一个用户既被授予了整张表的 SELECT 权限,又被单独授予某几列的 SELECT 权限,MySQL 不会合并——它只认“最小交集”。比如:用户有 SELECT ON test.t1(全表),又额外被收回 SELECT (ssn) ON test.t1,那 ssn 列仍然不可查;反过来,如果只给了 SELECT (name),没给表级权限,那哪怕 SELECT * 里包含 name,只要语句里出现其他列,整个查询就失败。
-
SELECT name FROM t1✅(只查被授权列) -
SELECT name, email FROM t1❌(email未授权,整条语句拒绝) -
SELECT * FROM t1❌(即使其中一列被授权,*展开后含未授权列) - 撤销列权限用
REVOKE SELECT (ssn) ON db.tbl FROM 'u'@'%';,不是DROP或DELETE
SHOW GRANTS 看不到列权限细节,得查系统表
SHOW GRANTS FOR 'u'@'%' 默认只显示表级权限,哪怕你确实授过列权限,输出里也不会出现括号列名——容易误以为没生效。真实授权信息存在 mysql.columns_priv 表里,需手动查:
SELECT User, Host, Db, Table_name, Column_name, Select_priv FROM mysql.columns_priv WHERE User = 'u1' AND Db = 'test' AND Table_name = 'users';
注意:columns_priv 表权限是行级缓存的,修改后要执行 FLUSH PRIVILEGES; 才能立即生效(虽然多数情况下自动重载,但遇到权限不生效,先刷一下)。
容易被忽略的是:列权限不会出现在 INFORMATION_SCHEMA.ROLE_TABLE_GRANTS 中,也不受角色(ROLE)的间接继承影响——MySQL 目前不支持把列权限赋予 ROLE 再分配给用户。











