2

我知道过去有人问过类似的问题,但他们仍然没有为我的案件提供适当的解决方案。

我有一个包含值varchar列的数据库表(第三方) datetime

它包含以下格式的日期。

  11181980 
  8 18 1960 
  10/01/1960 
  04-12-1953 
  041371 
  7/29/44
  Empty String 
  NULL

当我选择此列时,我想mm/dd/yyyy在可用时以标准格式(比如 )或 NULL 将日期带入。

我只能想到一个函数来执行此操作,但我不想执行 UDF,因为我需要确保它在尝试转换时不会出错。UDF 中没有 try/catch。我可以做一个 CLR 函数来利用更强大的 .net 功能,尽管我想避免它。

有没有其他更好的方法来处理 SQL Server 中的这种转换?如果可能的话,我应该如何在 SQL 中进行这种转换。

4

4 回答 4

2

对于您描述的一组潜在格式:

DECLARE @x TABLE(y VARCHAR(32))

INSERT @x VALUES
('11181980'),
('8 18 1960'),
('10/01/1960'),
('04-12-1953'), 
('041371'),
('7/29/44'),
(''), 
(NULL);

SET DATEFORMAT MDY;

SELECT CONVERT(DATETIME, CASE WHEN y LIKE '%/%' THEN y
 WHEN LEN(RTRIM(y)) = 0 THEN NULL
 WHEN LEN(RTRIM(y)) IN (6,8) AND ISNUMERIC(y) = 1 THEN
 STUFF(STUFF(y,3,0,'/'),6,0,'/') END)
FROM (SELECT y = REPLACE(REPLACE(y, ' ', '/'), '-', '/') FROM @x) AS x;

这将根据服务器设置解释7/29/442044, not 。1944为确保所有日期都在过去,您可以执行以下操作:

SELECT y = DATEADD(YEAR, CASE WHEN y > GETDATE() THEN -100 ELSE 0 END, y) 
FROM
(
  SELECT y = CONVERT(DATETIME, CASE WHEN y LIKE '%/%' THEN y
   WHEN LEN(RTRIM(y)) = 0 THEN NULL ELSE
   STUFF(STUFF(y, 3, 0, '/'),6, 0, '/') END)
  FROM (SELECT y = REPLACE(REPLACE(y, ' ', '/'), '-', '/') FROM @x) AS x
) AS z;

这也取决于没有垃圾数据不能被处理成日期。到底是什么样的系统进入这种不一致的胡说八道?

在 SQL Server 2012 中,您将能够使用TRY_PARSETRY_CONVERT,但是由于格式混乱,您仍然需要进行一些处理才能获得有意义的结果。

于 2012-08-30T16:00:02.557 回答
0

如果您拥有数据库但无法更改它,我将运行一个存储过程,将所有值清理为一种通用格式,并确保只能插入/更新该格式的条目。如果您无法控制 CRUD 操作,我将按原样获取“日期”并DateTime在您的 BL 层中执行到 a 的转换。

也许不是您问题的答案,但我个人喜欢通过将转换和其他逻辑保留在数据库之外来尽可能简单地查询所有查询。

于 2012-08-30T15:40:51.117 回答
0

我建议您执行以下操作:

  1. 找到设计那张桌子的人并射击他们
  2. 编写一个 CLR 函数将值解析为日期,可能使用正则表达式模式匹配
  3. 创建一个返回所有相同列的视图,但您的函数结果而不是 varchar 字段

老实说,数据看起来像垃圾,我怀疑你完全可以依赖它。可能存在以下值:

  • 11190
  • 1111990

这些应该是 1990-11-01 还是 1990-01-11?我认为 CLR 功能将以最稳定的方式为您提供最多的数据。

于 2012-08-30T15:53:44.953 回答
0

这是我对这个 3 年老问题的解决方案。我没有任何空格,但是您可以以此为基础,并在评估时使用替换功能将其删除。给你,互联网。感谢过去 10 年的所有帮助。这是相当特定于 SQL 数据导入导出的,但希望能帮助那些陷入手动 ETL 模式的人。

    CASE    WHEN DOB LIKE '__/__/____'  THEN [DOB]                              -- PROPER FORMAT
    WHEN DOB LIKE '_/__/____'   THEN '0'+ [DOB]                         -- NEED TO ADD A ZERO TO THE MONTH
    WHEN DOB LIKE '__/_/____'   THEN  LEFT(DOB,3)+'0'+RIGHT(DOB,6)      -- NEED TO ADD A ZERO TO THE DAY
    WHEN DOB LIKE '_/_/____'    THEN '0'+LEFT(DOB,2)+'0'+RIGHT(DOB,6)   -- NEED TO ADD A ZERO TO THE MONTH AND DAY
    WHEN LEN(DOB)=8 AND DOB BETWEEN '1900' AND '2016' THEN LEFT(RIGHT(DOB,4),2) + '/' + RIGHT(DOB,2) +'/'+ LEFT(DOB,4)
    WHEN LEN(DOB)=8 AND DOB BETWEEN '01011900' AND '12312016' AND DOB NOT LIKE '%/%' THEN LEFT(DOB,2) + '/' + RIGHT(LEFT(DOB,4),2) +'/'+ RIGHT(DOB,4)
    WHEN DOB LIKE '__/__/__'     -- CONVERT FROM MM/DD/YY (ADD TWO DIGIT YEAR PREFIX)
        THEN    CASE    WHEN RIGHT(replace(dob,'/',''),2) > RIGHT(YEAR(GETDATE()),2) --WHEN 2-DIGIT YEAR IS WITHIN 100 YEARS AGO USE 19
                            THEN LEFT(DOB,2)+'/'+LEFT(RIGHT(replace(dob,'/',''),4),2)+'/19'+RIGHT(replace(dob,'/',''),2) 
                        WHEN RIGHT(DOB,2) < RIGHT(YEAR(GETDATE()),2) --WHEN 2-DIGIT YEAR IS MORE THAN 100 YEARS AGO USE 20
                            THEN LEFT(DOB,2)+'/'+LEFT(RIGHT(replace(dob,'/',''),4),2)+'/20'+RIGHT(replace(dob,'/',''),2) 
                ELSE NULL END 

ELSE NULL END AS [DOB_CONVERTER]

正如 Max Vernon 所指出的,您必须考虑要匹配和修复的每个模式。错误处理可以很好地实现自动化。在那之前,一旦数据被清理,就会查看数据,加载到临时表并使用类似的模式查找坏人(WHERE NULL 以查找非模式匹配)(WHERE RIGHT(LEFT(REPLACE([DOB],'/',' '),4),2) > 31

模式搜索是 microsoft.com 上的一个有用站点 https://technet.microsoft.com/en-us/library/ms187489(v=sql.105).aspx

于 2016-06-15T23:46:41.993 回答