【问题标题】:Why do I get a DATETIME conversion error in TSQL?为什么我在 SQL 中收到 DATETIME 转换错误?
【发布时间】:2013-03-05 10:52:02
【问题描述】:

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

我们有一个相对简单的 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 语句中选择列时,为什么我没有遇到它?

更新

这里是执行计划,虽然我认为它不会有帮助。

【问题讨论】:

  • 运行查询without where子句时first rowmyText列的值是多少?
  • 当您的LIKE 子句保证MyText Date: 开头 时,为什么要CHARINDEX('Date:', MyText)
  • @Kaf:数据类似于:Ping 未达到 11.22.33.44:11.22.33.44 可达 0% 1.尝试日期:31.12.2000 12:34:56 IP:11.22.33.44 它产生这个的不是我的东西。
  • @Damien:我知道,但这就是“开发”的方式,但无论如何转换错误都没有关系。
  • 我认为这是您的数据。很容易确认您是否可以给我们一个 fiddle,它可以在没有 where 的情况下运行并在 where 失败。

标签: tsql datetime type-conversion


【解决方案1】:

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

当您选择时:

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 和子查询不计算在内,因为优化器可以“透视”这样的结构)

【讨论】:

  • 谢谢,这就是我在上面链接的另一个问题的答案中描述的内容。但是,当我根本不使用 WHERE 子句时,我 不会 得到错误。这很奇怪,当我完全省略 WHERE 子句时,与任何一个 WHERE 子句相比,我怎么能得到看似更少的数据?
  • 生成查询计划并将其编辑到您的问题中,以便更详细地了解它。
  • 今晚我会更新问题,因为由于 WTS 网络限制,我无法上传计划截图。
  • 我上传了工作执行计划的图片,导致错误的查询没有生成。
  • 第一个版本太小了——以为你能以某种方式看到原版。我现在分两部分上传。
猜你喜欢
  • 2019-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多