2017-08-03 56 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