mysql不支持单条grant语句批量授权多个用户,必须逐条执行或借助shell循环、存储过程(慎用)或角色机制实现;需确保用户已存在、主机段精确匹配,并验证权限实际生效。

MySQL不支持在单条SQL里批量GRANT多个用户
直接写 GRANT SELECT ON db.* TO 'u1'@'%', 'u2'@'%' 会报错:ERROR 1064 (42000)。MySQL的 GRANT 语句语法只允许一次指定一个用户(或配合角色),没有原生批量授权语法。想“循环执行”,本质得靠客户端逻辑或存储过程模拟,而不是纯SQL脚本本身能完成的。
用mysql命令行 + shell for循环最简单可靠
适合运维或DBA在终端快速操作,无需改MySQL配置或写存储过程。前提是用户账号已存在,且当前登录账户有 GRANT OPTION 权限。
示例:给 user_list.txt 中每行一个用户名(如 app_rw@'10.%.%.%')统一授予 SELECT, INSERT, UPDATE:
while read u; do mysql -u root -p'your_pass' -e "GRANT SELECT,INSERT,UPDATE ON mydb.* TO $u;" done <p>注意点:</p>
-
$u必须是完整格式,含主机段,比如'api@192.168.1.%';单引号要保留在文件里或由shell转义 - 避免密码明文写在命令中,建议用
~/.my.cnf配置认证 - 执行完别忘
FLUSH PRIVILEGES;—— 实际上GRANT自动触发,不用手动刷
用存储过程模拟循环(仅限5.7+,慎用)
如果必须在MySQL服务端内完成,且不能连外部shell,可建临时存储过程。但要注意:PREPARE + EXECUTE 无法直接参数化用户名(因为用户名/主机是标识符,不是值),必须拼接SQL字符串,有SQL注入风险,且调试困难。
安全做法是限定白名单,例如:
DELIMITER $$
CREATE PROCEDURE batch_grant()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE u_host VARCHAR(100);
DECLARE cur CURSOR FOR
SELECT "'app1'@'10.0.0.%'" UNION ALL
SELECT "'app2'@'10.0.0.%'" UNION ALL
SELECT "'app3'@'10.0.0.%'";
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
<p>OPEN cur;
read_loop: LOOP
FETCH cur INTO u_host;
IF done THEN LEAVE read_loop; END IF;
SET @sql = CONCAT('GRANT SELECT,UPDATE ON mydb.* TO ', u_host);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
END$$
DELIMITER ;
CALL batch_grant();</p>
关键限制:
- 用户名+主机必须硬编码在
SELECT列表里,不能从普通表读(否则需动态拼接更多层,更易出错) - MySQL 8.0+ 的角色(
ROLE)更适合替代这种循环——先CREATE ROLE,再GRANT权限给角色,最后GRANT role_name TO user,批量只需一条语句
权限变更后务必验证实际效果
很多人执行完 GRANT 就以为完成了,但常忽略两点:
- 用户当前连接不会自动继承新权限,需断开重连(或执行
REVOKE再GRANT强制刷新会话缓存) - 权限层级叠加:如果用户同时有
SELECT在库级和表级,又REVOKE了库级,表级仍生效——查INFORMATION_SCHEMA.ROLE_TABLE_GRANTS或用SHOW GRANTS FOR 'u'@'h'确认最终结果 - 主机匹配优先级:MySQL按
'user'@'192.168.1.%'、'user'@'192.168.%'、'user'@'%'顺序匹配,越具体越优先,批量时务必统一主机模板
真正麻烦的从来不是怎么写循环,而是权限叠加规则、主机通配符行为、以及旧连接是否感知变更——这些细节比语法本身更容易导致线上故障。











