如何根据动态条件构建 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 的字段名称一次,但我认为如果每个规则都单独翻译会更容易阅读。

参见:http://sqlfiddle.com/#!18/3df02/11/0