oracle 根据身份证号计算出生日期

1.情景展示

  如何根据身份证号推算出出生日期? 

2.解决方案

--根据身份证号计算出生日期
SELECT DECODE(LENGTH(ID_CARD),
              18,
              SUBSTR(ID_CARD, 7, 8),
              15,
              '19' || SUBSTR(ID_CARD, 7, 6)) 出生日期
  FROM VIRTUAL_CARD
 WHERE LENGTH(ID_CARD) = 18
    OR LENGTH(ID_CARD) = 15

3.拓展

  根据身份证号,截取出生日期后,更新到该表的birthday(日期类型)字段

  第一步:一个SQL搞定

UPDATE VIRTUAL_CARD
   SET BIRTHDAY = TO_DATE(DECODE(LENGTH(ID_CARD),
                                 18,
                                 SUBSTR(ID_CARD, 7, 8),
                                 15,
                                 '19' || SUBSTR(ID_CARD, 7, 6)),
                          'yyyymmdd' )
 WHERE REMARK =  '3' ;

  正常情况下,它会执行没有问题。(没有报错的不用再往下看了)

  但是,现实往往不按我们想象的路子走。

  这个错误的意思是:无效的月份,再直白点是:月份错误,换句话说就是:本来月份有01-12个月,但是按照身份证截取的出生月份超出了这个范围,所以在转换日期的时候报错。

  第二步:删除无效的月份所在行数据

DELETE FROM VIRTUAL_CARD
 WHERE ID_CARD  IN ( SELECT ID_CARD
                     FROM ( SELECT DECODE(LENGTH(ID_CARD),
                                         18,
                                         SUBSTR(ID_CARD, 11, 2),
                                         15,
                                         SUBSTR(ID_CARD, 9, 2)) MON,
                                  ID_CARD
                             FROM VIRTUAL_CARD
                            WHERE REMARK =  '3' )
                    WHERE MON  NOT IN ( '01' ,
                                      '02' ,
                                      '03' ,
                                      '04' ,
                                      '05' ,
                                      '06' ,
                                      '07' ,
                                      '08' ,
                                      '09' ,
                                      '10' ,
                                      '11' ,
                                      '12' )) 

  in()函数里,查询出来的是脏数据,我选择删掉。

  再次执行第一步的代码,还是报这个错误。

  既然月份存在的脏数据已经删除,那么是不是日期出了问题?即:天数超出了01-31天,这个范围 

  第三步:删除无效的天数

DELETE FROM VIRTUAL_CARD
 WHERE ID_CARD  IN ( SELECT ID_CARD
                     FROM ( SELECT /*DISTINCT*/
                            DECODE(LENGTH(ID_CARD),
                                   18,
                                   SUBSTR(ID_CARD, 13, 2),
                                   15,
                                   SUBSTR(ID_CARD, 11, 2)) DA,
                            ID_CARD
                             FROM VIRTUAL_CARD
                            WHERE REMARK =  '3' )
                    WHERE DA != 01
                      AND DA != 02
                      AND DA != 03
                      AND DA != 04
                      AND DA != 05
                      AND DA != 06
                      AND DA != 07
                      AND DA != 08
                      AND DA != 09
                      AND DA != 10
                      AND DA != 11
                      AND DA != 12
                      AND DA != 13
                      AND DA != 14
                      AND DA != 15
                      AND DA != 16
                      AND DA != 17
                      AND DA != 18
                      AND DA != 19
                      AND DA != 20
                      AND DA != 21
                      AND DA != 22
                      AND DA != 23
                      AND DA != 24
                      AND DA != 25
                      AND DA != 26
                      AND DA != 27
                      AND DA != 28
                      AND DA != 29
                      AND DA != 30
                      AND DA != 31)

  说明:既可以用in()函数,也可以使用!=,in()方便一些,另外,oracle中的整数类数值型字符串,可以不加""。

  果不其然,日期也有脏数据,再次删掉,执行第一步的代码,还是报错。

  思考:既然日期也搞定了,还有哪会有问题?

  首先,闰年有366天,这说明闰年的2月份有29天,平年有365天,2月份对应28天。

  其次,1,3,5,7,8,10,12,这7个月有31天,4,6,9,11,这4个月有30天。

  to_date()函数,会对其进行严格校验,只有日期无效,就不予转换。

  2019年是平年,2月只有28天,当我设置成29天时,就会报月份无效。

  第四步:删除假闰年数据

DELETE FROM VIRTUAL_CARD
 WHERE ID_CARD  IN ( SELECT ID_CARD
                     FROM ( SELECT DECODE(LENGTH(ID_CARD),
                                         18,
                                         SUBSTR(ID_CARD, 7, 4),
                                         15,
                                         '19' || SUBSTR(ID_CARD, 7, 2)) YEA,
                                  DECODE(LENGTH(ID_CARD),
                                         18,
                                         SUBSTR(ID_CARD, 11, 2),
                                         15,
                                         SUBSTR(ID_CARD, 9, 2)) MON,
                                  DECODE(LENGTH(ID_CARD),
                                         18,
                                         SUBSTR(ID_CARD, 13, 2),
                                         15,
                                         SUBSTR(ID_CARD, 11, 2)) DA,
                                  ID_CARD
                             FROM VIRTUAL_CARD
                            WHERE REMARK =  '3' )
                    WHERE MON =  '02'
                      AND DA > 28  /*2月份超过28天*/
                      AND MOD(YEA, 4) != 0)  /*余数不为0*/

  说明:闰年能够被4整除,2月份为29天,反之,平年超过28天的都是脏数据。

  第五步:删除4,6,9,11月超过30天的数据

DELETE FROM VIRTUAL_CARD
 WHERE ID_CARD  IN
       ( SELECT ID_CARD
          FROM ( SELECT DECODE(LENGTH(ID_CARD),
                              18,
                              SUBSTR(ID_CARD, 11, 2),
                              15,
                              SUBSTR(ID_CARD, 9, 2)) MON,
                       DECODE(LENGTH(ID_CARD),
                              18,
                              SUBSTR(ID_CARD, 13, 2),
                              15,
                              SUBSTR(ID_CARD, 11, 2)) DA,
                       ID_CARD
                  FROM VIRTUAL_CARD
                 WHERE REMARK =  '3' )
         WHERE (MON  IN ( '04' ,  '06' ,  '09' ,  '11' )  AND DA > 30))

  再次执行第一步的更新代码,成功更新完毕。

  为了以防万一,先做检验,再提交数据。(在当前窗口执行查询SQL)

  没有毛病,提交数据,大功告成。 

  另外,看到这里,我们对身份证的有效性的校验就又多了一种方式。

  通过截取身份证号的出生日期,利用to_date()函数进行日期转换,转换失败的话,说明该身份证号绝壁有问题。

  还有一种方式是:系统游标批量更新法

  使用游标循环单行更新,捕获异常,继续执行下一条数据更新,直至更新完毕。 

  这样,最后birthday字段没有更新的行数据(字段为空),就是脏数据。

4.拓展2

  保留身份证号的前4位和后4位,中间部位隐藏。

SELECT SUBSTR(ID_CARD, 1, 4) || /*截取前4位*/
       DECODE(LENGTH(ID_CARD), 18,  '**********' , 15,  '*******' ) || /*中间用*号代替*/
       SUBSTR(ID_CARD, -4) 身份证号 /*截取后4位*/
  FROM VIRTUAL_CARD
 WHERE REMARK = 3
 ORDER BY ADDRESS

  

写在最后

  哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!

 相关推荐:

posted @ 2019-06-05 11:38  Marydon  阅读(5775)  评论(0编辑  收藏  举报