这个问题的一般解决方案是为每个需要的日期生成一行,这可以使用connect by rownum(特定于Oracle)或各种其他方式完成,例如递归common table expression(许多dbs)。如果您需要定期执行此操作,您甚至可以考虑创建一个“日历表”。例如
SELECT * from (
SELECT to_date('2017-01-01','yyyy-mm-dd') + rownum - 1 dt
FROM DUAL CONNECT BY ROWNUM < 366
)
WHERE dt < to_date('2018-01-01','yyyy-mm-dd')
在行中获得日期后,将两组数据左连接到这些日期,工作就完成了。
select cal.dt, wp.*, h.*
from (
SELECT to_date('2017-01-01','yyyy-mm-dd') + rownum - 1 dt
FROM DUAL CONNECT BY ROWNUM < 36
) cal
left join Working_hour_parameter wp on cal.dt = wp.Dt
left join Holidays h on cal.dt between h.from_dt and h.to_dt
WHERE cal.dt < to_date('2018-01-01','yyyy-mm-dd')
----
为了还提出一种方法来汇总这些每个数据,first_value() 和 `last_value() 用于形成“岛”以对范围进行分组。
可通过SQL Fiddle 获得演示
CREATE TABLE WORKING_HOUR_PARAMETER
(DT timestamp, STD_INTIME varchar2(5), STD_OUTTIME varchar2(5))
;
INSERT ALL
INTO WORKING_HOUR_PARAMETER ("DT", "STD_INTIME", "STD_OUTTIME")
VALUES ('01-Jan-2017 12:00:00 AM', '09:00', '17:30')
INTO WORKING_HOUR_PARAMETER ("DT", "STD_INTIME", "STD_OUTTIME")
VALUES ('04-Jan-2017 12:00:00 AM', '09:00', '17:30')
INTO WORKING_HOUR_PARAMETER ("DT", "STD_INTIME", "STD_OUTTIME")
VALUES ('05-Jan-2017 12:00:00 AM', '09:00', '17:30')
INTO WORKING_HOUR_PARAMETER ("DT", "STD_INTIME", "STD_OUTTIME")
VALUES ('06-Jan-2017 12:00:00 AM', '09:00', '17:30')
SELECT * FROM dual
;
CREATE TABLE HOLIDAYS
(FROM_DT timestamp, TO_DT timestamp, HOLIDAY_TYPE varchar2(8), REMARKS varchar2(12))
;
INSERT ALL
INTO HOLIDAYS ("FROM_DT", "TO_DT", "HOLIDAY_TYPE", "REMARKS")
VALUES ('02-Jan-2017 12:00:00 AM', '03-Jan-2017 12:00:00 AM', 'whatever', 'avagoodbreak')
SELECT * FROM dual
;
查询 1:
select
min(dt) span_from
, max(dt) span_to
, std_intime
, std_outtime
, from_dt hol_start
, to_dt hol_to
, fval1 first_value
, lval1 last_value
from (
select cal.dt, h.FROM_DT, h.TO_DT, wp.std_intime, wp.std_outtime
, first_value(FROM_DT ignore nulls) over(order by cal.dt rows between current row and unbounded following) fval1
, last_value(TO_DT ignore nulls) over(order by cal.dt) lval1
from (
SELECT to_timestamp('2017-01-01','yyyy-mm-dd') + rownum - 1 dt
FROM DUAL CONNECT BY ROWNUM < 36
) cal
left join Working_hour_parameter wp on cal.dt = wp.Dt
left join Holidays h on cal.dt between h.from_dt and h.to_dt
WHERE cal.dt < to_date('2017-01-07','yyyy-mm-dd')
)
group by
std_intime
, std_outtime
, from_dt
, to_dt
, fval1
, lval1
order by 1, 2
Results:
| SPAN_FROM | SPAN_TO | STD_INTIME | STD_OUTTIME | HOL_START | HOL_TO | FIRST_VALUE | LAST_VALUE |
|----------------------|----------------------|------------|-------------|-----------------------|-----------------------|-----------------------|-----------------------|
| 2017-01-01T00:00:00Z | 2017-01-01T00:00:00Z | 09:00 | 17:30 | (null) | (null) | 2017-01-02 00:00:00.0 | (null) |
| 2017-01-02T00:00:00Z | 2017-01-03T00:00:00Z | (null) | (null) | 2017-01-02 00:00:00.0 | 2017-01-03 00:00:00.0 | 2017-01-02 00:00:00.0 | 2017-01-03 00:00:00.0 |
| 2017-01-04T00:00:00Z | 2017-01-06T00:00:00Z | 09:00 | 17:30 | (null) | (null) | (null) | 2017-01-03 00:00:00.0 |