2017-08-03 81 views
2

我正在嘗試編寫SQL代碼(使用SQL Developer),檢查過去6個月保險期限內是否有生日。檢查生日是否在跨越年度休假的6個月跨度

這就是我的代碼目前的樣子。

SELECT DRIVER_KEY, CASE WHEN BDAY BETWEEN EFFDAY AND EXPDAY THEN 1 ELSE 0 END AS BDAYIND FROM (
    SELECT DISTINCT A.DRIVER_KEY 
    , TO_CHAR(A.BIRTH_DATE,'mm/dd') AS BDAY   
    , TO_CHAR(SUBSTR(A.EFFECTIVE_DATE_KEY,5,2)||'/'||SUBSTR(A.EFFECTIVE_DATE_KEY,7,2)) AS EFFDAY 
    , TO_CHAR(SUBSTR(A.EXPIRATION_DATE_KEY,5,2)||'/'||SUBSTR(A.EXPIRATION_DATE_KEY,7,2)) AS EXPDAY 
    FROM DRIVER_TABLE A 
    ); 

它的工作原理 - 只要該術語不會在一年中突破。但是,我的代碼目前說01/25不在09/19和03/19之間......我該如何解決這個問題?

+2

編輯您的問題並提供示例da ta和期望的結果。你的問題提到6個月的時間,但你的代碼沒有這樣的東西。 –

+1

爲什麼不使用完整日期'mm/dd/yyyy'來確定它是否在範圍內? –

+0

由於您正在轉換爲「CHAR」或「字符串」數據,因此「01」不在「09」和「03」之間。 –

回答

2

編輯:正如APC指出的,我的解決方案不適用於閏年。我通常會刪除這篇文章,但它已被選爲問題的答案。我更新了我的代碼,使用Brian Leach的解決方案中的年份邏輯代替to_date字符串。請提出Brian或APC的答案。

這裏是我的任意日期創建語句:

create table DRIVER_TABLE 
(
    BIRTH_DATE date, 
    EFFECTIVE_DATE_KEY date, 
    EXPIRATION_DATE_KEY date 
); 

    insert into DRIVER_TABLE 
    values(to_date('05/01/1980','MM/DD/YYYY'), 
     to_date('11/01/2016','MM/DD/YYYY'), 
     to_date('04/01/2017','MM/DD/YYYY')); 

下面是該查詢:

select case when BirthdayEFFYear between EFFECTIVE_DATE_KEY and EXPIRATION_DATE_KEY 
       or BirthdayEXPYear between EFFECTIVE_DATE_KEY and EXPIRATION_DATE_KEY 
       or to_number(EXPIRATION_DATE_KEY - EFFECTIVE_DATE_KEY)/365 > 1 
     then 1 else 0 end BDAYIND 
from(
select add_months(BIRTH_DATE,12 * (extract(year from EFFECTIVE_DATE_KEY) - extract(year from BIRTH_DATE))) BirthdayEFFYear, 
     add_months(BIRTH_DATE,12 * (extract(year from EXPIRATION_DATE_KEY) - extract(year from BIRTH_DATE))) BirthdayEXPYear, 
     EFFECTIVE_DATE_KEY,EXPIRATION_DATE_KEY 
from DRIVER_TABLE A 
) 

SQLFiddle

+0

嘿! Oracle再次在SQLFiddle上工作?甜! :-) –

+0

如果司機在2月29日出生,這個解決方案就會發生。 – APC

+0

APC是正確的。使用這種方法,增加閏年帳戶將是非常混亂。我認爲APC的解決方案可能是解決所有問題的最佳解決方案。我剛剛測試過他,它也適用於閏年。 –

2

比較日期爲日期,而不是字符串。

顯然EFFECTIVE_DATE_KEY包含了前四個字符的一年,因此下面的應該給你你在找什麼:

SELECT DRIVER_KEY, 
     CASE 
     WHEN BDAY BETWEEN EFFDAY AND EXPDAY THEN 1 
     ELSE 0 
     END AS BDAYIND 
    FROM (SELECT DISTINCT A.DRIVER_KEY, 
         A.BIRTH_DATE AS BDAY, 
         TO_DATE(A.EFFECTIVE_DATE_KEY, 'YYYYMMDD') AS EFFDAY, 
         TO_DATE(A.EXPIRATION_DATE_KEY, 'YYYYMMDD') AS EXPDAY 
      FROM DRIVER_TABLE A); 

