位与(位运算符)数据库设计实占mysql

618 阅读8分钟

位运算 :与(&)、或(|)、异或(^)、取反(~)、左移(<<)、右移(>>)、无符号右移(>>>)

image.png

image.png 整数

// 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);
    }
}

image.png

做法: 位置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
image.png按位)或两个位都为0时,结果才为0
image.png(按位)异或两个位相同为0,相异为1
~(按位)取反0变1,1变0
<<(按位)左移各二进位全部左移若干位,高位丢弃,低位补0
image.png(按位)右移各二进位全部右移若干位,对无符号数,高位补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值后,假设有如下数据

idrole说明
131+2
2142+4+8
30无认证
451+4
562+4
671+2+4

从表中可以看到Role值成了3、14、0、5、6、7,这些数值如何供我们查询使用呢?
我们此时就可以使用位运算来解决这个问题
多个和单个条件都适用

  1. 当我们需要查询 具有 实名认证(1) 的员工时:

    SELECT * FROM t_test WHERE Role&1=1  
    
  2. 当我们需要查询 具有 实名认证(1) 和 指纹认证(4) 的员工时:

    SELECT * FROM t_test WHERE Role&5=5  
    
  3. 当我们需要设置 员工 具有实名认证(1) 和 指纹认证(4) 的员工时:

    UPDATE t_test SET Role=Role|5  WHERE Id=1  
    
  4. 当我们需要设置 员工 不具有实名认证(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)
);
  1. 当我们需要查询 上班和请假 的员工时:

    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 否,1180606020227654185	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>

参考: www.cnblogs.com/dibtp/p/148…