我有一个包含以下列的表: 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。这个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(按desc排序)及其相关的序列开始日期;星期六和星期日不会有任何记录,但仍将按顺序处理(日历日)。
与我们将Sat和Sun视为序列的一部分相同,如果那天在temp_holidys表中,它应该按顺序处理日期(参见下面的#2)。
答案 0 :(得分:0)
你需要一个PL / SQL函数。既可以为您提供流水线输出,也可以告诉您天数是否相互跟随。以下是第二种方式的解决方案:
这是所需的功能。由于Oracle SQL中缺少布尔数据类型,它返回0表示false,1表示true表示:
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语句,只为您提供一个用户的最后工作时间。
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;
答案 1 :(得分: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。
答案 2 :(得分:-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)
)
谢谢,Aditya