-1

在尝试回答有关 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';
4

2 回答 2

2

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

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

于 2013-12-18T17:04:55.810 回答
1

通过进行显式日期转换,您将获得预期的结果。

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
于 2013-12-19T03:39:25.810 回答