sql中&和|是按位运算符,行为因数据库而异:mysql、postgresql、sql server原生支持,sqlite需扩展或函数,oracle仅支持bitand();用& mask = mask判断标志位更安全可靠,|常用于update中设置权限;位运算无法走索引,大表慎用,建议冗余生成列并建索引优化查询性能。

SQL中&和|运算符的行为因数据库而异
MySQL、PostgreSQL、SQL Server 都支持 &(按位与)和 |(按位或),但 SQLite 默认不支持,需启用扩展或改用 bitand()/bitor() 函数(SQLite 3.35+)。Oracle 使用 BITAND() 函数,没有原生中缀运算符。别直接写 WHERE flags & 4 就跑,先确认你用的数据库是否真认这个语法。
用&判断某个标志位是否开启(常见权限/状态场景)
假设有个 user_permissions 整数字段,用二进制位表示权限:bit0=读、bit1=写、bit2=删。要查有“写”权限(即 bit1 = 2)的用户:
SELECT * FROM users WHERE user_permissions & 2 = 2;
注意不是 & 2 > 0 —— 虽然多数情况等价,但当字段为 NULL 时,NULL & 2 结果是 NULL,整个表达式不会命中;而 = 2 更明确表达“必须包含该位且不依赖其他位”。
- 推荐始终用
& mask = mask形式,语义清晰,避免误匹配(比如7 & 2是 2,但3 & 2也是 2,两者都满足“有写权限”,没问题;但若写成& 2 != 0,在某些方言里可能触发隐式类型转换警告 - mask 值建议用 2 的幂次方常量,如
1、2、4、8……别手算16#十六进制,易错 - 字段类型必须是整数(
TINYINT、INT等),VARCHAR或TEXT会静默转成 0 导致全不匹配
|用于动态组合权限或设置标志位(UPDATE 场景更常见)
SELECT 中很少单独用 | 做过滤,它更适合在 UPDATE 里“开开关”:比如给用户追加“删除”权限(bit2 = 4):
UPDATE users SET user_permissions = user_permissions | 4 WHERE id = 123;
这比先 SELECT 再计算再 UPDATE 更原子、更安全。但要注意:
- 如果字段原本是
NULL,NULL | 4结果仍是NULL,不会变成 4 —— 必须提前COALESCE(user_permissions, 0) - MySQL 8.0+ 和 PostgreSQL 支持
BIT_OR()聚合函数,但那是另一回事,和单行|运算无关 - 不要在
WHERE子句里用|做条件判断(如WHERE status | 8 = 8),逻辑难懂且无法走索引
位运算结果不能直接走索引,大表慎用
哪怕 user_permissions 字段上有索引,WHERE user_permissions & 4 = 4 也无法利用索引进行范围扫描,MySQL/PostgreSQL 都会退化为全表扫描。线上千万级用户表,这种查询可能秒变慢 SQL。
- 真正高频的位判断,建议冗余一个生成列(MySQL 5.7+)或物化视图(PostgreSQL),例如:
ALTER TABLE users ADD COLUMN has_delete TINYINT AS ((user_permissions & 4) = 4) STORED,然后对has_delete建索引 - SQLite 没有生成列,只能靠应用层缓存或预计算字段
- 位运算本身很快,瓶颈永远在 I/O —— 别被“CPU 运算快”误导,数据没索引,再快的 & 也救不了
实际用的时候,先看执行计划(EXPLAIN),再决定要不要拆字段或加冗余列。位存储省空间,但换来的可能是半夜告警。










