BIT_AND和BIT_OR是分组聚合函数,非逐行运算;需配合GROUP BY使用,输入转为64位无符号整数,NULL使BIT_AND结果为NULL,BIT_OR更常用于权限合并,而&、|才是逐行按位运算符。

BIT_AND 和 BIT_OR 是聚合函数,不是逐行计算的标量函数
很多人第一次用 BIT_AND 或 BIT_OR 时,会误以为它像 AND、OR 那样对两列做逐行按位运算。其实不是——它们是**分组聚合函数**,行为类似 SUM 或 COUNT:对每组内的所有值执行按位“与”或“或”,返回单个结果值。
常见错误现象:
– 写 SELECT BIT_AND(status) FROM users 却没加 GROUP BY,在严格模式下直接报错;
– 期望 BIT_AND(a, b) 这种双参数写法,但 MySQL 不支持(会报 Incorrect parameter count)。
- 正确用法必须配合
GROUP BY,或用于无分组的全表聚合(此时整张表算作一组) - 输入值会被隐式转为无符号 64 位整数(
BIGINT UNSIGNED),负数或超范围值可能被截断或归零 - 若组内有
NULL,BIT_AND返回NULL(因为“与”运算中任意NULL使结果不确定);BIT_OR同理,但实践中更常容忍NULL(只要有一个非零值,结果就不为零)
典型使用场景:权限合并与状态汇总
假设用户权限用 bit flag 存储:read=1(0b001)、write=2(0b010)、delete=4(0b100)。一张 user_permissions 表记录用户对各资源的权限位:
user_id | resource_id | perm_bits --------+-------------+---------- 1 | 101 | 3 -- read + write 1 | 102 | 5 -- read + delete 2 | 101 | 1 -- read only
要查每个用户对所有资源的「累计权限」(即只要任一资源有某权限,就算拥有),该用 BIT_OR:
SELECT user_id, BIT_OR(perm_bits) AS total_perms FROM user_permissions GROUP BY user_id;
结果:user_id=1 → 7(0b111,read+write+delete 全都有)user_id=2 → 1(只有 read)
- 若改用
BIT_AND,则表示「在所有资源上都具备的权限」——比如用户 1 对 resource 101 有 write,但对 102 没有,所以BIT_AND结果中 write 位为 0 - 注意:权限模型依赖业务语义,
BIT_OR更常用,BIT_AND多用于强约束场景(如“必须所有子项都启用才允许父项生效”)
与普通按位运算符(&、|)的关键区别
MySQL 中真正的逐行按位运算是用 & 和 | 符号,不是函数。例如:
SELECT id, flags & 1 AS has_read FROM users; -- 检查最低位是否为 1
而 BIT_AND(flags) 是另一回事——它把所有 flags 值“与”起来,不是和常量 1 做运算。
-
flags & 1:每行独立计算,结果是 0 或 1 -
BIT_AND(flags):跨行聚合,结果是所有行flags的按位与(比如三行值为 3、7、5 → 0b011 & 0b111 & 0b101 = 0b001 = 1) - 别混淆
BIT_AND(expr)和expr & const,前者无行上下文,后者有 - PostgreSQL、SQL Server 等不提供
BIT_AND/BIT_OR聚合函数,得用窗口函数或自定义 aggregate 模拟
容易被忽略的兼容性与边界问题
这些函数在 MySQL 5.7+ 默认可用,但有几个坑点几乎没人提:
- 输入值若为
NULL,整个组的BIT_AND结果就是NULL—— 即使其他值全非空。修复方式:用COALESCE(perm_bits, 0)替换NULL,但要注意 0 会影响“与”结果(任何数 & 0 = 0) - 如果字段是
TINYINT或SMALLINT,MySQL 仍按 64 位无符号处理,高位补零没问题;但若原始值来自带符号类型且为负(如TINYINT SIGNED的 -1),会被转成极大正数(255),导致意外的位模式 -
BIT_OR在空组(如WHERE过滤后无数据)下返回NULL,不是 0 —— 如果业务逻辑依赖“无数据 = 0”,需显式COALESCE(BIT_OR(...), 0)
真正麻烦的是跨版本迁移:MySQL 8.0 支持 BIT_AND 作为窗口函数(BIT_AND(...) OVER (...)),但 5.7 不支持,且语法不兼容。线上还在用 5.7 的团队,别想当然套用新文档示例。










