BIT_OR是MySQL位运算聚合函数,对分组内非NULL整数权限掩码逐位或运算,某位为1当且仅当该组至少一行在该位为1;须配合GROUP BY使用,权限值应为2的幂,校验需用&运算符。

BIT_OR 是什么,为什么能算权限
BIT_OR 是 MySQL 提供的位运算聚合函数,对一组整数执行按位 OR 运算,结果是所有值在二进制位上“有 1 就置 1”。权限系统常用单个 bit 表示一个功能开关(比如第 0 位表示「查看」、第 1 位表示「编辑」),多个权限就用一个整数存储。BIT_OR 正好能把用户拥有的所有角色权限合并成一个总权限值。
注意:PostgreSQL 和 SQL Server 没有 BIT_OR;SQLite 有但叫 BITOR(非聚合版),需配合 group_concat + 自定义函数模拟;只有 MySQL 原生支持聚合版 BIT_OR。
建表和数据怎么组织才合理
权限位必须预先约定且不重叠,推荐用 2 的幂次作为权限值(1、2、4、8…),避免进位干扰。常见错误是直接用递增 ID(如 1/2/3)当权限码,导致 BIT_OR(1,2,3) == 3,无法区分「只有权限 3」和「权限 1+2」。
- 角色表
roles:含id、name - 权限表
permissions:含id、code(如'view')、bit_value(如1、2、4) - 关联表
role_permissions:含role_id、perm_id - 用户角色表
user_roles:含user_id、role_id
查某个用户的总权限位,SQL 类似:
SELECT BIT_OR(p.bit_value) AS total_perm FROM users u JOIN user_roles ur ON u.id = ur.user_id JOIN role_permissions rp ON ur.role_id = rp.role_id JOIN permissions p ON rp.perm_id = p.id WHERE u.id = 123;
BIT_OR 遇到 NULL 或空组会怎样
BIT_OR 对空结果集返回 NULL,不是 0。如果用户没分配任何角色,查询结果就是 NULL,后续用这个值做位判断(如 total_perm & 4)会得到 NULL,而不是预期的 0(即「无权限」)。这是最常踩的坑。
安全写法是用 COALESCE 默认补 0:
SELECT COALESCE(BIT_OR(p.bit_value), 0) AS total_perm FROM ...
另外,BIT_OR 会自动跳过列值为 NULL 的行,但不会报错——所以要确保 bit_value 字段设了 NOT NULL 约束,否则漏掉权限可能无声无息。
权限校验时别直接在 SQL 里写死位运算
应用层拿到 total_perm 后,校验「是否有编辑权限(bit 1,即值为 2)」应写作 (total_perm & 2) != 0,而不是 total_perm >= 2 或 total_perm % 4 >= 2 —— 后两者在多权限组合下必然出错。
如果必须在 SQL 中批量过滤有某权限的用户,可以这样写:
SELECT u.id, u.name FROM users u JOIN user_roles ur ON u.id = ur.user_id JOIN role_permissions rp ON ur.role_id = rp.role_id JOIN permissions p ON rp.perm_id = p.id GROUP BY u.id, u.name HAVING COALESCE(BIT_OR(p.bit_value), 0) & 4 != 0;
这里 & 4 表示检查第 2 位(从 0 开始计),对应「删除」权限。注意 HAVING 必须放在 GROUP BY 之后,且不能用别名 total_perm —— MySQL 不允许在 HAVING 中引用 SELECT 别名。
位运算本身很快,但若权限位超过 64 个(超出 BIGINT 范围),MySQL 会静默截断,建议权限总数控制在 60 以内,或改用 JSON 数组 + 应用层合并。

















