MySQL 8.0.16+才真正支持列级权限控制;低于该版本的GRANT SELECT(col)语法虽可执行但无效,且仅SELECT、INSERT、UPDATE、REFERENCES四类操作支持列级授权,须显式列出列名、位置严格(权限后、ON前),大小写敏感,不支持通配符。

MySQL 8.0.16+ 才真正支持列级权限控制,低于该版本的 GRANT SELECT (col) 语法虽能执行,但不生效;别在 5.7 或早期 8.0 上白费力气。
确认你用的是真正支持列权限的 MySQL 版本
列级权限不是“写了就管用”,它从 MySQL 8.0.16 开始才进入生产可用状态。此前版本(包括 8.0.0–8.0.15)仅解析语法、不执行校验,用户仍会被拒绝访问——哪怕 SHOW GRANTS 看起来正常。
- 运行
SELECT VERSION();,输出必须是8.0.16或更高(如8.0.33、9.6.0) - 若用的是 MySQL 9.6.0(2026 年新版本),列权限已完全兼容且更稳定,但语法和行为与 8.0.16+ 一致
- 不要依赖客户端或管理工具的“版本显示”,直接连上执行 SQL 查证
GRANT 语句中列名的位置和写法必须严格正确
列名括号必须紧跟在权限类型之后、ON 之前,任何错位都会导致降级为表级授权,或直接报错。
- ✅ 正确:
GRANT SELECT (id, name) ON mydb.users TO 'u'@'localhost'; - ❌ 错误:
GRANT SELECT ON mydb.users (id, name) TO 'u'@'localhost';→ 报错ERROR 1064 (42000) - ❌ 错误:
GRANT SELECT ('id', 'name') ON mydb.users TO 'u'@'localhost';→ 单引号让 MySQL 当成字符串字面量,权限无效 - 列名区分大小写,必须与
SHOW COLUMNS FROM mydb.users输出完全一致(受lower_case_table_names影响的是表名,列名按定义时大小写匹配)
只允许这四种操作做列级授权:SELECT、INSERT、UPDATE、REFERENCES
DELETE、DROP、ALTER、CREATE 等权限天然作用于整行或整表,MySQL 不提供列级变体。试图写 GRANT DELETE (id) ON ... 会直接语法报错。
-
SELECT (a,b):查询语句中所有涉及的列(SELECT列表、WHERE、ORDER BY、GROUP BY)都必须在授权范围内,否则整条语句被拒 -
INSERT (x,y):只允许显式指定这些列插入;INSERT INTO t VALUES (...)全列写法会失败,即使未授权列有默认值 -
UPDATE (z):只允许SET z = ...;哪怕你有表级UPDATE权限,只要SET出现未授权列,立刻报ERROR 1142 -
REFERENCES (c)极少使用,仅用于外键定义时的权限检查,不影响日常 DML
列权限不叠加、不继承,查 mysql.columns_priv 才算数
SHOW GRANTS FOR 'u'@'h' 可能显示列权限,但它只反映 GRANT 记录;真实生效权限由 mysql.columns_priv 表驱动,且与表级权限是“并集”关系——但逻辑上以最严为准。
- 撤销表级
SELECT不会清除已授的列级SELECT (col),它们是独立存储的 - 要彻底清理,必须显式执行:
REVOKE SELECT (col1, col2) ON db.t FROM 'u'@'h'; - 检查实际列权限:
SELECT * FROM mysql.columns_priv WHERE User='u' AND Host='h' AND Db='db' AND Table_name='t'; - 列权限修改后无需
FLUSH PRIVILEGES(GRANT自动刷新),除非你手改了系统表
最易被忽略的一点:列权限对视图、存储过程、触发器中的字段访问同样生效,但这些对象的定义者权限可能绕过检查——所以生产环境若用视图做列隔离,必须确保调用者无基表权限,且视图字段显式列出、不含敏感列。


















