如何构建具有"NOT IN (a,b,c)"逻辑的决策表?



假设有一个业务规则用于派生值,并且由于业务用户需要更新该规则,因此逻辑必须保存在表中,而不是函数中。

当前功能示例

WHEN inputA IN (1,2,3) AND inputB NOT IN (55,66) THEN OUTPUT = 'HQ'
WHEN inputA IN (3,6)   AND inputB     IN (27,44) THEN OUTPUT = 'Northern'
WHEN inputC IN (6,4,1) AND inputB NOT IN (55,66) THEN OUTPUT = 'Eastern'
etc.

我可以为每个组合创建一个表,其中包含一行例如

C val val>27
OUTPUTA valB val
北部327
北方344
北部6
北部644

您可以用一个表来表示这些表达式,其中每行包含:

  • 输出
  • 等于?(真或假(
  • 输入
  • 价值

这将表示";如果(INPUT=VALUE(=EQUALS?则输出";,除了对于给定的OUTPUT/INPUT,您只需要1个令人满意的行,其中EQUALS?是真的,但所有行都必须满足EQUALS?是错误的。

这假设您只有一个";规则";对于每个不同的输出。

这里尝试使用一个包含正规则和负规则的规则表。

规则表将有效值存储为整数,但无效值存储在带分隔符的字符串中。(不确定json还是xml会更好(

但制定消极的规则会让事情变得非常棘手
因为通过为相同的输出名称添加另一个规则很容易忽略一个规则。

就我个人而言,我认为对这种类型的规则使用UDF更安全。

create table test (
id int identity(1,1) primary key, 
inputA int, 
inputB int, 
inputC int
)
create table output_areas (
code varchar(2) primary key, 
name varchar(30) not null
)
insert into output_areas values
('HQ', 'HQ')
, ('N', 'Northern'), ('E', 'Eastern')
, ('W', 'Westhern'), ('S', 'Southern')
, ('X', 'Extern'), ('Z', 'The Zone')

create table input_rules (
id int identity(1,1) primary key, 
output_area_code varchar(2) not null,
relevance int not null default 0,
inputA_valid int,
inputB_valid int,
inputC_valid int, 
inputA_invalid varchar(100),
inputB_invalid varchar(100),
inputC_invalid varchar(100),
foreign key (output_area_code) references output_areas(code)
)

insert into input_rules (output_area_code, relevance) 
values ('X', 0);
insert into input_rules (output_area_code, relevance, inputA_valid) 
values ('Z',10, 1);
insert into input_rules (output_area_code, relevance, inputA_valid, inputB_invalid) 
values
('HQ', 20, 1, '|55|56|')
, ('HQ', 20, 2, '|55|56|')
, ('HQ', 20, 3, '|55|56|')
;
insert into input_rules (output_area_code, relevance, inputA_valid, inputB_valid) 
values
('N', 30, 3, 27), ('N', 30, 3, 44)
, ('N', 30, 6, 27), ('N', 30, 6, 44)
;
insert into input_rules (output_area_code, relevance, inputC_valid, inputB_invalid) 
values
('E', 20, 6, '|55|66|' )
, ('E', 20, 4, '|55|66|' )
;

insert into test 
(inputA, inputB, inputC) values 
(1, 56, null)
, (3, 44, null), (3, 66, null)
, (1, 66, null), (1, 88, null)
, (null, 66, 6), (null, 88, 6)
select * from input_rules
id|output_area_code|correlation|inputA_valid|inputB_valid|inputC_valid|inputA_invalid|inputB_invalid|inputC_invalid-:|:----------------|----------------:|-----------------------------1|X|0|null|null|null|null|null|2|Z|10|1|>null|null|null 56||null4|HQ|20|2|null|null|null|55|56||2null5|HQ |20|3|1null2|3nullnullnull|null30|3|44|null|null|null8|N|30|6|27|>null>|null|null9 |N|30|6|44|nullnull|null|6|null>|55|66|>null11|E|20|1null2|2nullnull55|66|null
select *
from test t
outer apply (
select top 1 ref.name as output
from input_rules r
join output_areas ref on ref.code = r.output_area_code
where (r.inputA_valid is null or r.inputA_valid = t.inputA) 
and (r.inputB_valid is null or r.inputB_valid = t.inputB) 
and (r.inputC_valid is null or r.inputC_valid = t.inputC)
and (r.inputA_invalid is null or r.inputA_invalid not like concat('%|', t.inputA, '|%')) 
and (r.inputB_invalid is null or r.inputB_invalid not like concat('%|', t.inputB, '|%')) 
and (r.inputC_invalid is null or r.inputC_invalid not like concat('%|', t.inputC, '|%')) 
order by r.relevance desc, r.output_area_code asc
) ca
id|inputA|inputB|inputC|output-:|-----:|------------1|1|56|null|区域2|3|44|null|北方3|3|66||HQ4|1|66||HQ5|1|88 ||HQ6|null|66|6|外部7|null|88|6|Eastern

db<gt;小提琴这里

最新更新