【问题标题】:Complex WHERE expression using CASE-THEN in SQL 2008SQL 2008 中使用 CASE-THEN 的复杂 WHERE 表达式
【发布时间】:2009-09-03 19:20:42
【问题描述】:

我有一个问题:

SELECT ID FROM requests WHERE date <operator> <expression>

我需要根据存储过程的参数更改&lt;expression&gt;

如果设置了标志:WHERE date BETWEEN GETDATE() - 7 AND GETDATE()

如果不是:WHERE date = date

我尝试使用 CASE-THEN,但它强制使用具体运算符,但我必须有几个:

SELECT ID FROM requests WHERE date = -- here it is
CASE
  WHEN @flag = 1 THEN ..
  ELSE date
END

【问题讨论】:

  • 从请求中选择 ID 怎么样?如果@flag = 1,那么日期在 GETDATE() - 7 AND GETDATE() ELSE 1 = 1 END 之间?
  • @shahkalpesh 您能否将其发布为带有突出显示的答案?

标签: sql tsql sql-server-2008


【解决方案1】:
select 
    id
from
    requests
where
    (
        flag = 1
        and
        date between getdate()-7 and getdate()
    )
    or
    (
        flag <> 1
        and
        date = date
    )

【讨论】:

  • @kurosch:很好地使用 OR。当 flag 1. 时,可以排除 date = date
  • @Remus:这是一个新的流行词“unsargable”。在维基百科上查到了。 "sargable" = 便于使用索引的搜索参数。我现在明白你所说的“不可饶恕”是什么意思了。
  • @Remus:如果@flag 为 1,则将使用 [名称不佳] 日期列上的索引(假设存在) - 否?
  • @rexem:查询优化器最终可能会选择满足 both 条件 date between... and date=date 的计划并决定执行扫描。虽然在这种情况下,您可能可以使用OR,但在这种情况下,正确的做法是始终检查普通 IF 中的 @flag 并在每个分支上进行单独的查询,以便优化器确切地知道您在每种情况下想要什么。
  • 我其实是Oracle人,对sqlserver to tsql一无所知
【解决方案2】:
SELECT ID FROM requests WHERE 
    (date = date and @flag <> 1 ) 
    or ( date BETWEEN GETDATE() - 7 AND GETDATE() and @flag = 1)

或者在 Transact-SQL 中

if @flag = 1
    then
     SELECT ID FROM requests WHERE date = date
    else
     SELECT ID FROM requests WHERE date BETWEEN GETDATE() - 7 AND GETDATE()

【讨论】:

    【解决方案3】:

    您可以使用旧的 IF 语句来简化它

    if @flag = 1
       SELECT ID FROM requests WHERE date BETWEEN GETDATE() - 7 AND GETDATE()
    else
       SELECT ID FROM requests 
    

    编辑:我不确定它是否有效。但是,这是我在评论中添加的内容

    SELECT ID FROM requests 
    WHERE 
    CASE WHEN @flag = 1 THEN date BETWEEN GETDATE() - 7 AND GETDATE() 
    ELSE 1 = 1 
    END
    

    EDIT2:我认为使用 IF 语句(如果它在 TSQL 中)是有意义的。因为,条件依赖于外部变量而不是行中的东西

    即CASE 将对每一行的@flag 进行比较,这不是必需的。

    【讨论】:

    • WHERE 子句中的 CASE 不起作用,或者它在 SQL Server 2005 上因“关键字 BETWEEN 的语法不正确”而失败。
    • @rexem:我没有方便的 SQL 服务器来尝试。我猜这可能是由于 DATE 作为字段的名称?
    • @Shak:不,我正在使用名为“created”的日期时间列进行测试。我没有使用 2008,但它与 OPs cmets 不让它工作一致。
    • 第二个代码范围在我的 SQL 2008 Express 上也不起作用
    【解决方案4】:

    正如您所发现的,您不能在 WHERE 子句中使用 CASE 语句。您的替代方案是:

    选项 1:WHERE 子句

    SELECT t.id 
      FROM REQUESTS t
     WHERE 1 = 1
        OR (@flag = 1 AND t.date BETWEEN GETDATE() - 7 AND GETDATE())
    

    选项 2:使用 IF 并有两个(或更多,视情况而定)不同的 SQL 语句

    IF @flag = 1
       SELECT t.id 
         FROM REQUESTS t
    ELSE
       SELECT t.id 
         FROM REQUESTS t
        WHERE t.date BETWEEN GETDATE() - 7 AND GETDATE())
    

    选项 3:动态 SQL(每个人都喜欢)

    DECLARE @SQL nvarchar (4000)
    
    SET @SQL = 'SELECT t.id 
                  FROM REQUESTS t 
                 WHERE 1 = 1'
    
    IF @flag = 1 
      SET @SQL = @SQL + ' AND t.date BETWEEN GETDATE()-7 AND GETDATE()'
    
    EXEC sp_executesql @SQL 
    

    就个人而言,我不愿意处理选项 2。几乎相同的语句让我想重构为单个 SQL 查询。也就是说,如果事情不太复杂,请使用#1。否则,使用动态。

    【讨论】:

    • 您能否描述一下您在 option1 中使用 1=1 的目的是什么?不幸的是 option2 不适用于我的情况,因为原始查询比我的 OP 中的要大得多。我不相信(可能)动态 SQL 的优化,所以更喜欢使用纯 t-sql
    • 我必须在选项 1 中使用 1 = 1,因为如果标志 != 1 则不会返回任何数据。我习惯在动态 SQL 中使用它,以防我需要附加到 WHERE条款。这比使用 t.date = t.date 更好,因为这将使用索引来处理始终正确的情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-12
    相关资源
    最近更新 更多