postgresql中数据表如何通过一个字段标识数据行多种状态?
通常我们在业务开发过程中,为了避免多余字段的创建,要尽可能的减少单表字段个数,针对一些数据状态可以采用一个数字融合多种数据状态。
以下是业务开发过程中的枚举类:
@Getter public enum TicketTagEnum { REMINDERS(1, "状态1"), REASSIGNMENT(2, "状态2"), REWORK(4, "状态3"), SUSPEND(8, "状态4"), TRANSFER(16, "状态5"), INSPECTION(32, "状态6"), ALARM(64, "状态7"); private final Integer code; private final String desc; TicketTagEnum(Integer code, String desc) { this.code = code; this.desc = desc; } // 将标签数字和拆分成2次幂的数字 public static List<Integer> splitTags(int n) { List<Integer> result = new ArrayList<>(); int bit = 1; while (n > 0) { if ((n & bit) != 0) { result.add(bit); } n -= (n & bit); bit <<= 1; } return result; } // 添加标签 public static int addTag(int oldTag, int newTag) { return oldTag | newTag; } // 移除标签,但是这种计算需要旧的标签中一定包含了新的标签 public static int removeTag(int oldTag, int newTag) { return splitTags(oldTag).contains(newTag) ? oldTag ^ newTag : oldTag; } // 移除标签 更加完善的写法 public static int removeTagPro(int oldTag, int newTag) { return oldTag & ~newTag; } }
添加数据状态时:TicketTagEnum.addTag(oldTag, newTag);
移除数据状态时:TicketTagEnum.removeTag(oldTag, newTag);
SQL查询数据行是否包含某种状态时:select * from table where 4 = any(split_into_powers_of_two_arr(tags))
具体SQL函数的实现如下:
CREATE OR REPLACE FUNCTION "public"."split_into_powers_of_two_arr"("n" int8) RETURNS "pg_catalog"."_int8" AS $BODY$ DECLARE current_value BIGINT := n; current_power BIGINT := 1; powers_arr BIGINT[] := '{}'; BEGIN WHILE current_value > 0 LOOP IF (current_value & 1) = 1 THEN powers_arr := array_append(powers_arr, current_power); END IF; current_value := current_value >> 1; current_power := current_power << 1; END LOOP; RETURN powers_arr; END; $BODY$ LANGUAGE plpgsql VOLATILE COST 100