SQL Server CASE语句:表达式只计算一次

4

我可能漏掉了一些显而易见的东西!提前感谢任何帮助。

我正在尝试在嵌入式 SQL 语句中使用 CASE 语句。我只想评估表达式一次,所以我想把表达式放在 CASE 部分,然后在每个 WHEN 中评估结果。以下是示例:

    SELECT
         MyTable.ColumnA, 
         CASE DateDiff(d, MyTable.MyDate, getDate())    
                WHEN <= 0 THEN 'bad'
                WHEN BETWEEN 1 AND 15 THEN 'reasonable'
                ELSE 'good'
         END as MyCalculatedColumn,
         MyTable.SomeOtherColumn

我知道我可以做到这一点:
 CASE 
        WHEN DateDiff(d, MyTable.MyDate, getDate()) <= 0 THEN 'bad'
        WHEN DateDiff(d, MyTable.MyDate, getDate()) BETWEEN 1 AND 15 THEN 'reasonable'
        ELSE 'good'
 END

但在我的第一个例子中,SQL似乎不喜欢这个语句:

WHEN <= 0 THEN 'bad'

请注意,该语句与其他 SQL 语句一样是内联的,因此我无法执行以下操作:
DECLARE @DaysDiff bigint
SET @DaysDiff = DateDiff(d, MyTable.MyDate, getDate())  

 CASE @DaysDiff    
        WHEN <= 0 THEN 'bad'
        WHEN BETWEEN 1 AND 15 THEN 'reasonable'
        ELSE 'good'
 END

我的实际DateDiff表达式更加复杂,我只想维护其逻辑,并且只有在必要的时候才将其计算一次。

再次感谢...


在使用格式 CASE expression WHEN value THEN ... 时,您需要使用标量值。相反,您将不得不使用 CASE WHEN Boolean Expression THEN... - Thom A
1
CASE expression WHEN value 只能进行相等性检查。 - codeulike
是的,codeulike,这似乎是我遇到的限制。 - DanielG
3个回答

2
你可以使用apply来实现这个目的:最初的回答
 SELECT MyTable.ColumnA, 
        (CASE WHEN day_diff <= 0 THEN 'bad'
              WHEN BETWEEN 1 AND 15 THEN 'reasonable'
              ELSE 'good'
         END) as MyCalculatedColumn,
        MyTable.SomeOtherColumn
FROM MyTable CROSS APPLY
     (VALUES (DateDiff(day, MyTable.MyDate, getDate()))) v(day_diff)

APPLY是一种非常方便的方式,可以将计算值添加到语句中。由于它们在FROM子句中定义,因此可以在SELECTWHEREGROUP BY子句中使用,而列别名则无法被识别。

“最初的回答”翻译为英文是Original Answer


哇,Gordon,这个像冠军一样运行了。这是非常有用的信息,不仅适用于这种情况,也适用于我经常遇到的其他情况。我有几个其他的INNER JOIN和一些LEFT JOIN,它们也被用作DateDiff中的变量别名,但只需将CROSS APPLY放在所有连接的末尾,它就完美地工作了。我查看了执行计划,并且正如你建议的那样,由于它实际上在FROM子句中,因此只评估一次并可用于每一行。太棒了!非常感谢。 - DanielG
1
我以前没见过 VALUES,很有趣 https://learn.microsoft.com/en-us/sql/t-sql/queries/table-value-constructor-transact-sql?view=sql-server-2017 - codeulike

1
我看到了你的问题。 CASE expression WHEN value 只能进行相等性检查。
你可以尝试使用CTE(公共表达式),在CTE中除了CASE语句之外的所有操作都完成,然后将CASE放在最后的SELECT语句中。我不确定它是否会防止表达式被评估两次 - 这是优化器的问题,而不是你的问题(这就是我喜欢思考的方式)。
     WITH cteMyComplexThing AS(
            SELECT MyTable.ColumnA, 
                DateDiff(d, MyTable.MyDate, getDate()) as ComplexThing,
                MyTable.SomeOtherColumn
            FROM MyTable
     ) 
     SELECT
          ColumnA, 
          CASE    
            WHEN ComplexThing <= 0 THEN 'bad'
            WHEN ComplexThing BETWEEN 1 AND 15 THEN 'reasonable'
            ELSE 'good'
           END as MyCalculatedColumn,
          SomeOtherColumn
     FROM cteMyComplexThing

这是一个非常好的想法。最坏的情况下,它允许我在一个位置上维护DateDiff函数的复杂性。我很感激。 - DanielG

0
CASE语句中的WHEN子句需要条件的两个方面。单独使用“<=0”是不行的。
 CASE @DaysDiff    
    WHEN ? <= 0 THEN 'bad'
    WHEN BETWEEN 1 AND 15 THEN 'reasonable'
    ELSE 'good'
 END

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接