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

  

寫在最后

  哪位大佬如若發現文章存在紕漏之處或需要補充更多內容,歡迎留言!!!

 相關推薦:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM