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 は、連続している場合とそうでない場合があります。

もう 1 つのテーブルがあります。

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

最後の Work_Date (降順) とそれに関連するシーケンスの開始日を取得するには、クエリ / pl sql が必要です。土日は記録がありませんが、順番に扱います(暦日)。

土曜と日曜をシーケンスの一部として扱っているのと同じように、その日が 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 でユーザー ID 1 の場合、'09-SEP-2013' のレコードがなかった場合、開始日として '10-SEP-2013' を返す必要がある場合は、シーケンスを意味する必要があります。#2でも、ユーザー2の場合、「2013年9月26日」にレコードがなかった場合、開始日として2013年9月30日を返す必要があります。
4

3 に答える 3

0

これが1つのアプローチです。

  • このアプローチでは、すべての営業日、休日、および週末が同じテーブルにまとめられます。
  • 次に、各シーケンスの開始は、降順で並べられた日付で識別されます。
  • 各シーケンスには番号が付けられます。
  • 最初のシーケンスの最大値と最小値が検出されます。これは必要な結果です。

ユーザー 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 に答える
0

PL/SQL 関数が必要です。パイプライン化された出力を提供するもの、または日が互いに続いているかどうかを通知するもののいずれかです。2番目の方法の解決策は次のとおりです。

これが必要な機能です。Oracle SQL にはブール データ型がないため、false の場合は 0、true の場合は 1 を返します。

関数の作成または置換 are_dates_adjacent(vi_start_date date, vi_end_date date) 戻り番号として
  v_count 整数;
始める
  -- 当日・翌日はもちろん開始日順
  IF trunc(vi_end_date) in ( trunc(vi_start_date), trunc(vi_start_date) + 1 ) then
    1 を返します。 -  真実
  -- 開始日の前の終了日は無効です
  elsif trunc(vi_end_date) < trunc(vi_start_date) then
    0 を返します。 -  間違い
  終了する場合;

  -- 最初と最後の間の日をループしてギャップを探します。つまり、営業日をスキップしました。
  for offset in 1 .. trunc(vi_end_date) - trunc(vi_start_date) - 1 ループ
    -- 土曜日または日曜日の場合はこれで問題ありませんが、それ以外の場合は休日かどうかを見てみましょう
    if to_char(trunc(vi_start_date) + offset, 'DY', 'NLS_DATE_LANGUAGE=AMERICAN') が ('SAT','SUN') にない場合
      -- 土曜でも日曜でも休日でもない場合は false を返します
      count(*) を temp_holidys から v_count に選択します。ここで、休日 = trunc(vi_start_date) + オフセット;
      v_count = 0 の場合
        0 を返します。 -  間違い
      終了する場合;
    終了する場合;
  ループを終了します。

  -- ギャップが検出されませんでした。真を返す
  1 を返します。 -  真実
終わり;

これが選択ステートメントです。順序付けられたリストで、最初にグループの変更を探します。つまり、ユーザーが変更されたか、日付が隣接していると見なされません。これに基づいてグループが構築され、最終的にグループごとの最初と最後の日付を見つけることができます。

user_id、min(勤務日)、max(勤務日) を選択
から
(
  date_group として user_id, work_date, sum(group_change) over(user_id, work_date 順) を選択
  から
  (
    選択する
      ユーザーID、
      勤務日、
      ケースいつ
        user_id nvl(lag(user_id) over(user_id、work_date 順)、user_id) または
        are_dates_adjacent(nvl(lag(work_date) over(user_id順、work_date)、work_date)、work_date) = 0
      then 1 else 0 group_change として終了
    Test_Seq から
    user_id、work_date 順
  )
)
user_id、date_group でグループ化
user_id 順、min(work_date);

編集:そして、ここに select ステートメントがあり、1 人のユーザーの最後の作業時間のみが表示されます。

開始日、終了日を選択
から
(
    min(work_date) を start_date として選択し、max(work_date) を end_date として選択します。
    から
    (
        date_group として work_date, sum(group_change) over(work_date 順) を選択
        から
        (
            選択する
                勤務日、
                ケースいつ
                    are_dates_adjacent(nvl(lag(work_date) over(work_date 順), work_date), work_date) = 0
                then 1 else 0 group_change として終了
            Test_Seq から
            ここで、user_id = 1
            work_date 順
        )
    )
    date_group でグループ化
    min(work_date) 降順
)
行番号 = 1;
于 2013-09-24T07:57:22.253 に答える