我们知道,PHP当中的错误级别常量,是根据二进制位特性而确定的一个个整数,可以简单的通过位运算定制PHP的错误报告。我们也可以将其应用到 MySQL 当中!
你是否遇到过下面这样的情况?
一、用户可能有若干不同属性或不同状态,然后你可能会在数据表中通过一个个字段去标记和实现,比如:
id | int(11) | 用户ID(自增) |
username | char(50) | 用户名 |
password | char(50) | 密码 |
... | ... | ... |
confirm_info | tinyint(1) | 是否确认资料 [ 0/1 ] |
confirm_contract | tinyint(1) | 是否确认协议 [ 0/1 ] |
freeze | tinyint(1) | 冻结状态 [ 0/1 ] |
payer | tinyint(1) | 笔记是否付费用户 [ 0/1 ] |
high_quality | tinyint(1) | 标记是否优质用户 [ 0/1 ] |
二、用户拥有多个用户组(角色),常见的实现方法有:
1、增加一个字段,专门存它的用户组,用逗号隔开
id | int(11) | 用户ID(自增) |
username | char(50) | 用户名 |
password | char(50) | 密码 |
... | ... | ... |
confirm_info | tinyint(1) | 是否确认资料 [ 0/1 ] |
confirm_contract | tinyint(1) | 是否确认协议 [ 0/1 ] |
freeze | tinyint(1) | 冻结状态 [ 0/1 ] |
payer | tinyint(1) | 笔记是否付费用户 [ 0/1 ] |
high_quality | tinyint(1) | 标记是否优质用户 [ 0/1 ] |
role_id | varchar(255) | 如:1,3,11 |
使用 FIND_IN_SET() 或 LIKE 进行数据查询。这种存储方式,虽然比较直观,结构也比较接单,但是,但是查询和维护数据都不方便
2、增加一个关联表
user_id | int(11) | 用户ID |
role_id | int(11) | 角色ID |
... | ... | ... |
通过JOIN,连表查询。这种存储方式,似乎很好扩展,无论是增加了用户组,还是重新给用户划分用户组,直接操作这张关联表就行。但是,连表查询终究是需要操作多张表的,效率肯定不如单表查询,而且如果用户属性多了(不仅限于分组),那就得关联更多的表或者查多次,使查询变得更加复杂!
我们知道,PHP当中的错误级别常量,是根据二进制位特性而确定的一个个整数,可以简单的通过位运算定制PHP的错误报告。我们也可以将其应用到 MySQL 当中,还是以上面的用户表为例,我们只用一字段 status,专门记录用户状态,其结构如下:
id | int(11) | 用户ID(自增) |
username | char(50) | 用户名 |
password | char(50) | 密码 |
... | ... | ... |
status | int(11) | 用户状态位 |
然后我们给每一种状态设定一个对应的值,这个值需要使用2的N次方来表示,比如:
1 | 已确认资料 |
2 | 已确认协议 |
4 | 已冻结 |
8 | 付费用户 |
16 | 优质用户 |
查询技巧示例:
1、查询所有已冻结的用户:
SELECT * FROM `user` WHERE `status` & 4 = 4
2、查询所有未确认协议的用户
SELECT * FROM `user` WHERE `status` & 2 = 0
3、设置ID等于186的用户为优质用户
UPDATE `user` SET `status` = `status` | 16 WHERE `id` = 186
4、取消ID等于186的优质用户身份
UPDATE `user` SET `status` = `status` ^ 16 WHERE `id` = 186
5、查询已冻结的优质付费用户(4 + 8 + 16 = 28)
SELECT * FROM `user` WHERE `status` & 28 = 28
同理,上述应用场景二中的分组问题,也可以使用同样的方法,优化表结构!然后使用位运算,实现各种条件的查询!