【问题标题】:Select all dates from a given date range having to and from dates从给定日期范围中选择所有日期
【发布时间】:2017-11-08 05:51:42
【问题描述】:

我正在编写一份报告,我想要一年中所有日期的日历,这些日期必须按顺序打印,即按日期排序。

我有两张桌子,一张有假期详细信息,另一张有标准的工作日进出时间。

这是两个表的列详细信息:

工作小时参数Dt(Date),STD_INTIME ,STD_OUTTIME

节假日from_dt, to_dt ,holiday_type, remarks

示例:如果我将日期范围从 11 月 1 日到 11 月 5 日,假设 11 月 2 日、3 日是假期,则它必须返回结果为:

From date      To date      std_intime   std_outtime  remarks
-------        --------      --------   -------       ------ 
1st-Nov        1st-Nov      09:00        17:30        working day

2nd-Nov        3rd-Nov      (null)      (null)         holiday

4th-Nov        5th-Nov      09:00        17:30        working day

问题是,其中一个表具有日期范围列,即到日期和从日期,而另一个表基于单个日期。

这怎么可能?

有人可以帮帮我吗?

表格快照:

【问题讨论】:

  • 你能提供一些示例数据吗?像表格的截图或类似的东西
  • @JayasuryaSatheesh 我已经放置了快照
  • 天哪,数据图像,太令人沮丧了。
  • 请阅读meta.stackoverflow.com/questions/285551/… 和接受的答案
  • 为什么1st-NOV 是一个工作日,4th-NOV5th-NOV - 2 个工作日也是一排

标签: sql oracle


【解决方案1】:

这个问题的一般解决方案是为每个需要的日期生成一行,这可以使用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 |

【讨论】:

  • 我同意这项技术,但看起来您需要做一些额外的工作,可能使用查看前一行的“假期”或“工作”状态的分区(按顺序排列)日期)并增加一个数字(如果它已更改),则可以对它进行分组,并从该组中获取最小和最大日期,因为结果必须具有日期范围,说明一个人在度假或工作的时间,而不是一天按日期列出当天是工作还是假期。
  • 我尝试了以下查询:SELECT to_date('01-NOV-2017','DD-MON-YYYY') dt FROM DUAL CONNECT BY ROWNUM
  • 将其更改为SELECT to_date('01-NOV-2017','DD-MON-YYYY') + rownum dt FROM DUAL CONNECT BY ROWNUM &lt; 366 不需要 where 子句,因为查询只会生成 366 行。还缺少将 rownum 添加到日期的内容,因此它生成了 366 个相同的日期
  • 抱歉,现在运行的脚本 sn-p 已经在一堆其他东西中这样做了。但我并没有真正尝试编写整个查询,为您提供解决方法的方向。
  • @CaiusJard 提出的观点是正确的,一旦你有每个可用的日期,那么你需要总结它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多