14

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

我正在编写一个存储过程,它接受一个映射到几个标志列之一的输入参数。过滤请求列的最佳方法是什么?我目前正在使用 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

因此,如果我像调用 sproc 一样exec uspMyProc @flag = 'foo'返回第 1 行和第 4 行。

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

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

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

4

6 回答 6

21

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

您可以使用 case 语句来做到这一点。

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''

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

于 2009-12-30T03:02:48.957 回答
4

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

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

select ID, name, description, ...
from myTable
where (@flag = 'foo' and fooFlag = 1) OR (@flag = 'bar' and barFlag = 1) OR ...
于 2009-12-30T03:03:38.597 回答
4

我会做的是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))

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

于 2009-12-30T03:06:06.613 回答
1

您可以为每个可能的标志列设置一个参数,然后检查该参数是否为空或该列中的值是否等于该参数。然后为要检查的标志传入 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 的理想选择。

于 2009-12-30T03:06:00.997 回答
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 存在时,基于它的过滤完成,否则,过滤被跳过。

于 2017-03-06T06:57:13.550 回答
0
where
   case when @value<>0 then Field else 1 end
   =
   case when @value<>0 then @value else 1 end
于 2016-08-16T10:24:58.523 回答