SQL Server奇怪的错误从字符串

时间:2017-03-29 15:10:09

标签: sql sql-server

这很奇怪。我有一个varchar(8000)类型的列。我们会在其中保存各种数据,如数字,文本,日期等。

我编写了一个简单的选择查询,以便从表格中获取所有“日期”值,我也可以使用CASTConvert以此格式2014-12-16 00:00:00.000获取我的值。

现在我正在尝试添加where子句来过滤今年的信息,但是我收到错误:

  

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

当我在我的选择查询中使用TOP 10000时,错误消失了。这真的很奇怪

; WITH TempTable AS 
(
    SELECT 
        ID, CAST(Value AS DateTime) [SomeDate]
    FROM 
        SampleTable
    WHERE
        ColType = 'Date'
)
SELECT * 
FROM FROM TempTable 
WHERE [SomeDate] BETWEEN '1/1/2017' AND '12/30/2017'

抛出

  

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

; WITH TempTable AS 
(
    SELECT TOP 100000 
        ID, CAST(Value AS DateTime) [SomeDate]
    FROM 
        SampleTable
    WHERE
        ColType = 'Date'
)
SELECT * 
FROM FROM TempTable 
WHERE [SomeDate] BETWEEN '1/1/2017' AND '12/30/2017'

这很好用。请注意我的结果中只有25行。前100000只是我用过的一大数字。我的TOP关键字如何使我的查询变得很好,这很奇怪。

更新:

以下是我不使用TOP关键字进行解析的方法。我把我的查询分开了一点,如下所示。谢谢大家的时间和意见。

;WITH GetAllIDs
AS (
    SELECT ID
    FROM SampleTable
    WHERE ColType = 'Date'
    )
    ,FinalTable
AS (
    SELECT ID
        ,(
            SELECT Cast([Value] AS DATETIME)
            FROM SampleTable dt
            WHERE dt.ID = tt.ID
                AND dd.ColType = 'Date'
            ) [SomeDate]
    FROM GetAllIDs tt
    )
SELECT *FROM FinalTable
WHERE [SomeDate] BETWEEN '1/1/2017' AND '12/30/2017'

3 个答案:

答案 0 :(得分:2)

SQL语句中没有执行顺序。因此,where子句(必然)在转换值之前过滤行。因此,你的问题。

在SQL Server 2012+中,有一个使用try_convert()的简单解决方案:

With TempTable AS (
      select ID, try_convert(datetime, Value) as SomeDate
      from SampleTable
      where ColType = 'Date'
     )
Select * 
from TempTable 
Where SomeDate between '2017-01-01' and '2017-12-30';

Microsoft认为此行为是优化程序的功能,而不是错误(我不同意)。它为优化查询提供了更多机会。在这种情况下,简单的转换是在读取数据时完成的,而不是在处理管道的下游。

请注意,我还将日期常量更改为YYYY-MM-DD格式。您应始终使用YYYY-MM-DD或YYYYMMDD作为格式。

添加TOP时这种情况消失的事实是执行计划的一些奇怪的工件。出于某种原因,这将在评估表达式之前进行过滤。我知道TOP有时会产生这种影响。我在这种情况下感到惊讶,因为查询很简单。

编辑:

在SQL Server 2008中,您可以使用case。像这样:

With TempTable AS (
      select ID, (case when isdate(value) = 1 then convert(datetime, Value) end) as SomeDate
      from SampleTable
      where ColType = 'Date'
     )
Select * 
from TempTable 
Where SomeDate between '2017-01-01' and '2017-12-30';

case表达式保证了参数的评估顺序。仅在when

之后评估then

答案 1 :(得分:0)

基于SQL 2008,您必须更改模式。

首先,为什么它适用于TOP命令。 TOP选择查询中与WHERE子句匹配的所有记录,直到TOP的限制。由于您具有WHERE子句,因此具有TOP的版本仅将具有Coltype为“Date”的记录传递到日期检查。如果没有顶部,优化器会认为根据日期范围检查Value列的值将比过滤列类型更快,因为只需要检索一个数据元素来检查“加速”(在这种情况下,吹up)查询。

应该使用的一个选项是将TempTable定义代码更改为:

; WITH TempTable AS 
(
    SELECT TOP 100 PERCENT
        ID, CAST(Value AS DateTime) [SomeDate]
    FROM 
        SampleTable
    WHERE
        ColType = 'Date'
)

另一个明显的选择是将所有值提取到实际的临时表,而不是直接使用CTE,然后查询该表。

答案 2 :(得分:0)

请参阅我的更新:这是我最终解决问题的方法。感谢Gordon,Laughing Vergil的意见。