我有一个包含超过 100 万条记录的大表。不幸的是,创建该表的人决定将日期放入 varchar(50) 场地。

我需要做一个简单的日期比较 -

datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31

但它失败了 convert():

Conversion failed when converting datetime from character string.

显然这个领域有些东西是它不喜欢的,而且记录太多,光看也看不出来。如何正确清理整个日期字段,使其不会在 convert()?这是我现在所拥有的:

select count(*)
from MyTable
where
    isdate(lastUpdate) > 0
    and datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31

@SQL威胁

在这种情况下,我并不关心性能。这将是一次性查询。将表更改为日期时间字段不是一种选择。

@乔恩·林贾普

我尝试添加第三个参数,但没有什么区别。


@SQL威胁

问题很可能是数据的存储方式,只有两种安全格式;ISO 年月日;ISO 8601 yyyy-mm-dd Thh:mm:ss:mmm(无空格)

难道不是 isdate() 检查一下照顾这个吗?

我不需要 100% 的准确性。我只想获取过去 30 天的大部分记录。


@SQL威胁

select isdate('20080131') -- returns 1
select isdate('01312008') -- returns 0

@布莱恩·施克尔克

将 CASE 和 ISDATE 放在 CONVERT() 函数内。

谢谢!就这样做到了。

有帮助吗?

解决方案

放置 CASEISDATE 在 - 的里面 CONVERT() 功能。

SELECT COUNT(*) FROM MyTable
WHERE
    DATEDIFF(dd, CONVERT(DATETIME, CASE IsDate(lastUpdate)
        WHEN 1 THEN lastUpdate
        ELSE '12-30-1899'
    END), GetDate()) < 31

代替 '12-30-1899' 与您选择的默认日期。

其他提示

如何编写一个游标来循环遍历内容,尝试对每个条目进行强制转换?当发生错误时,输出问题记录的主键或其他标识详细信息。我想不出一种基于集合的方法来做到这一点。

不完全基于集合,但如果 100 万行中只有 3 行是坏的,它将节省您大量时间

select * into BadDates
from Yourtable
where isdate(lastUpdate) = 0

select * into GoodDates
from Yourtable
where isdate(lastUpdate) = 1

然后只需查看 BadDates 表并修复该问题

如果 ISDATE() 确实首先执行,则它会处理格式不正确的行。但是,如果您查看执行计划,您可能会发现 DATEDIFF 谓词首先被应用 - 这就是您痛苦的原因。

如果您使用的是 SQL Server Management Studio,请点击 控制键+L 查看特定查询的估计执行计划。

请记住,SQL 不是一种过程语言,短路逻辑可能会起作用,但前提是您在应用它时要小心。

编写一个游标来循环浏览内容,尝试对每个条目进行强制转换怎么样?

发生错误时,输出问题记录的主键或其他标识详细信息。

我想不出一种基于集合的方法来做到这一点。

编辑 - 啊,是的,我忘记了 ISDATE()。绝对是比使用光标更好的方法。+1 给 SQLMenace。

在您的转换调用中,您需要指定第三个样式参数,例如,存储为 varchar 的日期时间的格式,如本文档中所指定: 转换和转换 (T-SQL)

打印出记录。将硬拷贝交给决定使用 varchar(50) 的白痴,并要求他们找到问题记录。

下次他们可能只会看到选择合适的数据类型的意义。

问题很可能是数据的存储方式,只有两种安全格式

ISO年月日

ISO 8601 yyyy-mm-dd Thh:mm:ss:mmm(无空格)

无论您使用哪种语言,这些都可以使用。

您可能需要执行 SET DATEFORMAT YMD (或任何数据存储方式)才能使其正常工作

isdate() 检查不会解决这个问题吗?

运行这个看看会发生什么

select isdate('20080131')
select isdate('01312008')

我确信由于任何遗留系统要求,更改表/列可能不是一个选项,但是您是否考虑过创建一个内置日期转换逻辑的视图,如果您使用的是更新版本的 sql,那么您甚至可以使用索引视图吗?

我建议清理混乱并将列更改为日期时间,因为做这样的事情

WHERE datediff(dd, convert(datetime, lastUpdate), getDate()) < 31

不能使用索引,它会比你有一个日期时间列慢很多倍,并且做了

where lastUpdate > getDate() -31

当然,您还需要考虑小时和秒

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top