如何有条件地过滤WHERE子句中的列?

时间:2009-12-30 02:57:02

标签: tsql dynamic conditional where-clause

好的,无数次条件列问题:

我正在编写一个存储过程,它接受一个映射到多个标志列之一的输入参数。筛选请求列的最佳方法是什么?我目前正在使用SQL2000,但即将转向SQL2008,所以如果可以的话,我会采用现代解决方案。

在sproc中查询的表看起来像

ID ...  fooFlag  barFlag  bazFlag  quuxFlag
--      -------  -------  -------  --------
01         1        0       0          1
02         0        1       0          0
03         0        0       1          1
04         1        0       0          0

我想做点什么

select ID, name, description, ...
from myTable
where (colname like @flag + 'Flag') = 1

所以,如果我像exec uspMyProc @flag = 'foo'那样调用sproc,我会回到第1行和第4行。

我知道我不能直接在SQL中用parens做这个部分。为了做动态SQL,我必须将整个查询填充到一个字符串中,在WHERE子句中连接@flag param,然后执行字符串。除了在进行动态SQL时我得到的肮脏感觉,我的查询相当大(我选择了几十个字段,加入了5个表,调用了几个函数),所以它是一个巨大的字符串,因为一行在3行WHERE过滤器中。

或者,我可以有4个查询副本,并在CASE语句中选择它们。这使得SQL代码可以直接执行(并且受语法高亮等影响)但是以重复大块代码为代价,因为我不能仅在WHERE子句上使用CASE。

还有其他选择吗?可以应用任何棘手的连接或逻辑操作?或者我应该克服它并执行动态SQL?

6 个答案:

答案 0 :(得分:20)

有几种方法可以做到这一点:

您可以使用案例陈述来执行此操作。

select ID, name, description, ...
from myTable
where CASE
    WHEN @flag = 'foo' then fooFlag
    WHEN @flag = 'bar' then barFlag
END = 1

您可以使用IF。

IF (@flag = 'foo') BEGIN
    select ID, name, description, ...
    from myTable
    where fooFlag = 1
END ELSE IF (@flag = 'bar') BEGIN
    select ID, name, description, ...
    from myTable
    where barFlag = 1
END

....

你可以有一个带有很多括号的复杂where子句。

select ID, name, description, ...
from myTable
where (@flag = 'foo' and fooFlag = 1)
OR (@flag = 'bar' and barFlag = 1) OR ...

您可以使用动态sql执行此操作:

DECLARE @SQL nvarchar(4000)

SELECT @SQL = N'select ID, name, description, ...
from myTable
where (colname like ''' + @flag + 'Flag'') = 1'

EXECUTE sp_ExecuteSQL @SQL, N''

还有更多,但我认为其中一个会让你前进。

答案 1 :(得分:4)

“或者,我可以有4个查询副本,并在CASE语句中选择它们。”

您不需要复制整个查询4次,只需将所有可能性添加到查询的单个副本中的where子句中:

select ID, name, description, ...
from myTable
where (@flag = 'foo' and fooFlag = 1) OR (@flag = 'bar' and barFlag = 1) OR ...

答案 2 :(得分:3)

我要做的是CASE开头的一些变量。例如:

DECLARE
    @fooFlag int,
    @barFlag int,
    @bazFlag int,
    @quuxFlag int

SET @fooFlag = CASE WHEN @flag = 'foo' THEN 1 ELSE NULL END
SET @barFlag = CASE WHEN @flag = 'bar' THEN 1 ELSE NULL END
SET @bazFlag = CASE WHEN @flag = 'baz' THEN 1 ELSE NULL END
SET @quuxFlag = CASE WHEN @flag = 'quux' THEN 1 ELSE NULL END

SELECT ID, name, description, ...
FROM myTable
WHERE (fooFlag >= ISNULL(@fooFlag, 0) AND fooFlag <= ISNULL(@fooFlag, 1))
AND (barFlag >= ISNULL(@barFlag, 0) AND barFlag <= ISNULL(@barFlag, 1))
AND (bazFlag >= ISNULL(@bazFlag, 0) AND bazFlag <= ISNULL(@bazFlag, 1))
AND (quuxFlag >= ISNULL(@quuxFlag, 0) AND quuxFlag <= ISNULL(@quuxFlag, 1))

这个查询的好处在于,因为“flags”的可能值是有界的,所以您可以将所有条件计算为先决条件,而不是将列包装在其中。这保证了对索引的任何列进行高性能索引查找,并且不需要编写任何动态SQL。而且出于显而易见的原因,它比编写4个单独的查询要好。

答案 3 :(得分:1)

int应该被接受为varchar值

declare @CompanyID as varchar(10) = ''    -- or anyother value

select *  from EmployeeChatTbl chat

  where chat.ConversationDetails like '%'+@searchKey+'%' 

                and
                (
                    (0 = CASE WHEN (@CompanyID = '' ) THEN 0 ELSE 1 END) 
                                or 
                    (chat.CompanyID = @CompanyID)
                )

<强> 工作

当存在companyID时,则基于它进行过滤,否则,跳过过滤。

答案 4 :(得分:0)

您可以为每个可能的标志列设置一个参数,然后检查参数是否为null或列中的值是否等于参数。然后传入1表示要检查的标志,并将其他标志保留为空。

select id, name, description, ...
from myTable
where (@fooFlag is null or fooFlag = @fooFlag) AND
      (@barFlag is null or barFlag = @barFlag) AND
      ...

老实说,这似乎是构建动态LINQ查询并在进入SQL2008后跳过SPROC的理想选择。

答案 5 :(得分:0)

where
   case when @value<>0 then Field else 1 end
   =
   case when @value<>0 then @value else 1 end