go 使用二进制位记录数据状态值
在日常开发中,我们经常会遇到一条数据,同时会有多个状态或者多种类型值时.
例如: 一个老师的职位,可能是班主任,同时也是语文老师,也可能是学校主任.
职位对应编号
1-班主任; 2-语文老师;3-学校主任
传统方式:
id | 姓名 | 职位 | … |
---|---|---|---|
1 | 张三 | 1,2,3 | … |
传统方式在进行班主任进行检索时, contrains('1')
的方式进行检索, 会将职位为10,11等的信息查找出来. 不符合我们的检索结果
建立关系表方式:
id | 姓名 | … |
---|---|---|
1 | 张三 | … |
用户id | 职位id |
---|---|
1 | 1 |
1 | 2 |
1 | 3 |
使用链表查询的方式,可以检索到数据.
优点: 数据集清晰
缺点: 需要链表查询, 如果用户信息还有其他表,这个时候需要多个表进行链表,效率低
使用二进制位的方式
id | 姓名 | 职位 | … |
---|---|---|---|
1 | 张三 | 7 | … |
职位使用二进制位进行存储,SELECT * FROM 用户名 WHERE 职位字段名 & 1
优点: 少建一张表, 减少一次链表查询
缺点:
1.可读性差,职位存储值不能直观读取
2.如果使用sql进行与查询,不能走索引; 如果要走索引,使用in方法,in的值换算方法https://blog.youkuaiyun.com/qq_35392791/article/details/123842022
使用建议:
1.类型变化较少的字段,如少量的职位、状态等
2.值类型应从1,2,3,4,5连续设置, 尽量让每个二进制位都为1
代码
将二进制位转换为十进制数
// 入参: 切片(二进制对应位)
// 1,2,3,4 对应的二进制 00001111// 结果: 7
func SliceToOb(d []int) int {
res := 0
for _,v := range d {
res |= 1 << (v-1)
} return res
}
将十进制数转换为对应二进制位置数据
// 入参: 十进制数
// 将十进制数转换为二进制1对应位
// 运算过程:
// 12&1 (12 >> 1) 即为 6&1 (6 >> 1) 3&1 (3>>1) 1&1 (1>>1) 0// 00001100 00000110 00000011 00000001
// & 0001 & 0001 & 0001 & 0001
// --------- ---------- -------- --------
// 00000000 00000000 00000001 00000001
// 为0不记录位置信息 为0不记录位置信息 为1记录位置信息 为1记录位置信息 为0则遍历位置结束
// 开始下一位二级制计算
// 结果: [1,3]
func ObToSlice(d int) []int {
res := make([]int, 0)
i := 1
flag := true
for flag {
if d == 0 {
flag = false
break
}
if d & 1 != 0 {
res = append(res, i)
} i++
d = d >> 1
}
return res
}
调用演示
func main() {
res := SliceToOb([]int{1,2,3})
fmt.Printf("%d,%08b\n",res,res)
res2 := ObToSlice(res)
fmt.Printf("%+v\n",res2)
}