update-statement中的类型转换失败

时间:2015-09-21 13:38:00

标签: sql-server-2012 type-conversion

在给定的场景中,我们有两个表:

  1. ChangeLog:包含"字段"的历史值,存储为varchar(23)。
  2. ViewFields:包含有关"字段"的元信息,例如数据类型,最大值,最小值,显示名称。这适用于验证任务和UI配置。
  3. 以下陈述应更新所有"字段的所有历史值"可转换为" DateTime":

    BEGIN TRAN
    
    UPDATE c
       SET c.OldValue = CONVERT(varchar(23), CONVERT(DATETIME, c.OldValue, 101), 21)
      FROM ChangeLog c
     WHERE c.FieldID IN (   SELECT v.FieldID
                              FROM ViewFields v
                             WHERE FieldDataType IN (4,5,6))
    

    执行语句时,我收到以下错误消息:

      

    Msg 241,Level 16,State 1,Line 5   从字符串转换日期和/或时间时转换失败。

    假设结果中存在不可转换的值,则以下语句也会失败:

    SELECT CONVERT(varchar(23), CONVERT(DATETIME, c.OldValue, 101), 21)
      FROM ChangeLog c
     WHERE c.FieldID IN (   SELECT v.FieldID
                              FROM ViewFields v
                             WHERE FieldDataType IN (4,5,6))
    

    令人惊讶的是,select语句确实可以正常工作。 当我现在尝试再次执行更新语句时,它也可以正常工作。 要再次破坏该声明,请执行 DBCC DROPCLEANBUFFERS

    在我看来,在读取数值之前,应始终首先评估条件。但在这个例子中,这似乎显然不是这种情况。 问题似乎与缓存有关。 执行计划是相同的。

    有人可以解释这里发生了什么吗?

    可能的解决方案是使用游标更新值,但在我看来,这不是一个干净的解决方案。

2 个答案:

答案 0 :(得分:1)

显然,答案在于sql server引擎如何处理查询。概念查询处理顺序为:

 1. from 
 2. where
 3. group by
 4. having
 5. select
 6. order by

但实际上引擎可以决定改变这种排序。它可以先评估select子句,然后从where子句应用过滤器。这取决于许多因素。所以实际上这就是你的情况。

解决方法是首先将数据选择到临时表中,然后将强制转换应用于临时表中的数据。

答案 1 :(得分:0)

将日期时间转换为varchar时,无需指定样式参数。

考虑这个CONVERT(varchar(23), CONVERT(DATETIME, c.OldValue, 101), 21)

与此CONVERT(varchar(23), CONVERT(DATETIME, c.OldValue, 101))