SQL Server DATE转换错误

时间:2015-11-15 19:12:50

标签: sql sql-server sql-server-2012

这是我的问题:

SELECT 
    *
FROM 
    (SELECT  
         A.Name, AP.PropertyName, APV.Value AS [PropertyValue],
         CONVERT(DATETIME, APV.VALUE, 101) AS [DateValue]
     FROM dbo.Account AS A
     JOIN dbo.AccountProperty AS AP ON AP.AccountTypeId = A.AccountTypeId
     JOIN dbo.AccountPropertyValue AS APV ON APV.AccountPropertyId = APV.AccountPropertyId
                                          AND APV.AccountId = A.AccountId
     WHERE 
         A.AccountTypeId = '19602AEF-27B2-46E6-A068-7E8C18B0DD75'  --VENDOR
         AND AP.PropertyName LIKE '%DATE%'
         AND ISDATE(APV.Value) = 1 
         AND LEN(SUBSTRING( REVERSE(APV.Value), 0 , CHARINDEX( '/',     REVERSE(APV.Value)))) = 4  --ENSURE 4 digit year
     ) AS APV
WHERE 
    APV.DateValue < GETDATE()

导致以下错误:

  

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

如果您注释掉WHERE APV.DateValue < GETDATE()子句,则没有错误,我得到300多行。当我启用WHERE子句时,我收到错误。

所以你要告诉我我的数据是否正确?这就是我的想法,所以我试图弄清楚数据中的问题所在,所以我开始使用TOP()来隔离位置。问题是,一旦我使用TOP()函数,错误消失了,我只有2000行数据开始。所以我在内部TOP(99999999)上放了一个荒谬的SELECT,现在整个查询都有效。

内部SELECT返回相同数量的行,包含或不包含TOP()

WHY ???

仅供参考,这是有效的SQL:

SELECT  
    *
FROM
    (SELECT TOP(99999999)  
         A.Name, AP.PropertyName, APV.Value AS [PropertyValue],
         CONVERT(DATETIME, APV.VALUE, 101) AS [DateValue]
     FROM dbo.Account AS A
     JOIN dbo.AccountProperty AS AP ON AP.AccountTypeId = A.AccountTypeId
     JOIN dbo.AccountPropertyValue AS APV ON APV.AccountPropertyId = APV.AccountPropertyId
                                          AND APV.AccountId = A.AccountId
     WHERE 
         A.AccountTypeId = '19602AEF-27B2-46E6-A068-7E8C18B0DD75'  --VENDOR
         AND AP.PropertyName LIKE '%DATE%'
         AND ISDATE(APV.Value) = 1 
         AND LEN(SUBSTRING(REVERSE(APV.Value), 0 , CHARINDEX( '/',     REVERSE(APV.Value)))) = 4
     ) AS APV
WHERE 
    APV.DateValue < GETDATE()

3 个答案:

答案 0 :(得分:0)

我的猜测是,您的日期是APV.VALUE还包含无法转换为日期的数据,应该使用其他条件过滤掉吗?

由于SQL Server可以决定使用您给出的条件首先限制数据:

APV.DateValue < CONVERT( DATETIME,  GETDATE(),101)

如果有数据无法转换为日期,那么您将收到错误。

为了更清楚,这就是被过滤的内容:

CONVERT( DATETIME, APV.VALUE, 101) AS [DateValue]

如果有任何数据无法使用101格式转换为日期,则使用getdate()的过滤器将失败,即使该行未包含在最终结果集中,例如因为AP.PropertyName不包含DATE

由于您使用的是SQL Server 2012,因此使用try_convert代替转换可以解决您的问题

为什么它适用于顶级?在这种情况下,SQL Server无法使用外部查询中的条件,因为结果可能会更改,因为它可能会影响顶部返回的行数

答案 1 :(得分:0)

您面临的问题是SQL Server可以在查询处理期间随时评估表达式 - 甚至在评估WHERE子句之前。这对性能来说可能是一个很大的好处。但是,结果是错误可以由不在最终结果集中的行生成。 (除零以及转换错误也是如此。)

幸运的是,SQL Server解决了转换问题。使用try_convert()

TRY_CONVERT( DATETIME, APV.VALUE, 101) AS [DateValue]

如果出现问题,则返回NULL而不是错误。

某些版本的工作原因和其他版本的原因并不是因为执行顺序。实际上还没有一种方法可以预测哪些有效,哪些无效 - 如果查询的执行计划因其他原因(例如表统计信息)而发生变化,则可能会发生变化。因此,请使用try_convert()

答案 2 :(得分:0)

因为表中的记录数&lt; 999..99。关于错误,似乎SQL引擎在转换为日期后评估WHERE子句,因此您可以尝试这样做:

SELECT *
FROM (
 SELECT  A.Name
  , AP.PropertyName
  , APV.Value AS [PropertyValue]
  , 
  CASE 
    WHEN SDATE(APV.Value)  = 1
    THEN  CONVERT( DATETIME, APV.VALUE, 101) 
    ELSE NULL
  END AS [DateValue]
 FROM dbo.Account AS A
 JOIN dbo.AccountProperty AS AP
   ON AP.AccountTypeId = A.AccountTypeId
 JOIN dbo.AccountPropertyValue AS APV
   ON APV.AccountPropertyId = APV.AccountPropertyId
  AND APV.AccountId = A.AccountId
WHERE A.AccountTypeId = '19602AEF-27B2-46E6-A068-7E8C18B0DD75'  --VENDOR
  AND AP.PropertyName LIKE '%DATE%'
  AND LEN( SUBSTRING( REVERSE(APV.Value), 0 , CHARINDEX( '/',     REVERSE(APV.Value)))) = 4  --ENSURE 4 digit year
    ) AS APV
WHERE APV.DateValue IS NOT NULL  AND APV.DateValue < GETDATE()