消息 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')
,它可以使用涵盖expirydate
和date
的索引
您在此处标记了 [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'
)
顺便说一句between
is 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”附近的语法不正确的主要内容,如果未能解决你的问题,请参考以下文章
消息 102,级别 15,状态 1,第 3 行“,”附近的语法不正确 [重复]
消息 102,级别 15,状态 1,过程 CaDataGroup_Insert,第 88 行 '=' 附近的语法不正确
消息 102,级别 15,状态 1,第 1 行“日期”附近的语法不正确。在更新查询中[关闭]
添加新的 Join 语句后 MS-SQL 存储过程引发错误。消息 102,级别 15,状态 1,第 279 行“WHERE”附近的语法不正确