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。此 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 排序)及其关联的序列开始日期;周六和周日不会有任何记录,但仍将按顺序处理(日历日)。

就像我们将周六和周日视为序列的一部分一样,如果那一天在 temp_holidys 表中,它也应该依次处理这一天(参见下面的#2)。

  1. 对于 user_id 1,这应该给我 '10-SEP-2013' 作为结束日期和 '06-SEP-2013' 作为开始日期
  2. 对于 user_id 2,这应该给我 '01-OCT-2013' 作为结束日期和 '26-SEP-2013' 作为开始日期(27-OCT-2013 需要按 temp_holidys 表中定义的顺序处理)
  3. 它必须是序列含义,例如在 # 1 中,对于用户 id 1,如果没有“09-SEP-2013”​​的记录,它应该返回“10-SEP-2013”​​作为开始日期。同样在 #2 中,对于用户 2,如果在 '26-SEP-2013' 上没有记录,它应该返回 30-SEP-2013' 作为开始日期。
4

3 回答 3

0

你需要一个 PL/SQL 函数。一种是为您提供流水线输出,另一种是告诉您日子是否彼此紧随其后。这是第二种方式的解决方案:

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

创建或替换函数 are_dates_adjacent(vi_start_date date, vi_end_date date) 返回数字为
  v_count 整数;
开始
  -- 当天或次日当然与开始日顺序
  如果 trunc(vi_end_date) in (trunc(vi_start_date), trunc(vi_start_date) + 1) 那么
    返回 1; -  真的
  -- 开始日之前的结束日无效
  elsif trunc(vi_end_date) < trunc(vi_start_date) 然后
    返回0; -  错误的
  万一;

  -- 现在循环遍历第一个和最后一个之间的天数,以寻找间隙,即跳过的工作日
  对于 1 中的偏移量 .. trunc(vi_end_date) - trunc(vi_start_date) - 1 循环
    -- 如果是周六或周日,那我们就可以了,否则我们看看是不是假期
    如果 to_char(trunc(vi_start_date) + offset, 'DY', 'NLS_DATE_LANGUAGE=AMERICAN') 不在 ('SAT','SUN') 那么
      -- 如果既不是周六周日也不是节假日,则返回 false
      从 temp_holidys 中选择 count(*) 到 v_count 中,其中假期 = trunc(vi_start_date) + 偏移量;
      如果 v_count = 0 那么
        返回0; -  错误的
      万一;
    万一;
  结束循环;

  -- 未检测到间隙;返回真
  返回 1; -  真的
结尾;

这是选择语句。在有序列表中,它首先查找组更改,即用户更改或日期不被视为相邻。在此基础上构建组,以便最后我们可以找到每个组的第一个和最后一个日期。

选择用户 ID,最小值(工作日期),最大值(工作日期)
从
(
  选择 user_id, work_date, sum(group_change) over(order by user_id, work_date) 作为 date_group
  从
  (
    选择
      用户身份,
      工作日期,
      情况下
        user_id nvl(lag(user_id) over(order by user_id, work_date), user_id) 或
        are_dates_adjacent(nvl(lag(work_date) over(order by user_id, work_date), work_date), work_date) = 0
      然后 1 else 0 以 group_change 结尾
    来自 Test_Seq
    按 user_id、work_date 排序
  )
)
按 user_id、date_group 分组
按 user_id 排序,min(work_date);

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

选择开始日期,结束日期
从
(
    选择 min(work_date) 作为 start_date, max(work_date) 作为 end_date
    从
    (
        select work_date, sum(group_change) over(order by work_date) as date_group
        从
        (
            选择
                工作日期,
                情况下
                    are_dates_adjacent(nvl(lag(work_date) over(order by work_date), work_date), work_date) = 0
                然后 1 else 0 以 group_change 结尾
            来自 Test_Seq
            其中 user_id = 1
            按工作日期订购
        )
    )
    按日期组分组
    按 min(work_date) desc 排序
)
其中rownum = 1;
于 2013-09-24T07:57:22.253 回答
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

演示在这里

于 2013-09-25T05:52:16.970 回答
-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)
)

谢谢,阿迪亚

于 2013-09-24T07:15:29.313 回答