检查生日是否在跨越年假的 6 个月之间
Checking if a Birthday was between a 6 month span which crosses the year break
我正在尝试编写 SQL 代码(使用 SQL 开发人员)来检查一个人是否在过去 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 之间......我该如何解决这个问题?
编辑: 正如 APC 所指出的,我的解决方案不适用于闰年。我通常会删除这个 post,但它已经被选为问题的答案。我更新了下面的代码以使用 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
)
将日期作为日期而不是字符串进行比较。
显然 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);
祝你好运。
'01/25'
不在 '09/19'
和 '03/19'
之间,因为当第二个参数小于第一个参数时 between()
永远不会为真。你落入这个陷阱是因为你在处理弦乐。使用 DATE 数据类型处理日期总是更容易。
看起来您的列 effective_date
和 expiry_date
可能不是存储为日期而是字符串;不幸的是,这是一个常见的数据建模错误。如果是这样,您需要先将它们转换为 DATE,然后再应用以下内容。
此解决方案有一个子查询,它从 driver_table
中选择相关列,并计算每个 driver 的当前年龄(以年为单位)。年龄用于推导出最后一个生日,然后在主查询中将其与保险条款的范围进行比较。因为我们导出了一个实际日期,所以我们可以使用 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>
子查询同时生成 age
和 last_birthday
只是为了演示目的。在现实生活中,您只需要 last_birthday
列。
此解决方案与其他解决方案略有不同,因为:
- 适用于任何生效日期和到期日期之间的任何生日
- 它占闰年
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
我正在尝试编写 SQL 代码(使用 SQL 开发人员)来检查一个人是否在过去 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 之间......我该如何解决这个问题?
编辑: 正如 APC 所指出的,我的解决方案不适用于闰年。我通常会删除这个 post,但它已经被选为问题的答案。我更新了下面的代码以使用 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
)
将日期作为日期而不是字符串进行比较。
显然 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);
祝你好运。
'01/25'
不在 '09/19'
和 '03/19'
之间,因为当第二个参数小于第一个参数时 between()
永远不会为真。你落入这个陷阱是因为你在处理弦乐。使用 DATE 数据类型处理日期总是更容易。
看起来您的列 effective_date
和 expiry_date
可能不是存储为日期而是字符串;不幸的是,这是一个常见的数据建模错误。如果是这样,您需要先将它们转换为 DATE,然后再应用以下内容。
此解决方案有一个子查询,它从 driver_table
中选择相关列,并计算每个 driver 的当前年龄(以年为单位)。年龄用于推导出最后一个生日,然后在主查询中将其与保险条款的范围进行比较。因为我们导出了一个实际日期,所以我们可以使用 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>
子查询同时生成 age
和 last_birthday
只是为了演示目的。在现实生活中,您只需要 last_birthday
列。
此解决方案与其他解决方案略有不同,因为:
- 适用于任何生效日期和到期日期之间的任何生日
- 它占闰年
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