数据库存储校验规则时含NULL字段分表设计是否合理可行
你提出的拆分为ConstantRule、FieldRule两张独立规则表的方案合理且具备长期可维护性,比之前的单表、两表设计更贴合规则本身的逻辑差异,落地时注意补全关联结构即可,具体分析如下:
方案合理性依据
之前的单表设计存在大量NULL值,本质是把两类逻辑完全互斥的规则硬塞进了同一张表:
- 一类是单字段和固定常量做对比的规则(比如你提到的rule1:
totalPrice > 10),这类规则根本不需要第二个业务字段参与计算 - 另一类是两个业务字段互相对比的规则(比如rule2、rule3),这类规则不需要固定常量值参与计算
强行把两类规则存在同一张表,不仅会产生无意义的空字段存储冗余,还很容易产生脏数据——比如出现一条规则同时填了固定常量值、第二个对比字段的无效配置,后续做规则解析的时候还要加大量判断分支处理空值,维护成本很高。
拆成两张独立表之后,每张表的字段都可以设置为非空约束,从表结构层面就避免了上述脏数据问题,规则解析逻辑也会更清晰:读ConstantRule就走常量对比逻辑,读FieldRule就走字段间对比逻辑,不需要额外做空值判断。
落地时的结构调整建议
你之前设计的MatchConditionTable(跨集合匹配条件表)不能丢,要和FieldRule做关联,不要直接把跨集合匹配字段塞进FieldRule里,三张表的推荐字段结构如下:
ConstantRule表:存储常量对比类规则,字段全部非空- RuleId:规则唯一标识
- FieldPath:待校验字段的取值路径(比如
totalPrice) - Operator:比较运算符(>、<、=、>=等)
- ConstantValue:对比的固定常量值(比如10)
FieldRule表:存储字段对比类规则,字段全部非空- RuleId:规则唯一标识
- LeftFieldPath:对比左侧字段的取值路径(比如
totalPrice、invoice.price) - Operator:比较运算符
- RightFieldPath:对比右侧字段的取值路径(比如
totalCost、order.price) - IsCrossCollection:是否为跨集合匹配规则(布尔值,rule2为false,rule3为true)
MatchConditionTable表:仅存储跨集合规则的元素匹配条件,仅关联IsCrossCollection = true的FieldRule- RuleId:关联的字段对比规则ID
- LeftMatchFieldPath:左侧集合的匹配字段路径(比如
invoice.name) - RightMatchFieldPath:右侧集合的匹配字段路径(比如
order.description)
查询逻辑说明
你担心的“查询时拉取全部适用规则”的逻辑不会因为分表变复杂,只需要分两步查询再在内存组装即可:
- 从
ConstantRule中查询所有FieldPath属于当前业务对象字段范围的规则 - 从
FieldRule中查询左右字段路径都属于当前业务对象字段范围的规则,其中标记为跨集合的规则,顺带把MatchConditionTable中关联的匹配条件查出来
整个查询逻辑不需要做复杂的多表关联判断,比之前单表遍历做空值分支判断的效率更高。
补充注意点
你给出的业务对象示例存在语法问题,数组不需要额外包裹一层对象,正确的JSON结构应为:
{ "totalPrice": 20, "totalCost": 15, "invoice": [ { "price": 10, "name": "microphone" }, { "price": 10, "name": "speaker" } ], "order": [ { "price": 10, "description": "microphone" }, { "price": 10, "description": "speaker" } ] }
做字段路径解析的时候要提前支持数组路径的取值逻辑,避免跨集合匹配时取不到对应字段值。
如果你的规则总规模很小(比如不足百条),不拆分也能满足需求,但从长期扩展来看,当前的分表方案后续要加新规则类型(比如字段和动态变量对比、多条件组合规则)时,不需要重构现有表结构,扩展性更强。
内容的提问来源于stack exchange,提问作者Weanich Sanchol

