2013-05-07 132 views
1

我正在研究将数据从IBM iSeries服务器导入到MSSQL 2008 R2数据库的程序。不幸的是,一些开发人员很久以前决定将日期存储为十进制类型,这有效地打破了用于存储日期的CYYMMDD格式。将IBM iSeries DB2小数日期类型转换为DateTime对象

例如,以这种格式,1995年8月1日将被存储为:0950801.然而,实际存储在数据库中的是95081,如果我尝试将其转换为System.DateTime,那么显然会引发异常。

如果这是一个简单的缺少前导0的问题,我可以很容易地将它添加到字符串,然后再尝试转换它。然而,有几个(数千个,真的)日期只有3或4位数字,我真的不知道该怎么做。例如,存储的日期为1128.我不知道该怎么做。如果我只用3个前导0来转换它,它会产生一个明显不正确的日期。

那么,有没有人知道解析这些日期的可靠方法?直接通过SQL select语句,或者在C#中进行一些操作?或者我只是假设3位和4位数字的日期从来没有正确输入过,并且放弃了这样的日期?

+0

你知道日期应该代表什么吗?他们是DOB等...? – 2013-05-07 21:19:58

+0

@Robbie他们代表不同的东西,包括DOB的,但也是客户账户历史表中的交易日期。对于交易日期,我可以对年度进行一番探讨,因为显然在我们公司做或者将来之前交易不可能存在。但是这对生日不起作用... – Jedediah 2013-05-07 21:23:16

+0

我不认为3或4位数字的日期本身就是一个问题。我倾向于将它们原样导入并稍后处理。你见过这个吗? http://www.techrepublic.com/forum/questions/101-278438/cyymmdd-format-in-db2 – 2013-05-07 21:30:16

回答

0

十进制CYYMMDD是一种标准的IBM格式,其中C在1900年为零,在2000年为1。这可以追溯到S/38(1982年左右)或更早。但我不记得他们在S/38之前使用它,它是AS/400和iSeries的前身。

我建议在DB2中创建一个用户定义的函数,将您的小数日期转换为ISO日期值。 DB2 for i将缓存DETERMINISTIC函数的结果,因此每次查看之前处理的日期值时都不必重新计算函数。

更新

下面是一个例子我适于将压缩十进制(8,0)在ccyymmd或YYMMDD格式值到DB2日期:

CREATE OR REPLACE FUNCTION 
    Cvt_Dec8cymd_to_Date (dtin dec(8,0)) 
         returns date 
    LANGUAGE SQL 
    CONTAINS SQL 
    DETERMINISTIC       -- caches results 
    NO EXTERNAL ACTION 
    RETURNS NULL ON NULL INPUT 
    NOT FENCED 
    SET OPTION DBGVIEW = *SOURCE 

prc: BEGIN NOT ATOMIC      -- don't rollback on error 
      DECLARE ans date;           
      DECLARE cymd dec(8,0);          
      -- add declarations for conditions and handlers here 

      SET ans = null;             
      CASE               
      WHEN dtin > 999999 THEN   -- more than 6 digits given 
       set cymd = dtin;            
      WHEN dtin < 400000 THEN   -- yr < 40 means 2000's  
       SET cymd = 20000000 + dtin;         
      ELSE       -- yr >= 40 means 1900's  
       SET cymd = 19000000 + dtin;         
      END CASE;              

      --convert to date            
      SET ans = date(insert(insert(digits(cymd),7,0,'-'),5,0,'-')); 
      RETURN ans;              
     END prc               
; 

它是简单的逻辑,没有错误处理无效值。

其他人可能会有一个更好的例子,或者可能会改善这个。

+0

我可以尝试明天发布代码。 – WarrenT 2013-05-07 23:42:44

+0

这很棒,如果你有时间 – Jedediah 2013-05-08 13:40:03

0

我会建议看看程序(和,特别是任何改变评论 [假设他们存在])插入/更新表(查询DB2为此)。如果日期格式发生更改(例如Y2K)以及原因,更改评论将有望告诉您。

同样看着阅读DB的任何程序,可能有特殊代码来处理日期。可能有代码来确定日期格式。

95081也可能是序号日期(YYDDD)其中DDD是年的日子。请参阅Ordinal or Julian-Date。这些日期在一个阶段流行。

我猜想DB字段原本是YYMMDD没有世纪。对于Y2k,格式可能更改为CYYMMDD。像1128(和221)这样的日期可能是YYMMDD日期,在Y2k更改之前创建的位置(或者在原始y2k实现中遗漏并在稍后更改的位置)。

+0

不幸的是,据我所知,没有读/写程序。每个人都通过终端仿真器直接与数据库交互,这是我怀疑数据不正确而不是格式问题的原因之一。 – Jedediah 2013-05-08 13:39:25

+0

该示例是950801,而不是95081.它似乎是1995年8月1日。 – WarrenT 2013-05-10 23:15:21

+0

@ Hg3这些程序很可能是在终端仿真程序中以交互方式运行。如果您拥有(或获得)5250仿真器,则可以登录并查找程序源。最常用的方法是使用STRPDM命令(启动程序开发管理器)。在PDM中,可以使用库,然后使用库中的对象(查找源文件),然后使用源文件的“成员”。源文件最有可能被命名为QxxxSRC,其中xxx可以是CL,RPG,RPGLE,CBL,CBLLE或其他程序语言标识符。但坦率地说,这可能是一个巨大的时间吸收,收益不大。 – WarrenT 2013-05-10 23:43:30

0

经过大量的试验和错误,我想我已经找到了解决方案。

 
SELECT 
(CASE WHEN INT(SUBSTR(DIGITS(DTPSTD), 1,2)) > MOD(YEAR(CURRENT DATE),100) 
      THEN DATE(CONCAT(CONCAT(CONCAT(SUBSTR(DIGITS(DTPSTD), 3,2), '/'), CONCAT(SUBSTR(DIGITS(DTPSTD), 5,2), '/')), CONCAT('19', SUBSTR(DIGITS(DTPSTD), 1,2)))) 
     ELSE DATE(CONCAT(CONCAT(CONCAT(SUBSTR(DIGITS(DTPSTD), 3,2), '/'), CONCAT(SUBSTR(DIGITS(DTPSTD), 5,2), '/')), CONCAT('20', SUBSTR(DIGITS(DTPSTD), 1,2)))) 
     END) AS TransactionDate 
FROM TABLE_NAME 
WHERE CUSTOMER_ID = 1 

DTPSTD是“发布日期”

据我所知,这适用于在1900年或2000的任何日期,但不会对日期比1月 - 1900年长工作。在我的情况下,这很好,因为我没有任何日期比1920年或更早存储的日期。

+1

查看我的嵌套INSERT()代码,它比一堆CONCAT()和SUBSTR()函数简单得多。另外你可能会发现'A || B || C'比'CONCAT(A,B,C)'更简单 – WarrenT 2013-05-10 23:20:52