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