BIT_OR和BIT_AND是聚合函数,必须配合GROUP BY使用,因其语义是对一组行进行位级归约;单行权限判断应使用&运算符配合括号和等号,如WHERE (perm & 4) = 4。

BIT_OR 和 BIT_AND 只能在分组后使用,不能直接查单个用户有没有权限;真要判断权限,得用 & 运算符配合括号和等号。
为什么 BIT_OR / BIT_AND 必须配 GROUP BY?
这两个函数是聚合函数,语义上就是“对一组行做位级归约”,不是单行运算符。MySQL 会直接报错:Invalid use of group function,如果你写成 WHERE BIT_OR(perm_flag) = 4 或 SELECT BIT_AND(perm_flag) 却没写 GROUP BY。
-
BIT_OR返回某一位为 1 ⇔ 该组中**至少一行**在该位为 1 -
BIT_AND返回某一位为 1 ⇔ 该组中**每一行**在该位都为 1 - NULL 值会让整组结果变成
NULL,必须用COALESCE(perm_flag, 0)拦住 - 字段类型必须是整型(
TINYINT、INT),字符串如'4'会被静默转成十进制 4,但'0b100'或'0x4'不行
怎么写一个安全的 BIT_OR 分组查询?
典型场景:查每个角色拥有的全部权限掩码。假设权限值是 2 的幂(1=读、2=写、4=删),且表结构为 role_id + perm_flag。
- 基础写法:
SELECT role_id, BIT_OR(perm_flag) AS mask FROM role_perms GROUP BY role_id - 加 NULL 防御:
BIT_OR(COALESCE(perm_flag, 0)),避免整组失效 - 别漏关键分组维度——比如多租户系统里,只按
role_id分组却忽略tenant_id,会导致权限跨租户污染 - 如果原始数据有重复行(比如同一
role_id+perm_flag出现多次),BIT_OR结果不变,但逻辑冗余;要去重得先SELECT DISTINCT role_id, perm_flag再聚合
BIT_AND 常见误用和正确 HAVING 写法
BIT_AND 不是“判断是否全员开启”,而是严格按位交集。比如查“每个部门中所有员工都具备的权限位”:
- 错误:
HAVING BIT_AND(perm_mask) & 4 > 0—— 只要结果非零就过,可能是无关位碰巧为 1 - 正确:
HAVING (BIT_AND(COALESCE(perm_mask, 0)) & 4) = 4,确保 bit 2 全员置位 - 别在
WHERE里用它:单行权限检查永远用(perm_mask & 4) = 4,不是聚合函数的事 - 性能注意:
BIT_AND无法走索引,大数据量分组时比普通AVG更耗 CPU,因为要逐位计算
权限校验时 & 运算符的括号和优先级陷阱
这是线上最常出错的地方:& 优先级低于 =,不加括号就等于自毁逻辑。
- 错:
WHERE permissions & 4 = 4→ 实际解析为permissions & (4 = 4)→permissions & 1,全乱了 - 对:
WHERE (permissions & 4) = 4,明确先算位与,再比等值 - 同时有读(1)和写(2):
(permissions & 3) = 3(因为1 | 2 = 3),不是& 1 AND & 2 - 字段为
NULL时,(NULL & 4) = 4结果是NULL,整行不命中——符合“未知即无权”的语义,但得意识到这不是默认拒绝,而是三值逻辑
真正难的是把位运算和索引结合:WHERE 条件里的 & 基本不走索引,大表务必加生成列(如 has_delete TINYINT AS ((permissions & 4) = 4) STORED)再建索引。否则一查百万行,只能全表扫。

















