【问题标题】:How to standardise a column of mixed date formats in T-SQL如何在 T-SQL 中标准化混合日期格式的列
【发布时间】:2014-10-13 17:08:46
【问题描述】:

在 SQL Server 中有一个表,其中包含一个带有日期数据的 varchar 列。不幸的是,日期的格式多种多样。

2012-05-01
27/05/2012 
07MAY2014
19/07/13   

可能还有其他人,但到目前为止我遇到的就是这些。

我需要将这些压缩到另一个表中的datetime 列中,因此我一直在尝试将它们选择为标准日期时间值。起初,我认为这很容易:

UPDATE myTable 
SET myDateColumn = CONVERT(DATETIME, myDateColumn, 103)
WHERE ISDATE(myDateColumn) = 0

但问题在于 SQL Server 将 dd/mm/yydd/mm/yyyy 视为单独的格式。前者是代码 3,后者是代码 103。所以无论我以哪种方式运行该更新,它都会以相反的格式阻塞。

有什么方法可以根据日期格式选择/更新,并将所有这些日期转换为单一有效的DateTime 格式?

【问题讨论】:

  • 日期转换是 SQL Server(和其他)的一个弱点。如果您可以使用 CLR,那么您可以编写 .NET 代码来解析您的日期时间,这可能是最好的方法。在解析日期之前,您将不得不做一些笨拙的工作来尝试确定日期的格式?

标签: sql sql-server tsql date datetime


【解决方案1】:

我的猜测是,您只需要尝试区分不同的类并以适当的方式处理每种情况。像这样的:

declare @tab table (d varchar(20))
insert @tab values ('2012-05-01'),('27/05/2012'),('07MAY2014'),('19/07/13')

select 
    case 
        when isnumeric(left(d,4)) = 1 then cast(d as date) 
        when len(d) = 10 then convert(date, d, 103) 
        when len(d) = 8 then convert(date, d, 3) 
        when charindex('/',d) = 0 and isnumeric(d) = 0 then convert(date, d, 106) 
    end as [date]
from @tab

输出:

date
----------
2012-05-01
2012-05-27
2014-05-07
2013-07-19

它可能效率不高,但我认为这是一次性操作。我没有把它写成更新语句,但是查询应该很容易适应,我认为如果可能的话,您应该考虑将转换后的日期添加为新的适当的日期时间列。

编辑:这是相应的更新语句:

update @tab
set d = 
    case 
        when isnumeric(left(d,4)) = 1 then cast(d as date) 
        when len(d) = 10 then convert(date, d, 103) 
        when len(d) = 8 then convert(date, d, 3) 
        when charindex('/',d) = 0 and isnumeric(d) = 0 then convert(date, d, 106) 
    end 
from @tab

【讨论】:

  • 谢谢 - 我以为会是这样,但我看不到使用字符串处理函数来区分数据的方法。仅供参考,这是导入的数据,因此直接覆盖而不是添加列是安全的。
  • 我猜最后一个when 用于处理07MAY2014 的情况,但实际上可以使用默认表达式转换这种情况:convert(date, '07MAY2014')(因此不需要特定的样式)。
  • @KingKing 我相信它依赖于语言环境,默认设置在我的非“美国英语”安装中不起作用,但我想你在这种情况下是正确的,无论如何很高兴知道。跨度>
  • 只是当前语言设置的问题,应该是英文,比如Deutsch,日期应该是07MAI2014MAIMAY的短月份德语)。我还在 SQL Server 2000 参考中读到了106 的样式,它说格式是dd mon yy,不知道你为什么使用它(任何参考?)。当我尝试将语言设置为非英语语言时(使用语法 set language {language}),使用 convert(date, '07MAY2014', 106) 将不起作用。
  • @KingKing 我错了,我的测试数据库确实是用英文的,convert(date, '07MAY2014') 工作正常,不知道为什么以前不行,可能是我做了一些不同的事情。
【解决方案2】:

这完全是可怕的,但它适用于您的示例:

DECLARE @DodgyDates TABLE (
    DateString VARCHAR(50));
INSERT INTO @DodgyDates VALUES ('2012-05-01');
INSERT INTO @DodgyDates VALUES ('27/05/2012'); 
INSERT INTO @DodgyDates VALUES ('07MAY2014');
INSERT INTO @DodgyDates VALUES ('19/07/13');
SELECT * FROM @DodgyDates;
--SELECT CONVERT(DATE, DateString) FROM @DodgyDates;--Fails
WITH DateDeconstruct AS (
    SELECT
        *,
        CASE
            WHEN DateString LIKE '____-__-__' THEN DateString
            WHEN DateString LIKE '__/__/____' THEN RIGHT(DateString, 4) + '-' + SUBSTRING(DateString, 4, 2) + '-' + LEFT(DateString, 2)
            WHEN DateString LIKE '__/__/__' THEN '20' + RIGHT(DateString, 2) + '-' + SUBSTRING(DateString, 4, 2) + '-' + LEFT(DateString, 2)
            WHEN DateString LIKE '_________' THEN RIGHT(DateString, 4) + '-' + CONVERT(VARCHAR(2), DATEPART(MM, DateString)) + '-' + LEFT(DateString, 2)
        END AS FixedString
    FROM
        @DodgyDates)
SELECT
    DateString AS OriginalDate,
    FixedString AS FixedDate,
    CONVERT(DATE, FixedString) AS ConvertedDate
FROM
    DateDeconstruct;

结果是:

OriginalDate    FixedDate   ConvertedDate
2012-05-01  2012-05-01  2012-05-01
27/05/2012  2012-05-27  2012-05-27
07MAY2014   2014-5-07   2014-05-07
19/07/13    2013-07-19  2013-07-19

【讨论】:

    【解决方案3】:

    在 SQL Server 2012 中,您可以使用 try_convert()。否则,您可以进行多次更新:

    UPDATE myTable 
        SET myDateColumn = CONVERT(DATETIME, myDateColumn, 103)
        WHERE ISDATE(myDateColumn) = 0 AND MyDateColumn like '[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]';
    
    UPDATE myTable 
        SET myDateColumn = CONVERT(DATETIME, myDateColumn, 3)
        WHERE ISDATE(myDateColumn) = 0 AND MyDateColumn like '[0-9][0-9]/[0-9][0-9]/[0-9][0-9]';
    

    注意:where 子句可能适用于 update。它不适用于select。您可能还需要使用case

    UPDATE myTable 
        SET myDateColumn = (CASE WHEN ISDATE(myDateColumn) = 0 AND MyDateColumn like '[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]'
    
                                 THEN CONVERT(DATETIME, myDateColumn, 103)
                                 ELSE myDateColumn
                            END)
        WHERE ISDATE(myDateColumn) = 0 AND MyDateColumn like '[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-0]'
    

    此外,您将值放回同一列,因此您将覆盖原始数据——并且您还有另一个隐式转换回字符串我强烈建议您使用 @987654328 在表中添加另一列@data type 并把正确输入的值放在那里。

    【讨论】:

      【解决方案4】:

      为此,您首先可以将所有数据转换为另一种格式,例如110USA date fromat,然后再进一步将整个表格更新为所需的格式。

      【讨论】:

      • 什么?这只会导致无法首先转换为美国日期格式。
      • 这只是一个建议。很抱歉没有工作。
      猜你喜欢
      • 2016-01-16
      • 1970-01-01
      • 2016-02-26
      • 2018-01-03
      • 2016-01-10
      • 1970-01-01
      • 2019-07-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多