好運。

2

'01/25'不在'09/19''03/19'之間,因爲當第二個參數小於第一個參數時,between()從不爲真。你陷入這個陷阱,因爲你正在使用字符串。使用DATE數據類型處理日期總是更容易。

它看起來像你的列effective_dateexpiry_date可能不會被存儲爲日期,而是一個字符串;不幸的是這是一個常見的數據建模錯誤。如果是這樣,您需要先將它們投射到DATE,然後再應用以下內容。

該解決方案有一個子查詢,該子查詢從driver_table中選擇相關列,並計算每個駕駛員當前的年齡。年齡用於導出上一個生日,然後在主要查詢中將其與保險期限進行比較。因爲我們推導出實際日期,所以我們可以使用Oracle的標準日期算術,因此bdayind的計算是正確的。

SQL> with cte as (
    2  select driver_key 
    3    , date_of_birth 
    4    , trunc(months_between(sysdate, date_of_birth)/12) as age 
    5    , add_months(date_of_birth, 12 * (trunc(months_between(sysdate, date_of_birth)/12))) as last_birthday 
    6    , effective_date 
    7    , expiry_date 
    8  from driver_table 
    9 ) 
10 select driver_key 
11   , date_of_birth as dob 
12   , age 
13   , effective_date as eff_date 
14   , expiry_date as exp_date 
15   , last_birthday as last_bday 
16   , case 
17    when last_birthday between effective_date and expiry_date 
18    then 1 
19    else 0 end as bdayind 
20 from cte 
21/ 

DRIVER_KEY DOB  AGE EFF_DATE EXP_DATE LAST_BDAY BDAYIND 
---------- --------- ---- --------- --------- --------- ---------- 
     12 02-APR-98 19 01-DEC-16 31-MAY-17 02-APR-17   1 
     22 02-APR-98 19 01-JAN-17 30-JUN-17 02-APR-17   1 
     32 02-SEP-98 18 01-DEC-16 31-MAY-17 02-SEP-16   0 
     42 02-SEP-98 18 01-JAN-17 30-JUN-17 02-SEP-16   0 

SQL> 

子查詢只用於演示目的既產生agelast_birthday。在現實生活中,您只需要last_birthday列。

1

該解決方案從別人略有不同:

  1. 它適用於任何有效和到期之間的任何生日日期
  2. 它佔閏年

的raw_data只是設置舉例說明日期:

WITH 
    raw_data 
    AS 
     (SELECT DATE '1963-08-03' AS birthday 
       , DATE '2017-04-01' AS effectiveday 
       , DATE '2017-10-31' AS expirationday 
       , 'Billy' AS name 
      FROM DUAL 
     UNION ALL 
     SELECT DATE '1995-03-20' AS birthday 
       , DATE '2017-04-01' AS effectiveday 
       , DATE '2017-10-31' AS expirationday 
       , 'Sue' AS name 
      FROM DUAL 
     UNION ALL 
     SELECT DATE '1997-01-15' AS birthday 
       , DATE '2016-12-01' AS effectiveday 
       , DATE '2017-05-31' AS expirationday 
       , 'Olga' AS name 
      FROM DUAL), 
    mod_data 
    AS 
     (SELECT raw_data.* 
       , ADD_MONTHS (
        birthday 
        , (extract(year from effectiveday) - extract (year from birthday)) * 12 
       ) 
        effectiveanniversary 
       , ADD_MONTHS (
        birthday 
        , (extract(year from expirationday) - extract (year from birthday)) * 12 
       ) 
        expirationanniversary 
      FROM raw_data) 
SELECT name, mod_data.birthday, effectiveday, expirationday 
    , CASE 
      WHEN effectiveanniversary BETWEEN effectiveday AND expirationday 
      OR expirationanniversary BETWEEN effectiveday AND expirationday 
      THEN 
       1 
      ELSE 
       0 
     END 
      found_between 
    FROM mod_data 

NAME BIRTHDAY  EFFECTIVEDAY EXPIRATIONDAY FOUND_BETWEEN       
Billy 1963/08/03 2017/04/01 2017/10/31  1          
Sue 1995/03/20 2017/04/01 2017/10/31  0          
Olga 1997/01/15 2016/12/01 2017/05/31  1