0

我知道关于这个话题有很多问题,甚至是我不久前问过自己的一个(这里)。现在我遇到了一个不同的问题,我自己和我的同事都不知道这种奇怪行为的原因是什么。

我们有一个相对简单的 SQL 语句,如下所示:

SELECT
    CONVERT(DATETIME, SUBSTRING(MyText, CHARINDEX('Date:', MyText) + 8, 16) AS MyDate,
    SomeOtherColumn,
    ...
FROM
    MyTable
INNER JOIN MyOtherTable
    ON MyTable.ID = MyOtherTable.MyTableID
WHERE
    MyTable.ID > SomeValue AND
    MyText LIKE 'Date: %'

这不是我的数据库,也不是我的 SQL 语句,而且我没有创建出色的模式来将日期时间值存储在 varchar 列中,所以请忽略这一点。

我们现在面临的问题是 SQL 转换错误 241(“从字符串转换日期和/或时间时转换失败。”)。

现在我知道查询优化器可能会更改执行计划,在尝试转换后可以使用 WHERE 子句过滤结果,但真正奇怪的是,当我删除所有 WHERE 子句时,我没有收到任何错误.

当我在上面的语句中添加一行时,我也没有收到任何错误,如下所示:

SELECT
    MyText, -- This is the added line
    CONVERT(DATETIME, SUBSTRING(MyText, CHARINDEX('Date:', MyText) + 8, 16) AS MyDate,
    ...

一旦我删除它,我就会再次收到转换错误。手动检查 MyText 列中的值而不尝试转换它们并不表明存在任何可能导致问题的记录。

转换错误的原因是什么?当我还选择列作为 SELECT 语句的一部分时,为什么我没有遇到它?

更新

这里是执行计划,虽然我认为它不会有帮助。 第1部分 在此处输入图像描述

4

1 回答 1

1

有时,SQL Server 会通过在流程中更早地推送转换操作来进行积极优化,而不是它们原本需要的时间。(不应该。例如,请参阅SQL Server 不应在 Connect 上引发不合逻辑的错误)。

当您选择:

CONVERT(DATETIME, SUBSTRING(MyText, CHARINDEX('Date:', MyText) + 8, 16)

然后优化器决定它可以作为表/索引扫描或查找的一部分执行此转换-WHERE就在它从表中读取数据的时候(重要的是,在子句过滤器之前或同时)。然后查询的其余部分可以只使用转换后的值。

当您选择:

MyText, -- This is the added line
CONVERT(DATETIME, SUBSTRING(MyText, CHARINDEX('Date:', MyText) + 8, 16)

它决定让转换稍后发生。重要的是,现在(偶然)的转换发生在子句过滤器之后WHERE,按权利,子句过滤器应该在尝试转换之前过滤所有行。


处理此问题的唯一安全方法是在尝试转换之前强制过滤绝对发生。如果您不处理聚合,CASE则表达式可能足够安全:

SELECT CASE WHEN MyText LIKE 'Date: %' THEN CONVERT(DATETIME, SUBSTRING(MyText, CHARINDEX('Date:', MyText) + 8, 16) END

否则,更安全的选择是将查询拆分为两个单独的查询,并将中间结果存储在临时表或表变量中(视图、CTE 和子查询不计算在内,因为优化器可以“看穿”此类构造)

于 2013-03-05T11:04:34.657 回答