Tsql 如何有条件地筛选WHERE子句中的列?

Tsql 如何有条件地筛选WHERE子句中的列?,tsql,dynamic,conditional,where-clause,Tsql,Dynamic,Conditional,Where Clause,好的,第无数个条件列问题: 我正在编写一个存储过程,它接受一个映射到几个标志列之一的输入参数。对请求的列进行筛选的最佳方式是什么?我目前使用的是SQL2000,但即将转到SQL2008,所以如果有一个当代解决方案,我将采用它 存储过程中查询的表如下所示 ID ... fooFlag barFlag bazFlag quuxFlag -- ------- ------- ------- -------- 01 1 0 0

好的,第无数个条件列问题:

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

存储过程中查询的表如下所示

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'
,我将返回第1行和第4行

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


或者,我可以拥有查询的4个副本,并在CASE语句中从中进行选择。这使得SQL代码可以直接执行(并受语法hilighting等的约束),但代价是重复大量代码,因为我不能仅在WHERE子句上使用大小写


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

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

您可以通过案例陈述来实现这一点

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''
还有更多,但我认为其中一个会让您继续。

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

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

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

可以为每个可能的标志列指定一个参数,然后检查该参数是否为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后跳过存储过程的理想选择。

我要做的是在开始时使用
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))
声明
@fooFlag int,
@barFlag int,
@巴齐特,
@quuxFlag int
当@flag='foo'时,设置@fooFlag=CASE,然后设置1个ELSE NULL END
当@flag='bar'时设置@barFlag=CASE,然后设置1 ELSE NULL END
当@flag='baz'时,设置@bazFlag=CASE,然后设置1 ELSE NULL END
当@flag='quux'时设置@quuxFlag=CASE,然后设置1个ELSE NULL END
选择ID、名称、说明等。。。
从myTable
其中(fooFlag>=ISNULL(@fooFlag,0)和fooFlag=ISNULL(@barFlag,0)和barFlag=ISNULL(@bazFlag,0)和bazFlag=ISNULL(@quuxFlag,0)和quuxFlag
,其中
当@value0然后字段else 1结束时的情况
=
当@value0然后@value else 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存在时,基于它的过滤将完成,否则,过滤将被跳过。

“exec uspMyProc@flag='foo'我将返回第1行。”为什么只选择第1行而不选择第4行?+1:这将是我的方法,尽管我仍然使用dynamic,这样就不会在每次查询运行时都拖拽所有OR。是的,这是我的首选解决方案。我选择了Gabriel的答案tho,因为他给了我很多选择,包括这一个。如果性能不是主要问题,我会这么做(即,标志列未编制索引).不,我真的希望避免多个参数--如果扩展到更多标志,则需要更多的维护。使用单个参数,维护仅限于SP;如果添加参数,我也必须更改调用代码的结构,而不仅仅是在现有参数中传递新值。谢谢,tho!哦,是的--忘了提及,尽管我正在移动数据库对于SQL2008,我的应用程序是ASP Classic,我不会很快将应用程序的这一部分移动到.NET。因此LINQ退出了…+1-就个人而言,我会使用
CASE
方法,因为我认为这是最简洁的方法,尽管我更可能使用简单的CASE而不是搜索的CASE,因为要匹配的对象是常量(即当“foo”时为CASE@flag,当“bar”时为fooFlag,当“bar”时为barFlag END=1
)@格雷格:当然,这可能是所有选项中性能最差的一个,因为你现在不仅在一个函数中包装一列,而且在包装所有的列。如果你在每列上都有不同的索引,那么这就变成了表扫描。+1-取决于查询的复杂性。虽然根据我的经验,即使在查询开始时ff simple(在维护等过程中)结果总是很复杂,我希望我从动态sql开始。此外,如果您发现查询长度超过4000个字符,请使用NVARCHAR(最大),这将为您提供2GB大小(sql 2008)关于使用动态sql的另一个注意事项是:如果尝试串联,两个变量的大小必须相同这并不保证高性能索引查找,因为它将重新计算barFlag>=ISNULL(@barFlag,0)每次。@barFlag只是一个标量变量,而不是表列。它是可搜索的-您可以将整个表达式扩展为8个单独的常量标志。请试一试。抱歉,Aaron,我使用的查询是is NULL或..表达式:)对数值表达式求值似乎不是这样的。对不起,不是这样的。没有任何东西需要重新求值,在查询开始时对
ISNULL
求值一次。@Russel,别担心,这个东西也让我困惑了很长时间!在代码周围添加一些文本解释将有助于询问者。
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)
                )