【问题标题】:cast exception in SQL Server when querying derived table查询派生表时 SQL Server 中的强制转换异常
【发布时间】:2014-01-06 22:52:38
【问题描述】:

在尝试回答有关 Stack Overflow 的另一个 SQL Server 问题时,我遇到了一些无法按预期工作的问题。我使用的是 SQL Server 2008R2,但这可能并不重要。

我有一个简单的假设表,其中有一些日期存储为 VARCHAR 数据类型。我知道使用 DATE 数据类型存储日期信息是显而易见且实用的,但此示例有意使用 VARCHAR 来演示我遇到的问题。日期无效的原因并不重要 - 可能是清理不当、更新查询格式错误、发挥您的想象力等。

-- setup
CREATE TABLE #temp (DateString VARCHAR(10)); 
INSERT #temp (DateString) VALUES ('01/01/2013');
INSERT #temp (DateString) VALUES ('02/14/2013');
INSERT #temp (DateString) VALUES ('03/31/2013');
INSERT #temp (DateString) VALUES ('05/27/2013');
INSERT #temp (DateString) VALUES ('06/31/201'); -- known invalid date, maybe the data wasn't sanitized, etc.
INSERT #temp (DateString) VALUES ('07/04/2013');

我想选择 2013 年 7 月 1 日之前的假期数。我怀疑日期可能无效,因此我必须为此做好计划以避免异常。在我编写以下这些查询之前,我就知道它们会失败:

-- fails: cast exception, obviously
SELECT COUNT(*) AS [CountHolidays]
FROM #temp
WHERE CAST(DateString AS DATE)<'20130701';

-- fails: ISDATE() is not gauranteed to be evaluated first, less obvious, pointed out by another user.
SELECT COUNT(*) AS [CountHolidays]
FROM #temp
WHERE ISDATE(DateString) = 1 AND CAST(DateString AS DATE)<'20130701';

此查询按预期工作,将是我的最终选择:

-- works
SELECT COUNT(*) AS [CountHolidays]
FROM #temp
WHERE CONVERT(DATE,CASE WHEN ISDATE(DateString)=1 THEN datestring ELSE NULL END) < '20130701';

不过,在我编写最终查询之前,我先尝试了这个,我希望它能够工作,但它也会引发一个强制转换异常。为什么这个查询具体失败了?

-- Why does this query fail specifically?
-- I expected my derived inner query to filter invalid dates out first, but it does not. I get the same cast exception.
SELECT COUNT(*) AS [CountHolidays]
FROM (
    -- the derived table returns expected data when executed independently.
    SELECT DateString
    FROM #temp
    WHERE ISDATE(DateString) = 1
) AS T
WHERE CAST(DateString AS DATE)<'20130701';

【问题讨论】:

标签: sql sql-server sql-server-2008 tsql derived-table


【解决方案1】:

SQL Server 足够聪明,可以查看您在做什么,并结合内部和外部查询以提高效率。您将不得不强制它执行两次传递。

使用内部查询选择一个临时表,然后在外部查询中使用它。

【讨论】:

    【解决方案2】:

    通过显式日期转换,你会得到你所期望的结果。

    SELECT COUNT(*) AS [CountHolidays] 
    FROM (SELECT DateString FROM #temp WHERE ISDATE(DateString) = 1) AS T
    WHERE CAST(DateString AS DATE)<(select CAST('20130701' AS DATE));
    

    下面的查询本身返回结果(空),但它没有给出计数。因为在此之前,日期转换本身会失败,并且会显示错误消息“从字符串转换日期和/或时间时转换失败”。 如果您尝试使用“选择 *”,您会得到结果,然后出现错误消息。

    SELECT COUNT(*) AS [CountHolidays] 
    FROM (SELECT DateString FROM #temp WHERE ISDATE(DateString) = 1) AS T
    WHERE CAST(DateString AS DATE)<'20130701';
    

    根据连接语言设置,日期字段的解释可能不同。它依赖于 SQL Server 语言。日期格式在每种语言中的解释不同。因此明确转换日期格式效果更好。要检查您的语言设置和其他选项,请使用以下查询。

    DBCC USEROPTIONS
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-22
      • 2015-05-29
      • 2011-07-15
      • 1970-01-01
      • 2019-04-05
      • 2023-03-19
      相关资源
      最近更新 更多