【问题标题】:SQL: Error when converting varchar to datetimeSQL:将 varchar 转换为 datetime 时出错
【发布时间】:2016-12-09 01:17:21
【问题描述】:
WITH Valid_dates (ValidDate)
AS
(
    SELECT '19' + SUBSTRING(column, 0, 3) + '-' + SUBSTRING(column, 3, 2) + '-' + SUBSTRING(column, 5, 2) AS ValidDate
    FROM table
    WHERE SUBSTRING(column, 3, 2) <= '12' --Max month
    AND SUBSTRING(column, 3, 2) >= '01' --Min month
    AND ISNULL(SUBSTRING(column, 3, 2), '') <> '' --Empty string
    AND SUBSTRING(column, 5, 2) <= '31' --Max day
    AND SUBSTRING(column, 5, 2) >= '01' --Min month
    AND ISNULL(SUBSTRING(column, 5, 2), '') <> '' --Empty string
    AND LEN('19' + SUBSTRING(column, 0, 3) + '-' + SUBSTRING(column, 3, 2) + '-' + SUBSTRING(column, 5, 2)) = 10 --Must match 10 character format
)

SELECT CONVERT(DATETIME, ValidDate)
FROM Valid_dates

当我在 SQL Server Management Studio 中执行此查询时,我收到以下错误消息:“将 varchar 数据类型转换为 datetime 数据类型导致值超出范围。”

我已过滤掉所有无效日期。如您所见,2 月 30 日、4 月 31 日、6 月 31 日等不存在的日期本身并没有被过滤掉,但我已经检查了它们,它们不存在。仅运行 CTE 查询会产生大量结果,但我找不到任何差异。

【问题讨论】:

  • 它对任何小数据子集是否有效或失败,例如,只需在一些单条记录上尝试您的 SQL - 如果它总是失败,则说明您输入错误的数据,如果有时失败,那么您可能有一些例如 29/02/1900 之类的无效日期
  • 如果您通过 validdtae 执行 SELECT TOP 1 CONVERT(DATETIME, ValidDate) 订单,如果失败,请尝试通过 validdtae 执行 SELECT TOP 1 ValidDate 订单 - 会发生什么?
  • tablecolumn 的输入(实际内容)是什么?

标签: sql sql-server datetime type-conversion


【解决方案1】:

这是 SQL Server 的问题。它不保证查询中子句的求值顺序where(即使在 CTE 中)在查询的其余部分“之前”被评估是没有意义的。

注意:这是优化器的一个特性。通过重新安排查询的不同组件的评估,优化器可以创建更好的查询计划。

在 SQL Server 2012+ 中,最简单的方法是TRY_CONVERT()

with . . .
select try_convert(datetime, validdate)
from valid_dates;

在早期版本中,您可以使用case

select (case when <all your conditions here>
             then convert(datetime, validdate)
        end)

SQL Server确实保证了case语句在正常情况下的顺序评估(聚合有一些古怪的情况),所以这也解决了这个问题。

【讨论】:

  • 我不信,你是说未选中的数据必须在选中的字段列表中合法转换?我知道生成选定字段的最终列表是最后一步,因此例如在“WHERE”中消除零除数将停止除以零错误
  • @AndrewDeighton 。 . .你是否相信并不重要。这就是 SQL Server 的工作方式。 SQL 优化器保留重新安排处理的权利,这可能会在处理中引入错误。这是一个已知问题,它已经存在了很长时间,并且可能与为什么在 SQL Server 2012 中引入try_convert() 有关。
  • 他只需要一个无效的日期,它不会起作用,我建议他先寻求其他途径,例如如果他检查那一天小于 29 会发生什么 - 会那仍然给出错误?也许你有他的数据集来检查所有这些,否则你的答案几乎是一个猜测——就像我的一样,这就是我告诉他尝试一些东西的原因。如果他将他选择的数据查询到一个临时表中,那么它会全部转换吗?根据你的理论,应该可以。所以是的,我认为这是一个糟糕的答案!
  • 所以 select 子句可能在 where 子句之前被评估,但它总是在它之后执行?
  • 在这种情况下,您可以通过将 CTE 选择到 #temp 中进行试验,然后在 #temp 上单独运行选择 - 这将表明执行操作的顺序存在问题,否则如果它失败了,它会提示数据错误
【解决方案2】:

我很确定这与文化有关。

您没有指定实际输入,从您的代码中我想它是某种未分隔的格式,没有像 920130 这样的年份,意思是 1992 年 1 月 30 日?

这将被转换为1992-01-30

您的默认文化可能是使用类似 yyyy-dd-mm 的文化,这将导致“月”高于 12 的超出范围错误...

尝试使用 102 作为文化键:

SELECT CONVERT(DATETIME, ValidDate,102)
FROM Valid_dates

更新

如果我的假设是正确的,那就更容易尝试了

SELECT TRY_CAST('19' + column AS DATE) FROM table

SQL Server 允许本地转换类似于yyyymmdd 的未分隔字符串。如果无法进行强制转换,使用 TRY_CAST 将返回 NULL

旧版本(

SELECT CASE WHEN ISDATE('19' + column)=1 THEN CAST('19' + column AS DATE) ELSE NULL END FROM table

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-26
    • 1970-01-01
    • 1970-01-01
    • 2021-08-14
    • 2013-06-11
    相关资源
    最近更新 更多