2013-09-23 55 views
2

我有一个表下列: 1. USER_ID 2. Work_Date如何获取表中序列天的开始日期和结束日期?

create table Test_Seq(user_id number, work_date date); 

它具有以下数据:

insert into Test_Seq values (1, '01-SEP-2013'); 
insert into Test_Seq values (1, '02-SEP-2013'); 
insert into Test_Seq values (1, '06-SEP-2013'); 
insert into Test_Seq values (1, '09-SEP-2013'); 
insert into Test_Seq values (1, '10-SEP-2013'); 

insert into Test_Seq values (2, '10-SEP-2013'); 
insert into Test_Seq values (2, '26-SEP-2013'); 
insert into Test_Seq values (2, '30-SEP-2013'); 
insert into Test_Seq values (2, '01-OCT-2013'); 

此表存储work_date用户。此工作日期可能会或可能不会按顺序进行。

还有一个表:

create table temp_holidys (holiday date); 
insert into temp_holidys values ('27-SEP-2013'); 
insert into temp_holidys values ('31-DEC-2013'); 

我需要查询/ PL SQL来获得最后Work_Date(按降序排列)及其相关序列的开始日期;星期六和星期日将不会有任何记录,但仍将按顺序处理(日历日期)。

与我们将Sat和Sun作为序列的一部分对待相同,如果该日在temp_holidys表(请参见下面的#2)中,它也应按顺序处理一天。

  1. 对于USER_ID 1,本应该给我'10 -sep-2013' 为结束日期和'06 -sep-2013' 作为起始日期
  2. 对于USER_ID 2,本应该给我01 -OCT -2013'作为结束日期并且'26 -SEP-2013'作为开始日期(2013年10月27日需要按照在temp_holidys表中定义的顺序进行处理)
  3. 它必须是序列含义,例如在#1中,对于用户标识1,如果'09 -SEP-2013'没有记录,则应为开始日期返回'10 -SEP-2013'。同样在#2中,对于用户2,如果'26 -SEP-2013'上没有记录,则应该为开始日期返回30-SEP-2013'。
+0

你能描述更多的第三条件吗? –

+0

所需的输出是这样吗? 1,'01 -SEP-2013','02 -SEP-2013'*** 1,'06 -SEP-2013','10 -SEP-2013'*** 2,'10 -SEP-2013', '10 -SEP-2013'*** 2,'26 -SEP-2013','01 -OCT-2013' –

+0

请根据样本数据添加所需的输出。 –

回答

0

这里有一种方法。

  • 在这种方法中,所有的工作日,节假日和周末放在一起放在同一张表中。
  • 然后,每个序列的开始都按照降序排列的日期进行标识。
  • 每个序列都有一个数字。
  • 找出第一个序列的最大值和最小值,这是所需的结果。

下面是用户1的查询

/*---for user 1---*/ 
with minmaxdays as(
    --find the latest and earliest working date for each user 
    select min(work_date) min_date, 
     max(work_date) max_date 
    from test_seq 
    where user_id = 1 
), 
alldays as(
    --generate all days from earliest to latest dates 
    select min_date + level all_days 
    from minmaxdays 
    connect by min_date + level < max_date 
), 
combined_test_seq as(
    --get the working days 
    select work_date working_days, 'W' date_type --W indicates working days 
    from test_seq 
    where user_id = 1 
    union all 
    --get the holidays 
    select holiday working_days, 'H' date_type --H indicates holidays/weekends 
    from temp_holidys 
    union all 
    --get all the weeknds 
    select all_days working_days, 'H' date_type --H indicates holidays/weekends 
    from alldays 
    where to_char(all_days,'D') in ('1','7')  --select only saturdays and sundays 
), 
grouping as(
--find out the beginning of each sequence 
    select working_days, 
     date_type, 
     case when working_days + 1 = 
        lag(working_days,1) over (order by working_days desc) 
       then 0 
       else 1 
     end seq_start 
    from combined_test_seq 
), 
grouping2 as(
--assign sequence no, and keep only the working days 
    select working_days, 
     sum(seq_start) over (order by working_days desc) grp 
    from grouping 
    where date_type = 'W' 
) 
-- get the max and min date in the first sequence. 
select max(working_days) keep (dense_rank first order by grp), 
     min(working_days) keep (dense_rank first order by grp) 
from grouping2; 

结果:

max(date)  min(date) 
------------------------- 
10-SEP-2013  06-SEP-2013 

演示here

-1

你能显示所需的输出吗?这将有所帮助。

我试图理解你的查询和与此想出了 -

select distinct * from 
(
select user_id, min(work_date) over (partition by user_id) start_date, max(work_date) over (partition by user_id) end_date 
from test_seq t 
where not exists (select null from temp_holidys h 
         where h.holiday=t.work_date) 
) 

感谢,阿迪亚

0

你需要一个PL/SQL函数。无论是给你一个流水线输出还是一个告诉你日子是否跟随彼此的输出。这是第二种方式的解决方案:

这是所需的功能。由于Oracle SQL中缺少布尔型数据类型,它返回0代表假,1代表真。

 

create or replace function are_dates_adjacent(vi_start_date date, vi_end_date date) return number as 
    v_count integer; 
begin 
    -- Same day or next day is of course in sequence with the start day 
    IF trunc(vi_end_date) in (trunc(vi_start_date), trunc(vi_start_date) + 1) then 
    return 1; -- TRUE 
    -- An end day before start day is invalid 
    elsif trunc(vi_end_date) < trunc(vi_start_date) then 
    return 0; -- FALSE 
    end if; 

    -- Now loop through the days between first and last to look out for gaps, i.e. skipped working days 
    for offset in 1 .. trunc(vi_end_date) - trunc(vi_start_date) - 1 loop 
    -- If Saturday or Sunday, then we are fine with this, otherwise let's see if it's a holiday 
    if to_char(trunc(vi_start_date) + offset, 'DY', 'NLS_DATE_LANGUAGE=AMERICAN') not in ('SAT','SUN') then 
     -- If it's neither Saturday or Sunday nor a holiday then return false 
     select count(*) into v_count from temp_holidys where holiday = trunc(vi_start_date) + offset; 
     if v_count = 0 then 
     return 0; -- FALSE 
     end if; 
    end if; 
    end loop; 

    -- No gap detected; return true 
    return 1; -- TRUE 
end; 

这是select语句。在有序列表中,它首先查找组更改,即用户更改或日期不被视为相邻。基于这些组建立起来的,所以最后我们可以找到每组的最后一个日期。

 
select user_id, min(work_date), max(work_date) 
from 
(
    select user_id, work_date, sum(group_change) over(order by user_id, work_date) as date_group 
    from 
    (
    select 
     user_id, 
     work_date, 
     case when 
     user_id nvl(lag(user_id) over(order by user_id, work_date), user_id) or 
     are_dates_adjacent(nvl(lag(work_date) over(order by user_id, work_date), work_date), work_date) = 0 
     then 1 else 0 end as group_change 
    from Test_Seq 
    order by user_id, work_date 
) 
) 
group by user_id, date_group 
order by user_id, min(work_date); 

编辑:这里是选择语句只给你一个用户的最后工作时间。

 
select start_date, end_date 
from 
(
    select min(work_date) as start_date, max(work_date) as end_date 
    from 
    (
     select work_date, sum(group_change) over(order by work_date) as date_group 
     from 
     (
      select 
       work_date, 
       case when 
        are_dates_adjacent(nvl(lag(work_date) over(order by work_date), work_date), work_date) = 0 
       then 1 else 0 end as group_change 
      from Test_Seq 
      where user_id = 1 
      order by work_date 
     ) 
    ) 
    group by date_group 
    order by min(work_date) desc 
) 
where rownum = 1; 
+0

非常感谢您的帮助,但我需要知道我的文章中提到的开始日期和结束日期。 – user1663715

+0

啊,我看起来并没有彻底读完。我添加了一个select语句来获取只有一个用户,并且只有他最后的工作时间。 –

相关问题