位运算 :与(&)、或(|)、异或(^)、取反(~)、左移(<<)、右移(>>)、无符号右移(>>>)
整数
// 1111111111111111111111111111111 int 最大 2147483647 二进制31 位
// 10000000000000000000000000000000 int 取反 -2147483648 二进制32 位
数据库位与字段设计中:指定位数改0,改1 算法如下: 首先需要增加读写锁:保证计算开始时 数据值是最新的;
public static int modifyIntBitPositionValue(int num, int position, boolean value) {
if (value) {
// 将指定位置的位设置为1,将数字1的二进制表示向左移动position位,右边用0填充。例如,如果position为3,那么结果将是8(二进制表示为1000)等价于2^position
return num | (1 << position);
} else {
// 将指定位置的位设置为0; 將指定位改成0 其他全部位置改成1 (取反),例如 2^2 : 11111111111111111111111111111011
return num & ~(1 << position);
}
}
做法: 位置X(0开始)
第一步:判空
if (consumptionDetailBo.getDiscount() == null) {
consumptionDetailBo.setDiscount(0);
}
第二步 位置改1:和之前的值进行或| 运算
personConsumptionDetailBo.setDiscount(
personConsumptionDetailBo.getDiscount() | (int) Math.pow(2, X));
第二步:位置改0:先取反(2^X)-再与&数据库的值
示例: ProductSource & (~2^X)
注意:出去消费明细生成流程之外的更新,必须加读写锁先查询之前的值再计算新的值
位运算符是二目运算符
| 符号 | 描述 | 运算规则 |
|---|---|---|
| & | (按位)与 | 两个位都为1时,结果才为1 |
| 按位)或 | 两个位都为0时,结果才为0 | |
| (按位)异或 | 两个位相同为0,相异为1 | |
| ~ | (按位)取反 | 0变1,1变0 |
| << | (按位)左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
| (按位)右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,有的补符号位(算术右移),有的补0(逻辑右移) |
权限等一对多的字段设计
参照 Linux chmod命令
举例说明:
假设人员认证有实名认证、人脸认证、指纹认证、机构认证 4 种
我们一般的做法可能有两种:一种是增加一个varchar字段,每种认证之间用一个特殊符号分隔保存,例如"1,2,3,4"; 另一种方法是建立一个关系表
第一种方法查询极不方便,例如 查询有人脸和机构认证的人员, 第二种方法如果人员还有其他属性,就要根据每个属性去建立关系表,增加了复杂度。
我们可以这么做:
CREATE TABLE IF NOT EXISTS t_test(
Id serial,
Name varchar(10) NOT NULL, -- 员工
Role smallint DEFAULT 0, -- 认证 1实名认证 2人脸认证 4指纹认证 8机构认证
CreatedAt timestamptz(6) DEFAULT CURRENT_TIMESTAMP,
UpdatedAt timestamptz(6) DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (Id)
);
首先我们用 1、2、4、8、16、32、64...这种2 n的数字 让每个数值代表一个类型
Role 字段存的是类型数值之和,如 员工具有人脸认证和指纹认证 则Role的值为6 (2+4 = 6 )
经过如上方法计算的Role值后,假设有如下数据
| id | role | 说明 |
|---|---|---|
| 1 | 3 | 1+2 |
| 2 | 14 | 2+4+8 |
| 3 | 0 | 无认证 |
| 4 | 5 | 1+4 |
| 5 | 6 | 2+4 |
| 6 | 7 | 1+2+4 |
从表中可以看到Role值成了3、14、0、5、6、7,这些数值如何供我们查询使用呢?
我们此时就可以使用位运算来解决这个问题
多个和单个条件都适用
-
当我们需要查询 具有 实名认证(1) 的员工时:
SELECT * FROM t_test WHERE Role&1=1 -
当我们需要查询 具有 实名认证(1) 和 指纹认证(4) 的员工时:
SELECT * FROM t_test WHERE Role&5=5 -
当我们需要设置 员工 具有实名认证(1) 和 指纹认证(4) 的员工时:
UPDATE t_test SET Role=Role|5 WHERE Id=1 -
当我们需要设置 员工 不具有实名认证(1) 和 指纹认证(4) 的员工时:
此时要注意 该员工一定是具有 1 和 4 认证的才能使用该方法去设置不具有该认证UPDATE t_test SET Role=Role^5 WHERE Id=1 AND Role&5=5
类似状态等一对一的字段设计
举例说明:
假设人员状态有上班、出差、休息、请假 4 种
如果使用位运算的方式 我们同样可以设计成:
CREATE TABLE IF NOT EXISTS t_test(
Id serial,
Name varchar(10) NOT NULL, -- 员工
Status smallint DEFAULT 0, -- 状态 1上班 2出差 4休息 8请假
CreatedAt timestamptz(6) DEFAULT CURRENT_TIMESTAMP,
UpdatedAt timestamptz(6) DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (Id)
);
-
当我们需要查询 上班和请假 的员工时:
SELECT * FROM t_test WHERE Status&9>0
-
与运算 a & b ,
-
或运算 a | b ,
-
异或运算 a ^ b ,
-
将或运算理解为 + 法
例如
1|2 = 3 (1+2 = 3)
1|2|4 = 7 (1+2+4 = 7) -
将 异或运算理解为 - 法
例如
3^2 = 1 (3-2 = 1)
3^1 = 2 (3-1 = 2) -
最后 与运算 作为判断
例如
3&2 = 1 (3 = 1 + 2, 由 1和2组成 ,所以判断3&1 = 1 )
3&2 = 2 (3 = 1 + 2, 由 1和2组成 ,所以判断3&2 = 2 )
3&4 = 0 ( 3 没有由 4组成,所以判断3&4 = 0)
查询可配置; Dis & 1 : condition条件 ,0: value 值;
SELECT
Id ,Dis,Dis & 1 as Dis1 ,Dis&2 as Dis2
FROM
Tab
WHERE
IsDelete = 0
AND Id IN (
'180606020227654185',
'191001211524654198',
'20160512144219375328',
'201605121442193753281',
'210709123009552149'
)
-- 全部 则不带条件
AND Dis & 1=1 -- 脱敏
AND Dis & 1=0 -- 不脱敏
AND Dis & 2 =2-- 项目
AND Dis & 2=0 -- 统一
-- AND Dis & 4=4 -- 其他 是
-- AND Dis & (1+2)=3 -- 脱敏 项目
-- AND Dis & (1+2+4)=7 -- 脱敏 项目 其他 是
-- AND Dis & 4=0 -- 其他 否
LIMIT 6
-- 2^0 脱敏: 0 不脱敏, 1 脱敏
-- 2^1 明细方式: 0 统一 1 项目
-- 2^2 其他 0 否,1 是
180606020227654185 2018-01-11 09:15:00 0 0 0
191001211524654198 2019-10-01 20:14:00 1 1 0
20160512144219375328 2023-05-01 21:24:58 2 0 2
201605121442193753281 2023-05-05 17:45:23 3 1 2
201605121442193753281 2023-05-19 07:34:12 4 0 0
201605121442193753281 2023-07-09 20:48:10 6 0 2
210709123009552149 2023-07-04 12:28:00 7 1 2
查询方式1
SELECT
KeyId,TradeTime , Discount
FROM
DetailTab
WHERE
IsDelete = 0
AND Id IN (
'180606020227654185',
'191001211524654198',
'20160512144219375328',
'201605121442193753281',
'210709123009552149'
)
-- 全部 则不带条件
AND Dis & 1 -- 脱敏
AND Dis & 2 -- 项目
AND Dis & 4 -- 其他 是
AND Dis & (1+2) -- 脱敏 项目
AND Dis & (1+2+4) -- 脱敏 项目 其他 是
AND Dis & 1=0 -- 不脱敏
AND Dis & 2=0 -- 统一
AND Dis & 4=0 -- 其他 否
LIMIT 6
2^0 脱敏: 0 不脱敏, 1 脱敏
2^1 明细方式: 0 统一 1 项目
2^2 其他 0 否,1 是
查询方式2
SELECT
KeyId,TradeTime , Discount
FROM
PersonConsumptionDetailTab
WHERE
IsDelete = 0
AND KeyId IN (
'180606020227654185',
'191001211524654198',
'20160512144219375328',
'201605121442193753281',
'210709123009552149'
)
-- 全部 则不带条件
AND Dis & 1=1 -- 脱敏
AND Dis & 2 =2-- 项目
AND Dis & 4=4 -- 其他 是
AND Dis & (1+2)=3 -- 脱敏 项目
AND Dis & (1+2+4)=7 -- 脱敏 项目 其他 是
AND Dis & 1=0 -- 不脱敏
AND Dis & 2=0 -- 统一
AND Dis & 4=0 -- 其他 否
LIMIT 6
-- 2^0 脱敏: 0 不脱敏, 1 脱敏
-- 2^1 明细方式: 0 统一 1 项目
-- 2^2 其他 0 否,1 是
定制业务限制:
2^0 平台券数据脱敏: 消费明细脱敏标识
2^1 明细出账单方式
-
0默认同步给客户|统一出账单
-
1不同步给用户|按照项目出账单
2^2 0:单结离店付 1:单结预付
<if test="param.customLimit != null and param.customLimit == true">
AND (Discount & 2 > 0)=0
</if>