【问题标题】:Datetime 'Conversion failed' error when using CASE comparison使用 CASE 比较时出现日期时间“转换失败”错误
【发布时间】:2016-02-17 19:02:36
【问题描述】:

我有一个存储字段/值对的表格以供比较,例如:

ID    FieldName    ValueA      ValueB
----  ----------   -------     ------
1     Name         ABC         XYZ
1     StartDate    2015-05-04  2015-06-05
1     Amount       4           4
2     Name         DEF         UVW
2     StartDate    2015-03-22  2015-02-11
2     Amount       4           4

我正在尝试比较 ValueA 和 ValueB 的值,寻找不匹配的值。由于它们在表中存储为 varchar(max),因此有时会在不应该出现的地方出现不匹配,即

ID    FieldName    ValueA      ValueB
----  ----------   -------     ------
3     StartDate    2015-01-30  01/30/2015

所以我使用CASE 语句在比较之前转换数据类型,即

Select *
From ComparisonTable
where
(case when FieldName = 'StartDate' then CAST(ValueA as datetime)
      when FieldName = 'Amount' then CAST(ValueA as int)
      else CAST(ValueA as varchar(max))
 end)
=
(case when FieldName = 'StartDate' then CAST(ValueB as datetime)
      when FieldName = 'Amount' then CAST(ValueB as int)
      else CAST(ValueB as varchar(max))
 end)

我收到一个错误

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

我认为这是因为某处输入了错误的 StartDate 值。但是,我进行了以下搜索:

Select *
From ComparisonTable
where FieldName = 'StartDate'
and
(ISDATE(ValueA) = 0
OR
ISDATE(ValueB) = 0)

对我来说,没有任何结果表明所有日期值都已正确存储。

我想知道是否有其他人遇到过类似的问题,或者 SQL/SQL Server 中是否存在不允许在 WHERE/CASE 语句中进行这些类型的数据类型转换的东西?

更新 我尝试在一个简单的SELECT 中使用相同的CASE

SELECT
    (case when FieldName = 'StartDate' then CAST(ValueA as datetime)
          when FieldName = 'Amount' then CAST(ValueA as int)
          else CAST(ValueA as varchar(max))
     end)
    =
    (case when FieldName = 'StartDate' then CAST(ValueB as datetime)
          when FieldName = 'Amount' then CAST(ValueB as int)
          else CAST(ValueB as varchar(max))
     end)
WHERE FieldName IN ('StartDate','Amount')

一切都以日期时间的形式返回,这意味着即使 int 值也被转换为日期,这让我相信这种类型的 CASE 转换是不允许的。尽管CASE 说要将“名称”字段转换为varchar,但它会将所有内容都转换为日期时间。

如果这是标准 SQL Server 行为,是否有任何解决方法可用于进行这些转换?

【问题讨论】:

  • 嗯...将Amount 转换为Datetime 似乎很奇怪...因为在您的示例中它的值是4...也许您的意思是整数或数字?建议:仔细检查“金额”的预期数据类型
  • 您不能在 case 表达式中混合这样的数据类型。它将遵循数据类型优先规则并尝试将所有内容转换为日期时间。这是使用 EAV 的具有挑战性的部分之一,并且做得不好。在您的查询中,您必须将所有内容转换为字符数据类型才能使其正常工作。
  • @xQbert 这是一个复制/粘贴错字。他们正在转换为int

标签: sql sql-server datetime case type-conversion


【解决方案1】:

我明白您要做什么,但是您不能对这样的单个值使用多种类型。它们都需要是相同的数据类型才能按照您尝试的方式进行。

这应该可以满足您的要求,但这会导致性能下降,因为没有索引可用于此类比较。

Select  *
From    ComparisonTable
Where   
(
    FieldName = 'StartDate'
And Cast(ValueA As DateTime) = Cast(ValueB As DateTime)
)
Or
(
    FieldName = 'Amount'
And Cast(ValueA As Int) = Cast(ValueB As Int)
)
Or
(
    FieldName Not In ('StartDate', 'Amount')
And Cast(ValueA As Varchar (Max)) = Cast(ValueB As Varchar (Max))
)

另一种选择是在数据类型转换后将所有内容转换为VARCHAR

Select  *
From    ComparisonTable
Where   
(
    case    
        when FieldName = 'StartDate' then CAST(CAST(ValueA as datetime) As Varchar (Max))
        when FieldName = 'Amount' then CAST(CAST(ValueA as int) AS Varchar (Max))
        else CAST(ValueA as varchar(max))
    end
)
=
(
    case 
        when FieldName = 'StartDate' then CAST(CAST(ValueB as datetime) AS Varchar(Max))
        when FieldName = 'Amount' then CAST(CAST(ValueB as int) As Varchar (Max))
        else CAST(ValueB as varchar(max))
    end
)

【讨论】:

    【解决方案2】:

    我想出的一个解决方案是使用UNION ALL 来区分不同的类型:

    SELECT *
    FROM ComparisonTable
    WHERE FieldName = 'StartDate'
    AND CAST(ValueA as datetime) = CAST(ValueB as datetime)
    UNION ALL
    SELECT *
    FROM ComparisonTable
    WHERE FieldName = 'Amount'
    AND CAST(ValueA as int) = CAST(ValueB as int)
    UNION ALL
    SELECT *
    FROM ComparisonTable
    WHERE FieldName = 'Name'
    AND CAST(ValueA as varchar(max)) = CAST(ValueB as varchar(max))
    

    它有效,但我会将其与其他方法进行比较以提高效率。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多