MySQL 8.0.16+才真正支持列级授权,此前版本仅语法兼容但无效;必须用GRANT SELECT(col) ON db.tbl显式指定列名,不支持通配符,且WHERE/ORDER BY中涉及的列也须授权。

MySQL 8.0.16+ 才真正支持列级授权
低于这个版本(比如 MySQL 5.7 或 8.0.15 及更早)执行 GRANT SELECT (name) ON db.tbl TO 'u'@'%' 不报错,但权限完全不生效——用户登录后查该列仍会报 ERROR 1142 (42000): SELECT command denied。必须先确认版本:SELECT VERSION();。如果结果是 8.0.15 或更低,别在列授权上浪费时间,它只是语法兼容,没实际检查逻辑。
列级授权只支持 SELECT/INSERT/UPDATE/REFERENCES 四种操作
想对某列限制 DELETE 或 ALTER?不行。MySQL 明确不支持,这类语句写成 GRANT DELETE (col) ON db.t 会直接报 ERROR 1064 (42000)。能带列名的只有:
-
SELECT (id, name):查询时只能选这些列,WHERE、ORDER BY、GROUP BY里出现未授权列也会失败 -
INSERT (name, email):必须显式列出列名,INSERT INTO t VALUES (...)全字段插入会被拒绝 -
UPDATE (status):SET子句里只要出现未授权列(如SET status='a', phone='b'),整条语句就失败 -
REFERENCES (id):极少见,用于外键定义时的校验
列名必须紧贴权限关键字后、ON 前,且不能加引号
常见写错方式:
-
GRANT SELECT ON db.t(id)→ 语法错误,ERROR 1064 -
GRANT SELECT ('name') ON db.t→ 单引号让 MySQL 当成字符串字面量,权限不生效 -
GRANT SELECT (name, *) ON db.t→ 通配符*在列列表中无效,必须逐个写死 -
GRANT SELECT (Name) ON db.t→ 列名大小写敏感,按建表时定义的大小写匹配(受lower_case_table_names影响但不改变列名本身)
正确写法只有一种:GRANT SELECT (id, email) ON mydb.users TO 'report'@'localhost';
列权限和表权限不是叠加,而是“最严格优先”
用户既有 SELECT ON mydb.users(全表),又被单独 REVOKE SELECT (ssn) ON mydb.users,那 ssn 还是查不了;反过来,如果只授了 SELECT (name),没授表级 SELECT,那 SELECT name FROM users 能过,但 SELECT name, email FROM users 直接被拒——哪怕 email 是空值或默认值也不行。
撤回权限也得对口:REVOKE SELECT (ssn) ON mydb.users FROM 'u'@'%';,而不是 REVOKE SELECT ON mydb.users,后者不会动列级授权。
真正容易被忽略的是:WHERE 条件里用到的列,哪怕不在 SELECT 列表中,也必须被授权。比如只有 SELECT (name),却执行 SELECT name FROM t WHERE id = 1,照样报错——id 在 WHERE 里被访问了,但它没在列授权列表中。


















