消息 156,级别 15,状态 1,第 7 行关键字“BETWEEN”附近的语法不正确

Posted

技术标签:

【中文标题】消息 156,级别 15,状态 1,第 7 行关键字“BETWEEN”附近的语法不正确【英文标题】:Msg 156, Level 15, State 1, Line 7 Incorrect syntax near the keyword 'BETWEEN' 【发布时间】:2021-09-27 01:28:38 【问题描述】:

当我运行这个查询时

SELECT number, id, clientid, hash, 
    CASE WHEN expirydate IS NULL THEN 
            date 
        ELSE expirydate 
    END as date, 
    CASE company WHEN '' THEN 
            (
                SELECT CONCAT_WS(' ', firstname, lastname) 
                FROM tblcontacts 
                WHERE userid = tblclients.userid 
                and is_primary = 1
            ) 
        ELSE company END as company 

FROM "tblestimates" 
    LEFT JOIN tblclients ON tblclients.userid=tblestimates.clientid
WHERE status != 3 
AND status != 4 
AND CASE WHEN expirydate IS NULL THEN 
        (date BETWEEN '2021-06-27' AND '2021-08-08') 
    ELSE (expirydate BETWEEN '2021-06-27' AND '2021-08-08') 
    END

我收到了这个错误:

消息 156,第 15 级,状态 1,第 7 行 关键字“BETWEEN”附近的语法不正确。

【问题讨论】:

我也强烈建议您养成格式化文本的习惯;由于缺少 SQL,您的 SQL 难以阅读。 至于错误,CASE是一个表达式而不是语句,它返回一个标量值,而不是一个布尔结果。但是,实际上,您不应该在WHERE 中使用CASE 表达式;坚持AND OR逻辑。 提示:coalesce(expirydate, date) as date. 即使您让CASE 工作,您也可能最终会进行全表扫描。此查询可以替换为更简单 expirydate BETWEEN '202106-27' AND '202108-08' OR ( expirydate is NULL and date BETWEEN '20210627' AND '20210808'),它可以使用涵盖expirydatedate 的索引 您在此处标记了 [sql-server2008],但您使用的是 CONCAT_WS。这是在 SQL Server 2017 中引入的;以上会因为使用它而失败。您真的在使用完全不受支持的 SQL Server 版本吗? 【参考方案1】:

CASE 语句总是解析为某个结果值,因此逻辑上这将评估为“真”或“假”:

CASE
    WHEN expirydate IS NULL THEN 
        (date BETWEEN '2021-06-27' AND '2021-08-08') 
    ELSE
        (expirydate BETWEEN '2021-06-27' AND '2021-08-08') 
END

不幸的是,SQL Server 没有布尔数据类型,因此您不能将比较结果直接用作表达式。相反,您需要让 CASE 语句返回支持的类型,例如 BIT:

CASE 
    WHEN expirydate IS NULL AND date BETWEEN '2021-06-27' AND '2021-08-08'
        THEN 1
    WHEN expirydate IS NOT NULL AND expirydate BETWEEN '2021-06-27' AND '2021-08-08'
        THEN 1
    ELSE 0
END

然后您可以比较该结果以在 WHERE 子句中创建表达式:

WHERE 1 =
    CASE 
        WHEN expirydate IS NULL AND date BETWEEN '2021-06-27' AND '2021-08-08'
            THEN 1
        WHEN expirydate IS NOT NULL AND expirydate BETWEEN '2021-06-27' AND '2021-08-08'
            THEN 1
        ELSE 0
    END

但是,这种组合可以更自然(并且可能更有效)用 OR 编写,如果与 AND 组合,请记住将其放在括号中:

WHERE 
  (
     expirydate IS NULL AND date BETWEEN '2021-06-27' AND '2021-08-08'
     OR
     expirydate IS NOT NULL AND expirydate BETWEEN '2021-06-27' AND '2021-08-08'
  )

在这种特殊情况下,您可以使用 COALESCE 更简单地编写它,因为您要比较的不是空的列:

WHERE
    COALESCE(expirydate, date) BETWEEN '2021-06-27' AND '2021-08-08'

【讨论】:

这不是一个好主意。这将阻止使用索引 @PanagiotisKanavos 更新了一些变化。我认为在进行更广泛的更改之前了解错误和最直接的等价物很有用。【参考方案2】:

不要在where 子句中使用case。这是不寻常且不必要的

AND 
(
      (expirydate IS NULL AND [date] BETWEEN '2021-06-27' AND '2021-08-08')
   OR expirydate BETWEEN '2021-06-27' AND '2021-08-08'
)

顺便说一句betweenis problematic。更好地使用(注意我的日期更改为2021-08-09

AND 
(
      (expirydate IS NULL AND [date] >= '2021-06-27' AND [date] < '2021-08-09')
   OR expirydate >= '2021-06-27' AND expirydate < '2021-08-09'
)

【讨论】:

expirydate IS NOT NULL 不需要,因为如果值为 NULL,BETWEEN 将失败。我很确定优化器会消除这个 如果对 OP 来说不是很明显,则应将整个子句括起来以将其包含在查询中。

以上是关于消息 156,级别 15,状态 1,第 7 行关键字“BETWEEN”附近的语法不正确的主要内容,如果未能解决你的问题,请参考以下文章

关键字“FOR”XML 附近的语法不正确

消息 102,级别 15,状态 1,第 3 行“,”附近的语法不正确 [重复]

关键字“read”附近的语法不正确

消息 102,级别 15,状态 1,过程 CaDataGroup_Insert,第 88 行 '=' 附近的语法不正确

消息 102,级别 15,状态 1,第 1 行“日期”附近的语法不正确。在更新查询中[关闭]

添加新的 Join 语句后 MS-SQL 存储过程引发错误。消息 102,级别 15,状态 1,第 279 行“WHERE”附近的语法不正确