解码日期并与列进行比较
decode the day and compare with a column
我有专栏:
1.:delivery_date
列.. 它是 returns 星期几,像这样:
1 --monday
2 --tuesday
3 --wednesday
4 --thursday
5 --friday
2.: date
列.. returns 取件日期 - 这是一个正常的日期列 - 例如:25.03.2019 (format DD.MM.YYYY)
但是, 我们的系统会自动比较并将 date
设置为最接近的 delivery_date
。
示例:
合作伙伴每周delivery_date
星期四和星期二.. [所以 4 和 2]。他想在星期五提货。此时我们的系统将 date
更改为星期四。但我想要下一个 date
,也就是星期二 - 我想解码它到现在为止(格式 DD.MM.YYYY)
所以输出如下:
--if pick up date: 22.03.2019 (friday)
--partner has delivery_date: 4,2(thursday, tuesday)
I want to get: 26.03.2019 --as output
我可以比较列..这不是问题..但是我如何解码 "actual pick up" 日期?
我该怎么做?
为了解决这个问题,构建一个 "calendar" table 可能是一个优势 - 让它足够大(!),随后可以用来加密你的数据(见注释下面的代码)。为了进行测试,让我们使用以下 tables (Oracle 12c, 18c):
Table CAL_
create table cal_
as
select sysdate + level date_
, to_char( sysdate + level, 'Day' ) dname_
, to_char( sysdate + level, 'D' ) dnumber_
from dual
connect by level <= 31 ; -- number should be big enough to cover the lifetime of your system
SQL> select * from cal_ ;
DATE_ DNAME_ DNUMBER_
17-MAR-19 Sunday 7
18-MAR-19 Monday 1
19-MAR-19 Tuesday 2
20-MAR-19 Wednesday 3
21-MAR-19 Thursday 4
...
12-APR-19 Friday 5
13-APR-19 Saturday 6
14-APR-19 Sunday 7
15-APR-19 Monday 1
16-APR-19 Tuesday 2
Table 交货天数
create table deliverydays ( partner, deliveryday, dayofweek )
as
select
mod( level, 3 ) + 1
, mod( level, 5 ) + 1
, to_char( sysdate + ( mod( level, 5 ) + 2 ), 'Day' )
from dual
connect by level <= 10 ;
-- partner 4 delivers on Tue and Thu (same days as in the question)
insert into deliverydays ( partner, deliveryday, dayofweek )
values ( 4, 2, 'Tuesday' ) ;
insert into deliverydays ( partner, deliveryday, dayofweek )
values ( 4, 4, 'Thursday' ) ;
SQL> select * from deliverydays order by partner, deliveryday ;
PARTNER DELIVERYDAY DAYOFWEEK
1 2 Tuesday
1 4 Thursday
1 5 Friday
2 1 Monday
2 2 Tuesday
2 3 Wednesday
2 5 Friday
3 1 Monday
3 3 Wednesday
3 4 Thursday
4 2 Tuesday
4 4 Thursday
12 rows selected.
您可以像这样DENSIFY您的数据。请注意,交货日期之间的间隔变得可见。 (带有尾随下划线的列名:来自 CAL_ table)。
select
partner, deliveryday , dayofweek
, date_, dname_, dnumber_
from deliverydays D partition by ( partner )
right join cal_ C on D.deliveryday = C.dnumber_
where partner = 4
order by date_ ;
PARTNER DELIVERYDAY DAYOFWEEK DATE_ DNAME_ DNUMBER_
4 NULL NULL 17-MAR-19 Sunday 7
4 NULL NULL 18-MAR-19 Monday 1
4 2 Tuesday 19-MAR-19 Tuesday 2
4 NULL NULL 20-MAR-19 Wednesday 3
4 4 Thursday 21-MAR-19 Thursday 4
4 NULL NULL 22-MAR-19 Friday 5
4 NULL NULL 23-MAR-19 Saturday 6
4 NULL NULL 24-MAR-19 Sunday 7
4 NULL NULL 25-MAR-19 Monday 1
4 2 Tuesday 26-MAR-19 Tuesday 2
4 NULL NULL 27-MAR-19 Wednesday 3
4 4 Thursday 28-MAR-19 Thursday 4
...
-- 31 rows selected
下一步,您可以使用 LEAD() 函数来查找 "next" 交货日期,
然后计算 "picking up" 和交货之间的天数,并根据需要格式化日期。 (请参阅查询 here 的 "commented" 版本。)
select pid, dday, next_dday, date_, dname_, dnumber_
, case
when dnumber_ > next_dday then ( 7 - dnumber_ + next_dday ) -- next week
else next_dday - dnumber_
end ddiff_
, case
when dnumber_ > next_dday then
to_char( date_ + ( 7 - dnumber_ + next_dday ), 'DD.MM.YYYY' )
else
to_char( date_ + ( next_dday - dnumber_ ), 'DD.MM.YYYY' )
end delivered_on
from (
select
partner as pid, deliveryday as dday
, lead( deliveryday ) ignore nulls over ( order by date_ ) as next_dday
, dayofweek
, date_, dname_, dnumber_
from deliverydays D partition by ( partner )
right join cal_ C on D.deliveryday = C.dnumber_
where partner = 4 -- partner id
)
where to_char( date_, 'DD.MM.YYYY' ) = '22.03.2019' -- "pick up" date
order by date_ ;
结果
PID DDAY NEXT_DDAY DATE_ DNAME_ DNUMBER_ DDIFF_ DELIVERED_ON
4 NULL 2 22-MAR-19 Friday 5 4 26.03.2019
不使用外部 WHERE 子句,我们得到 ..
PID DDAY NEXT_DDAY DATE_ DNAME_ DNUMBER_ DDIFF_ DELIVERED_ON
4 NULL 2 17-MAR-19 Sunday 7 2 19.03.2019
4 NULL 2 18-MAR-19 Monday 1 1 19.03.2019
4 2 4 19-MAR-19 Tuesday 2 2 21.03.2019
4 NULL 4 20-MAR-19 Wednesday 3 1 21.03.2019
4 4 2 21-MAR-19 Thursday 4 5 26.03.2019
4 NULL 2 22-MAR-19 Friday 5 4 26.03.2019
4 NULL 2 23-MAR-19 Saturday 6 3 26.03.2019
4 NULL 2 24-MAR-19 Sunday 7 2 26.03.2019
4 NULL 2 25-MAR-19 Monday 1 1 26.03.2019
4 2 4 26-MAR-19 Tuesday 2 2 28.03.2019
4 NULL 4 27-MAR-19 Wednesday 3 1 28.03.2019
4 4 2 28-MAR-19 Thursday 4 5 02.04.2019
...
-- 31 rows selected
我有专栏:
1.:delivery_date
列.. 它是 returns 星期几,像这样:
1 --monday
2 --tuesday
3 --wednesday
4 --thursday
5 --friday
2.: date
列.. returns 取件日期 - 这是一个正常的日期列 - 例如:25.03.2019 (format DD.MM.YYYY)
但是, 我们的系统会自动比较并将 date
设置为最接近的 delivery_date
。
示例:
合作伙伴每周delivery_date
星期四和星期二.. [所以 4 和 2]。他想在星期五提货。此时我们的系统将 date
更改为星期四。但我想要下一个 date
,也就是星期二 - 我想解码它到现在为止(格式 DD.MM.YYYY)
所以输出如下:
--if pick up date: 22.03.2019 (friday)
--partner has delivery_date: 4,2(thursday, tuesday)
I want to get: 26.03.2019 --as output
我可以比较列..这不是问题..但是我如何解码 "actual pick up" 日期?
我该怎么做?
为了解决这个问题,构建一个 "calendar" table 可能是一个优势 - 让它足够大(!),随后可以用来加密你的数据(见注释下面的代码)。为了进行测试,让我们使用以下 tables (Oracle 12c, 18c):
Table CAL_
create table cal_
as
select sysdate + level date_
, to_char( sysdate + level, 'Day' ) dname_
, to_char( sysdate + level, 'D' ) dnumber_
from dual
connect by level <= 31 ; -- number should be big enough to cover the lifetime of your system
SQL> select * from cal_ ;
DATE_ DNAME_ DNUMBER_
17-MAR-19 Sunday 7
18-MAR-19 Monday 1
19-MAR-19 Tuesday 2
20-MAR-19 Wednesday 3
21-MAR-19 Thursday 4
...
12-APR-19 Friday 5
13-APR-19 Saturday 6
14-APR-19 Sunday 7
15-APR-19 Monday 1
16-APR-19 Tuesday 2
Table 交货天数
create table deliverydays ( partner, deliveryday, dayofweek )
as
select
mod( level, 3 ) + 1
, mod( level, 5 ) + 1
, to_char( sysdate + ( mod( level, 5 ) + 2 ), 'Day' )
from dual
connect by level <= 10 ;
-- partner 4 delivers on Tue and Thu (same days as in the question)
insert into deliverydays ( partner, deliveryday, dayofweek )
values ( 4, 2, 'Tuesday' ) ;
insert into deliverydays ( partner, deliveryday, dayofweek )
values ( 4, 4, 'Thursday' ) ;
SQL> select * from deliverydays order by partner, deliveryday ;
PARTNER DELIVERYDAY DAYOFWEEK
1 2 Tuesday
1 4 Thursday
1 5 Friday
2 1 Monday
2 2 Tuesday
2 3 Wednesday
2 5 Friday
3 1 Monday
3 3 Wednesday
3 4 Thursday
4 2 Tuesday
4 4 Thursday
12 rows selected.
您可以像这样DENSIFY您的数据。请注意,交货日期之间的间隔变得可见。 (带有尾随下划线的列名:来自 CAL_ table)。
select
partner, deliveryday , dayofweek
, date_, dname_, dnumber_
from deliverydays D partition by ( partner )
right join cal_ C on D.deliveryday = C.dnumber_
where partner = 4
order by date_ ;
PARTNER DELIVERYDAY DAYOFWEEK DATE_ DNAME_ DNUMBER_
4 NULL NULL 17-MAR-19 Sunday 7
4 NULL NULL 18-MAR-19 Monday 1
4 2 Tuesday 19-MAR-19 Tuesday 2
4 NULL NULL 20-MAR-19 Wednesday 3
4 4 Thursday 21-MAR-19 Thursday 4
4 NULL NULL 22-MAR-19 Friday 5
4 NULL NULL 23-MAR-19 Saturday 6
4 NULL NULL 24-MAR-19 Sunday 7
4 NULL NULL 25-MAR-19 Monday 1
4 2 Tuesday 26-MAR-19 Tuesday 2
4 NULL NULL 27-MAR-19 Wednesday 3
4 4 Thursday 28-MAR-19 Thursday 4
...
-- 31 rows selected
下一步,您可以使用 LEAD() 函数来查找 "next" 交货日期, 然后计算 "picking up" 和交货之间的天数,并根据需要格式化日期。 (请参阅查询 here 的 "commented" 版本。)
select pid, dday, next_dday, date_, dname_, dnumber_
, case
when dnumber_ > next_dday then ( 7 - dnumber_ + next_dday ) -- next week
else next_dday - dnumber_
end ddiff_
, case
when dnumber_ > next_dday then
to_char( date_ + ( 7 - dnumber_ + next_dday ), 'DD.MM.YYYY' )
else
to_char( date_ + ( next_dday - dnumber_ ), 'DD.MM.YYYY' )
end delivered_on
from (
select
partner as pid, deliveryday as dday
, lead( deliveryday ) ignore nulls over ( order by date_ ) as next_dday
, dayofweek
, date_, dname_, dnumber_
from deliverydays D partition by ( partner )
right join cal_ C on D.deliveryday = C.dnumber_
where partner = 4 -- partner id
)
where to_char( date_, 'DD.MM.YYYY' ) = '22.03.2019' -- "pick up" date
order by date_ ;
结果
PID DDAY NEXT_DDAY DATE_ DNAME_ DNUMBER_ DDIFF_ DELIVERED_ON
4 NULL 2 22-MAR-19 Friday 5 4 26.03.2019
不使用外部 WHERE 子句,我们得到 ..
PID DDAY NEXT_DDAY DATE_ DNAME_ DNUMBER_ DDIFF_ DELIVERED_ON
4 NULL 2 17-MAR-19 Sunday 7 2 19.03.2019
4 NULL 2 18-MAR-19 Monday 1 1 19.03.2019
4 2 4 19-MAR-19 Tuesday 2 2 21.03.2019
4 NULL 4 20-MAR-19 Wednesday 3 1 21.03.2019
4 4 2 21-MAR-19 Thursday 4 5 26.03.2019
4 NULL 2 22-MAR-19 Friday 5 4 26.03.2019
4 NULL 2 23-MAR-19 Saturday 6 3 26.03.2019
4 NULL 2 24-MAR-19 Sunday 7 2 26.03.2019
4 NULL 2 25-MAR-19 Monday 1 1 26.03.2019
4 2 4 26-MAR-19 Tuesday 2 2 28.03.2019
4 NULL 4 27-MAR-19 Wednesday 3 1 28.03.2019
4 4 2 28-MAR-19 Thursday 4 5 02.04.2019
...
-- 31 rows selected