MySQL位运算查询核心是用&做掩码检测,如查同时有读写权限需WHERE (roles & 3) = 3;注意优先级、索引无效、类型溢出及维护语义一致性。

WHERE 条件里直接用 & 判断权限位是否开启
MySQL 的位运算查询核心就一句话:用 &(按位与)做“掩码检测”。比如字段 roles 存的是整数,每个二进制位代表一个权限(bit 0 = 读,bit 1 = 写,bit 2 = 删除),要查“同时有读和写权限”的用户,就得确保 bit 0 和 bit 1 都是 1,对应掩码就是 3(二进制 11)。
正确写法是:SELECT * FROM users WHERE (roles & 3) = 3。注意括号不能省——& 优先级低于 =,不加括号会先算 roles & (3 = 3),结果恒为 roles & 1,逻辑全错。
- 查“有读权限(不管其他)” →
(roles & 1) = 1 - 查“没有写权限” →
(roles & 2) = 0 - 查“有读或删除权限(至少一个)” →
(roles & 5) != 0(因为5 = 101b,覆盖 bit 0 和 bit 2)
BIT_AND/BIT_OR 等聚合函数只在 GROUP BY 场景下有意义
BIT_AND()、BIT_OR()、BIT_XOR() 不是用于单行过滤的,而是对分组后的多行值做**逐位聚合**。比如统计某类商品所有 SKU 的状态组合:若每个 SKU 的 flags 字段表示“是否上架|是否有库存|是否打折”,那么 BIT_AND(flags) 就是该品类下“所有 SKU 都具备”的状态位,BIT_OR(flags) 是“任一 SKU 具备”的状态位。
常见误用是把它当过滤条件写在 WHERE 里,比如 WHERE BIT_AND(status) = 1 —— 这语法非法,MySQL 会报错 Invalid use of group function。必须配合 GROUP BY 才能用。
- 想看每类 cate 的“共性状态” →
SELECT cate, BIT_AND(flags) AS common_flags FROM products GROUP BY cate -
BIT_COUNT()可单独用,比如查某用户开启了几个权限:SELECT name, BIT_COUNT(roles) FROM users
索引对 & 查询基本无效,别指望走索引加速
MySQL 无法对 column & N = M 这类表达式使用普通 B+Tree 索引。即使你在 roles 字段建了索引,执行 WHERE (roles & 4) = 4 时,优化器大概率还是全表扫描。
真要高频查某一位,有两个现实选择:
- 拆出单独的布尔列,比如加一列
is_admin TINYINT(1),并建索引,然后查WHERE is_admin = 1 - 用生成列(Generated Column)+ 索引:
ALTER TABLE users ADD COLUMN is_editor TINYINT AS ((roles & 2) = 2) STORED,再给is_editor建索引
硬扛位运算 + 大表 + 高并发?性能瓶颈会来得比预想快得多。
注意整数类型长度和符号位带来的隐式转换问题
MySQL 对无符号整数(INT UNSIGNED)和有符号整数(INT)的位运算行为一致,但 ~(取反)操作在有符号字段上容易翻车。例如 ~1 在 64 位环境下结果是 18446744073709551614,不是你直觉的 -2 —— 因为 MySQL 默认按 8 字节无符号处理常量。
更隐蔽的问题是字段类型太小:如果用 TINYINT 存权限,最多只能塞 8 个 flag;一旦业务扩展到 10 个权限,TINYINT 溢出后高位截断,& 结果就不可信了。
- 建议统一用
INT UNSIGNED或BIGINT UNSIGNED,留足位扩展空间 - 避免在 WHERE 中混用
~和&,比如WHERE (flags & ~4) = 0,可读性差且易错,不如显式写成WHERE (flags & 4) = 0
位运算本身不难,难的是把“位”当成结构化语义来维护。一旦哪天有人往 roles 里直接写了个 100 而不是用 | 组合,整个状态体系就 quietly 崩溃了——这种错误不会报错,只会让查询结果越来越诡异。


















