如何根据动态条件构建 where 语句
How to build a where statement based on dynamic conditions
由于业务复杂性,我们必须有动态条件 table。
我们的table叫做“规则”
它有这个结构
ID Field Op Unit Value Discount
==========================================================
1 Gender = NULL Male 0.07
2 Gender = NULL Female 0.08
3 Age >= Year 60 0.02
4 Age = Year 18 0.09
5 Age <= Month 6 0.04
6 Height <= NULL 150 0.03
7 Height >= NULL 165 0.06
我正在尝试 select 仅记录与客户数据匹配的记录
DECLARE @Gender varchar(10) = 'Male'
DECLARE @Age int = 70 -- In months
SELECT *
FROM Rules
WHERE (Field = 'Gender' AND Value = @Gender) OR
(@Age/12 BETWEEN
CASE
WHEN Field = 'Age' AND Op IN ('=', '>', '>=') AND Unit = 'Year' THEN (@Age/12)
ELSE 0
END
AND
CASE
WHEN Field = 'Age' AND Op IN ('=', '<', '<=') AND Unit = 'Year' THEN (@Age/12)
ELSE 999
END
)
但是这个查询的问题是我得到了所有规则 selected 而不仅仅是相关的规则。
如何调整以将 selection 限制为仅相关记录?
我假设 Value
是一个文本列。在比较年龄之前,它必须转换为数字。我这样做是在子select.
我还假设 @Age
是以月为单位给出的。因此,如果给出 70 岁,则必须输入
DECLARE @Age int = 12 * 70
年龄必须始终以相同的单位给出,因为查询无法确定是否应该将其与年份或月份进行比较。例如。如果你输入5,那么如果是5个月的婴儿,则适用0.04的折扣,但如果是5岁的儿童,则不适用年龄折扣。
SELECT *
FROM
(SELECT *, CASE WHEN IsNumeric(Value)=1 THEN CONVERT(int, Value) ELSE 0 END AS IntValue
FROM Rules
) R
WHERE
(Field = 'Gender' AND @Gender = Value) OR
(Field = 'Age' AND Op = '>=' AND
@Age >= IntValue * CASE Unit WHEN 'Year' THEN 12 ELSE 1 END) OR
(Field = 'Age' AND Op = '=' AND
@Age = IntValue * CASE Unit WHEN 'Year' THEN 12 ELSE 1 END) OR
(Field = 'Age' AND Op = '<=' AND
@Age <= IntValue * CASE Unit WHEN 'Year' THEN 12 ELSE 1 END);
另请注意,我乘以 IntValue
将年换算成月,而不是除以年龄。这样更好,因为除法会导致舍入误差。通过乘法,我们保持在整数范围内。
这可以简化,例如通过测试 Age
的字段名称一次,但我认为如果每个规则都单独翻译会更容易阅读。
由于业务复杂性,我们必须有动态条件 table。
我们的table叫做“规则”
它有这个结构
ID Field Op Unit Value Discount
==========================================================
1 Gender = NULL Male 0.07
2 Gender = NULL Female 0.08
3 Age >= Year 60 0.02
4 Age = Year 18 0.09
5 Age <= Month 6 0.04
6 Height <= NULL 150 0.03
7 Height >= NULL 165 0.06
我正在尝试 select 仅记录与客户数据匹配的记录
DECLARE @Gender varchar(10) = 'Male'
DECLARE @Age int = 70 -- In months
SELECT *
FROM Rules
WHERE (Field = 'Gender' AND Value = @Gender) OR
(@Age/12 BETWEEN
CASE
WHEN Field = 'Age' AND Op IN ('=', '>', '>=') AND Unit = 'Year' THEN (@Age/12)
ELSE 0
END
AND
CASE
WHEN Field = 'Age' AND Op IN ('=', '<', '<=') AND Unit = 'Year' THEN (@Age/12)
ELSE 999
END
)
但是这个查询的问题是我得到了所有规则 selected 而不仅仅是相关的规则。
如何调整以将 selection 限制为仅相关记录?
我假设 Value
是一个文本列。在比较年龄之前,它必须转换为数字。我这样做是在子select.
我还假设 @Age
是以月为单位给出的。因此,如果给出 70 岁,则必须输入
DECLARE @Age int = 12 * 70
年龄必须始终以相同的单位给出,因为查询无法确定是否应该将其与年份或月份进行比较。例如。如果你输入5,那么如果是5个月的婴儿,则适用0.04的折扣,但如果是5岁的儿童,则不适用年龄折扣。
SELECT *
FROM
(SELECT *, CASE WHEN IsNumeric(Value)=1 THEN CONVERT(int, Value) ELSE 0 END AS IntValue
FROM Rules
) R
WHERE
(Field = 'Gender' AND @Gender = Value) OR
(Field = 'Age' AND Op = '>=' AND
@Age >= IntValue * CASE Unit WHEN 'Year' THEN 12 ELSE 1 END) OR
(Field = 'Age' AND Op = '=' AND
@Age = IntValue * CASE Unit WHEN 'Year' THEN 12 ELSE 1 END) OR
(Field = 'Age' AND Op = '<=' AND
@Age <= IntValue * CASE Unit WHEN 'Year' THEN 12 ELSE 1 END);
另请注意,我乘以 IntValue
将年换算成月,而不是除以年龄。这样更好,因为除法会导致舍入误差。通过乘法,我们保持在整数范围内。
这可以简化,例如通过测试 Age
的字段名称一次,但我认为如果每个规则都单独翻译会更容易阅读。