mysql列级别权限仅支持select、insert、update、references四种操作,delete等语句级权限不支持列级细化;列权限需显式指定字段,且不继承、不叠加,show grants中独立显示。

MySQL列级别权限只支持SELECT、INSERT、UPDATE、REFERENCES四种操作
MySQL确实允许在GRANT语句中指定具体字段名,但不是所有权限类型都支持——DELETE、DROP、ALTER等语句级权限无法细化到列。如果你看到GRANT DELETE ON db.tbl(col1) TO 'u'@'h'这种写法,它会直接报错:ERROR 1064 (42000): You have an error in your SQL syntax。
真正能带列名的只有这四类:
-
SELECT (col1, col2):控制哪些列可被查询 -
INSERT (col1, col3):限制INSERT时允许显式赋值的字段(注意:不包含隐式默认值或自增列) -
UPDATE (col2, col4):仅允许UPDATE语句修改这些列(SET子句中出现其他列会报错) -
REFERENCES (col5):用于外键定义时的权限校验(极少用)
INSERT列权限的实际行为容易误解
给用户授予INSERT (name, email)权限,并不意味着他不能插入整行;而是要求他**必须显式列出被授权的列**,且不能包含未授权列。下面这些操作结果不同:
INSERT INTO users (name, email) VALUES ('A', 'a@b.c'); -- ✅ 允许
INSERT INTO users VALUES ('A', 'a@b.c', NOW()); -- ❌ 报错:Column 'created_at' has no permission
INSERT INTO users (name, email, created_at) VALUES ('A', 'a@b.c', NOW()); -- ❌ 即使指定了未授权列,也拒绝
另外要注意:INSERT ... SELECT语句不受列级INSERT权限约束,只检查SELECT部分的列权限。
UPDATE列权限会拦截SET子句中的非法列
哪怕你有表级UPDATE权限,只要开了列级限制,UPDATE语句里SET了未授权列,就会立刻失败。例如:
GRANT UPDATE (status) ON app.orders TO 'worker'@'%';
那么以下语句都会被拒绝:
UPDATE orders SET status = 'shipped', updated_at = NOW() WHERE id = 123; -- ❌ updated_at未授权
UPDATE orders SET total = total * 1.1 WHERE id = 123; -- ❌ total未授权
但这个可以执行:
UPDATE orders SET status = 'canceled' WHERE id = 123; -- ✅ 只改了status
注意:WHERE条件里的列不受列级UPDATE权限影响,哪怕id没被授权也能用。
列权限不会自动继承,且SHOW GRANTS不直观显示
MySQL不会把列权限和表权限合并展示。SHOW GRANTS FOR 'u'@'h'返回的结果里,列权限是独立一行、带括号字段列表的,比如:
GRANT SELECT (id, name) ON `app`.`users` TO 'reporter'@'%';
这意味着你得逐行检查输出,不能只看“SELECT ON app.users”就认为能查全表。更麻烦的是:列权限不继承——即使你给user@'%'授了SELECT (a,b),再创建user@'192.168.%',后者默认没有任何列权限,必须重新GRANT。
还有一个隐藏坑:如果某用户同时有表级SELECT和列级SELECT (x,y),MySQL以更严格的为准,即他只能查x,y两列——表级权限会被列级覆盖,而不是叠加。











