MySQL中判断权限位必须写成(column & mask) = mask形式,否则因运算符优先级错误导致逻辑错误;大表需添加生成列并建索引才能高效查询。

直接用 & 判断权限位是否开启,但必须写成 (column & mask) = mask 形式,否则可能逻辑错误或漏匹配 NULL 值。
MySQL 中查“有某权限”必须加括号和等号
MySQL 的 & 优先级低于 =,不加括号会先算右边的比较。比如 permissions & 4 = 4 实际被解析为 permissions & (4 = 4) → permissions & 1,结果完全不对。
- 正确写法:
SELECT * FROM users WHERE (permissions & 4) = 4 - 查“同时有读(1)和写(2)”:
(permissions & 3) = 3(不是& 1 AND & 2) - 查“没有删除权限(4)”:
(permissions & 4) = 0,别用!= 4 - 字段为
NULL时,NULL & 4结果仍是NULL,整行不会被命中——这符合预期,但得意识到它不是“默认无权限”,而是“未知权限”
PostgreSQL / SQL Server 语法一致,SQLite 和 Oracle 要绕路
PostgreSQL 和 SQL Server 原生支持 & 和 |,写法和 MySQL 完全一样。但 SQLite 默认不认 & 作为位运算符(它当字符串连接符),得用函数:
- SQLite 3.35+:
bitand(permissions, 4) = 4 - Oracle:
BITAND(permissions, 4) = 4,没有中缀& - 所有数据库都支持十六进制字面量,但建议用十进制常量(如
4),避免手误写错0x4或0b100(后者多数库不支持)
WHERE 里用 &,UPDATE 里用 |,别混场景
& 是过滤,“检查有没有”;| 是修改,“追加某个开关”。它们语义不同,不能互换。
- 给用户加“删除”权限:
UPDATE users SET permissions = permissions | 4 WHERE id = 123 - 但若
permissions原值是NULL,NULL | 4还是NULL,得显式兜底:COALESCE(permissions, 0) | 4 - 移除权限要用
& ~mask,比如清掉“写”权限(2):permissions & ~2,不是- 2(减法会破坏其他位) -
BIT_OR()、BIT_AND()是聚合函数,只能在GROUP BY中用,不能写在WHERE里过滤单行
索引基本失效,大表务必冗余生成列
哪怕你在 permissions 上建了索引,WHERE (permissions & 4) = 4 也几乎无法走索引——优化器没法把位运算反推成范围或等值条件。
- 真实线上大表(百万级以上),应额外加生成列:
ALTER TABLE users ADD has_delete TINYINT AS ((permissions & 4) = 4) STORED - 再对这个布尔列建索引:
CREATE INDEX idx_has_delete ON users(has_delete) - 查询改写为:
WHERE has_delete = 1,就能稳定走索引 - 字段类型必须是整型(
TINYINT、INT等),VARCHAR或TEXT会静默转成 0,导致所有行都不匹配
位运算本身很快,但它的查询模式天然排斥索引。真正影响性能的从来不是 & 运算耗时,而是你忘了加生成列和索引,结果每次都在扫全表。

















