【问题标题】:Expand date range to get all dates between series of date ranges from a table with excluded dates扩展日期范围以从包含排除日期的表格中获取一系列日期范围之间的所有日期
【发布时间】:2018-10-15 12:15:02
【问题描述】:

我需要获取表LEAVE 的每个ID 之间DATE_FROMDATE_TO 之间的所有日期,不包括周末、停工和节假日。考虑到这条记录(IDDATE_FROMDATE_TO):

001   04-OCT-2018   09-OCT-2018
002   05-OCT-2018   05-OCT-2018
...
n     01-OCT-2018   05-OCT-2018

我需要以这种格式(IDDAY_TOKEN)获取这些范围之间的所有日期:

001  04-OCT-2018
001  05-OCT-2018
001  08-OCT-2018
001  09-OCT-2018
002  05-OCT-2018
...
n    01-OCT-2018
n    02-OCT-2018
n    03-OCT-2018
n    04-OCT-2018
n    05-OCT-2018

我正在使用根据我找到的查询修改的这个查询:

SELECT ID, a.date_from + rnum - 1 AS day_token
  FROM (SELECT a.ID, a.date_from, a.date_to, ROWNUM AS rnum
          FROM all_objects, leave a
          -- Aside from ALL_OBJECT, I cross join it with my LEAVE table
         WHERE ROWNUM <= a.date_to - a.date_from + 1) a
 WHERE TO_CHAR (a.date_from + rnum - 1, 'DY') NOT IN ('SAT', 'SUN');
   AND NOT EXISTS (SELECT 1
                     FROM holiday b
                    WHERE b.schedule = d.date_from + rnum - 1)
   AND NOT EXISTS (SELECT 1
                     FROM suspension c
                    WHERE c.schedule = d.date_from + rnum - 1)

问题是只有第一条记录会正确展开,其他记录不会包含在记录集中,除非DATE_FROMDATE_TO是同一日期。

我想尽可能避免使用 PL-SQL 函数,但是如果不使用函数就无法达到我需要的结果集,请至少告诉我原因。

【问题讨论】:

  • 一件小事:您应该避免使用TO_CHAR(., 'DY'),因为这会导致本地化问题。最好改用TO_CHAR(., 'D')
  • 不是一个改进,真的,@Radagast81。 ISO 标准 8601 规定星期一是一周的第一天,星期二是第二天(因此,在克罗地亚和世界大部分地区,您的查询将返回“2”,就像今天是星期二一样)。但是,加拿大、美国、澳大利亚……认为星期天是一周的第一天,所以在那里您的查询将返回“3”(它仍然是星期二,对吗?)。 'D' 由 NLS_TERRITORY 控制,但不能在 TO_CHAR 函数调用中使用;一个应该改变会话。不过,是的 - NLS_DATE_LANGUAGE 可能在 TO_CHAR 中使用并会处理。

标签: sql oracle oracle11g


【解决方案1】:

以下是如何为每个 ID 创建与 FROM 和 TO 日期之间的天数一样多的行,不包括周末(周六和周日):

SQL> with leave (id, date_from, date_to) as
  2  (select '001', date '2018-10-04', date '2018-10-09' from dual union all
  3   select '002', date '2018-10-05', date '2018-10-05' from dual union all
  4   select '003', date '2018-10-02', date '2018-10-08' from dual
  5  ),
  6  inter as
  7    (select l.id,
  8       l.date_from + column_value datum,
  9       to_char(l.date_from + column_value, 'day') day
 10     from leave l,
 11          table(cast(multiset(select level from dual
 12                              connect by level <= l.date_to - l.date_from + 1
 13                             ) as sys.odcinumberlist))
 14    )
 15  select id, datum
 16  from inter
 17  where to_char(datum, 'dy') not in ('sat', 'sun');

ID  DATUM
--- -----------
001 05-oct-2018
001 08-oct-2018
001 09-oct-2018
001 10-oct-2018
003 03-oct-2018
003 04-oct-2018
003 05-oct-2018
003 08-oct-2018
003 09-oct-2018

9 rows selected.

SQL>

作为第 18 行(等等),添加附加条件(删除假期、暂停等)。

(顺便说一句,我想知道谁以及为什么对你的问题投了反对票;它格式正确,显示了你所拥有的,你试图解决它的尝试......对我来说真的是个谜)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-29
    • 2013-04-14
    • 1970-01-01
    • 2013-03-02
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    相关资源
    最近更新 更多