WHERE 子句中的执行顺序不允许 varchar AS 日期的 CAST

Order of execution in WHERE clause not allowing CAST of varchar AS date

提问人:CGSD 提问时间:9/14/2022 最后编辑:Dale KCGSD 更新时间:9/14/2022 访问量:124

问:

我有如下表格:

名称:VW_Questionnaire

问卷 Id (int) (pk) QuestionId (整数) 问题 (varchar) 响应 (varchar) 标记 (varchar)
1 1 你有多少节课? 4
2 2 你上课的第一天是什么时候? 2022-09-01 SP1系列
3 1 你有多少节课? 9
4 2 你上课的第一天是什么时候? 2022-10-01 SP1系列

以下代码有效:

SELECT Question, Response
From VW_Questionnaire
WHERE QuestionId = 2
      AND CAST(Response AS Date) < SELECT (CURRENT_TIMESTAMP)

以下代码引发错误:

SELECT Question, Response
From VW_Questionnaire
WHERE Tag = 'SP1'
      AND CAST(Response AS Date) < SELECT (CURRENT_TIMESTAMP)

从字符串转换日期和/或时间时转换失败。

似乎在第一个示例中,它首先过滤到 WHERE QuestionId = 2,然后它可以成功地将响应 CAST AS Date 强制转换为日期。

但是,在第二个示例中,它似乎在筛选到 WHERE Tag = 'SP1' 之前尝试 CAST Response AS Date。

我什至尝试使用 CTE 执行此操作,其中在 CTE 中,我对 WHERE Tag = 'SP1' 进行过滤,然后从这个过滤表和 CAST 响应 AS 日期中进行选择。我仍然遇到转换错误。

知道为什么会这样吗?

sql-server

评论

0赞 Dale K 9/14/2022
你知道你可以只使用而不是对吗?CURRENT_TIMESTAMPSELECT (CURRENT_TIMESTAMP)
1赞 Dale K 9/14/2022
您几乎无法控制数据库引擎计算数据的顺序,也不知道它尝试筛选了多少行,因为您不知道首先执行 where 子句的哪一部分。因此,如果数据库中有无效数据,即使您尝试过滤掉该数据,也可能导致此类问题。将数据正确存储在正确的数据类型中,而不是将它们混合在一起,这是一个很好的理由。

答:

1赞 Charlieface 9/14/2022 #1

SQL 是一种声明性语言。这意味着服务器可以自由地对谓词进行重新排序,以便达到索引并提高性能。

因此,不能依赖按从左到右顺序执行的谓词。相反,您可以只使用 ,这将清除任何无效值TRY_CAST

SELECT Question, Response
FROM VW_Questionnaire
WHERE QuestionId = 2
  AND TRY_CAST(Response AS Date) < CURRENT_TIMESTAMP;

请注意,关于重新排序的观点甚至适用于派生的表和视图。例如,在此版本中,您可能仍会收到错误:

SELECT Question, Response
FROM (
    SELECT *
    FROM VW_Questionnaire
    WHERE QuestionId = 2
) v
WHERE CAST(Response AS Date) < CURRENT_TIMESTAMP;

唯一保证从左到右计算的构造是(即使这种保证也仅适用于标量值,而不适用于聚合)。CASE


我强烈建议您首先确保您的数据格式正确,这样您就不需要强制转换它。

评论

0赞 CGSD 9/14/2022
TRY_CAST是一个可行的选项。响应是一个通用字段,它根据许多问题收集数据,这些问题的响应可以从文本响应、日期、布尔值、整数等格式化。回答将始终针对问题类型进行适当的格式设置,但所有回答都属于同一个“响应”字段。也许这个 Response 表有更好的设计,但就目前而言,TRY_CAST允许筛选器根据需要执行。
0赞 Dale K 9/14/2022
@CGSD,是的,更好的(恕我直言)答案表设计是您可能期望的每种答案类型的一列,例如字符串、数字、布尔值、日期时间。这使您可以更好地控制查询等。
0赞 Charlieface 9/14/2022
@CGSD 这也是我建议的(以及确保一次只有一个可能的字段不为空的约束)。在紧要关头,您也可以使用柱子来执行此操作,但那时您仍然需要。CHECKsql_variantTRY_CAST
0赞 CGSD 9/14/2022
@DaleK我同意你的看法,但不确定如何设置这样的东西。我基本上有一个问题表和一个回答表,然后将它们绑定到一个视图中。这些问题由不同的用户重复和多次回答。我创建了一个视图,可以将响应表与问题表相关联,而不必每次都重复问题,而只需引用 QuestionId。也许解决方案是为每个响应数据类型创建大量响应表,然后将所有结果合并到一个视图中?
0赞 Dale K 9/14/2022
我不会使用单独的表,使用单独的列。然后将您期望的答案类型(字符串、数字等)存储为问题的一部分,然后,如果您需要将结果作为一列进行查看,请使用事写表达式来确定要显示的列。