使用Boolean确定5-way Where子句

时间:2014-01-24 20:21:17

标签: sql boolean logic where-clause

我正在查看5个不同的列(不幸的是,db很糟糕)。如果五列中的两列具有一个“1”值和一个“2”值,我希望从结果中排除该记录。但是,如果它只有两个值中的一个,我希望它包含在内。

到目前为止我有这个,但我确信如果它有两个值中的一个,它将不会包含记录。

    NOT ((Ew.DocRecvd1 = 10 OR Ew.DocRecvd1 = 11) OR 
(Ew.DocRecvd2 = 10 OR Ew.DocRecvd2 = 11) OR 
(Ew.DocRecvd3 = 10 OR Ew.DocRecvd3 = 11) OR 
(Ew.DocRecvd4 = 10 OR Ew.DocRecvd4 = 11) OR 
(Ew.DocRecvd5 = 10 OR Ew.DocRecvd5 = 11))

感谢。

3 个答案:

答案 0 :(得分:1)

我建议您计算每个组中您想要的值的数量。而且,我会在子查询中执行此操作,因为这样可以使代码更具可读性和可维护性。

以下是一个例子:

from (select t.*,
             ((case when Ew.DocRecvd1 in (10, 11) then 1 else 0) +
              (case when Ew.DocRecvd2 in (10, 11) then 1 else 0) +
              (case when Ew.DocRecvd3 in (10, 11) then 1 else 0) +
              (case when Ew.DocRecvd4 in (10, 11) then 1 else 0) +
              (case when Ew.DocRecvd5 in (10, 11) then 1 else 0) +
             ) as Num1s,
             <something similar> as Num2s
      from table t
     ) t
where Num1s = 2 and Num2s = 1;

答案 1 :(得分:1)

您只需在where子句中说明过滤条件。给出一个表

create table foobar
(
  id int not null primary key ,
  c1 int not null ,
  c2 int not null ,
  c3 int not null ,
  c4 int not null ,
  c5 int not null ,
)
go

你可以说

select *
from foobar
where not (     2 = case c1 when 1 then 1 else 0 end
                  + case c2 when 1 then 1 else 0 end
                  + case c3 when 1 then 1 else 0 end
                  + case c4 when 1 then 1 else 0 end
                  + case c5 when 1 then 1 else 0 end
            and 1 = case c1 when 2 then 1 else 0 end
                  + case c2 when 2 then 1 else 0 end
                  + case c3 when 2 then 1 else 0 end
                  + case c4 when 2 then 1 else 0 end
                  + case c5 when 2 then 1 else 0 end
          )

另一种可能运行得更快的方法是使用掩码表,其中包含要排除的条件。像这样的东西:

create table mask
(
  c1 tinyint null ,
  c2 tinyint null ,
  c3 tinyint null ,
  c4 tinyint null ,
  c5 tinyint null ,
  unique clustered ( c1,c2,c3,c4,c5) ,
)

在您的情况下,只有30个条件被排除在外:

c1   c2   c3   c4   c5
---- ---- ---- ---- ----
NULL NULL 1    1    2
NULL NULL 1    2    1
NULL NULL 2    1    1
NULL 1    NULL 1    2
NULL 1    NULL 2    1
NULL 1    1    NULL 2
NULL 1    1    2    NULL
NULL 1    2    NULL 1
NULL 1    2    1    NULL
NULL 2    NULL 1    1
NULL 2    1    NULL 1
NULL 2    1    1    NULL
1    NULL NULL 1    2
1    NULL NULL 2    1
1    NULL 1    NULL 2
1    NULL 1    2    NULL
1    NULL 2    NULL 1
1    NULL 2    1    NULL
1    1    NULL NULL 2
1    1    NULL 2    NULL
1    1    2    NULL NULL
1    2    NULL NULL 1
1    2    NULL 1    NULL
1    2    1    NULL NULL
2    NULL NULL 1    1
2    NULL 1    NULL 1
2    NULL 1    1    NULL
2    1    NULL NULL 1
2    1    NULL 1    NULL
2    1    1    NULL NULL

(30 row(s) affected)

然后实际的查询是微不足道的(如果你要测试的列上有覆盖索引,那么测试是通过索引搜索完成的,因此应该执行得非常好:

select *
from dbo.foobar t
where not exists ( select *
                   from mask m
                   where t.c1 = m.c1
                     and t.c2 = m.c2
                     and t.c3 = m.c3
                     and t.c4 = m.c4
                     and t.c5 = m.c6
                 )

这种方法的优点是规则集是表驱动的,这意味着未来对规则的更改只是对掩码表的数据修改。

您也可以使用一组积极的规则,但在您的情况下,该集合更大(> 200个正面案例,而不是30个案例)。

答案 2 :(得分:0)

好的,我想我找到了我想要的结果。

我在查询的WHERE子句中使用了以下内容:

 NOT
     (2 =
     (CASE WHEN Ew.DocRecvd1 = 10 THEN 1 ELSE 0 END
      +
     CASE WHEN Ew.DocRecvd2 = 10 THEN 1 ELSE 0 END
      +
     CASE WHEN Ew.DocRecvd3 = 10 THEN 1 ELSE 0 END
      +
     CASE WHEN Ew.DocRecvd4 = 10 THEN 1 ELSE 0 END
      +
     CASE WHEN Ew.DocRecvd5 = 10 THEN 1 ELSE 0 END
      +
     CASE WHEN Ew.DocRecvd1 = 11 THEN 1 ELSE 0 END
      +
     CASE WHEN Ew.DocRecvd2 = 11 THEN 1 ELSE 0 END
      +
     CASE WHEN Ew.DocRecvd3 = 11 THEN 1 ELSE 0 END
      +
     CASE WHEN Ew.DocRecvd4 = 11 THEN 1 ELSE 0 END
      +
     CASE WHEN Ew.DocRecvd5 = 11 THEN 1 ELSE 0 END))

只有在我的数据库中才能将这两个文档放在一个记录中的五个位置之一,所以我正在寻找的两个文件的计数不能超过2个。

感谢Nicholas Carey和Gordon Linoff让我知道我能做什么并寻找!