トップページに戻る    次のSQLパズルへ    前のSQLパズルへ

10-4 最新の更新日を取得

SQLパズル

社員テーブル
社員コード   担当職務コード     勤務開始日
----------   ------------     ----------
        1               A     2005/04/01
        1               B     2005/05/01
        1               A     2005/06/01
        1               A     2005/07/01
        1               A     2005/08/01
        2               B     2005/07/01
        2               A     2005/08/01
        2               B     2005/09/01
        3               B     2005/10/01
        4               A     2005/11/01

社員テーブルから、社員ごとの最新の担当職務コード更新日を取得する。

出力結果
社員コード   担当職務コード     勤務開始日
----------   ------------     ----------
        1               A     2005/06/01
        2               B     2005/09/01
        3               B     2005/10/01
        4               A     2005/11/01


データ作成スクリプト

create table 社員テーブル(
社員コード     number(1),
担当職務コード char(1),
勤務開始日     date,
primary key(社員コード,勤務開始日));

insert into 社員テーブル values(1,'A',to_date(20050401,'yyyymmdd'));
insert into 社員テーブル values(1,'B',to_date(20050501,'yyyymmdd'));
insert into 社員テーブル values(1,'A',to_date(20050601,'yyyymmdd'));
insert into 社員テーブル values(1,'A',to_date(20050701,'yyyymmdd'));
insert into 社員テーブル values(1,'A',to_date(20050801,'yyyymmdd'));
insert into 社員テーブル values(2,'B',to_date(20050701,'yyyymmdd'));
insert into 社員テーブル values(2,'A',to_date(20050801,'yyyymmdd'));
insert into 社員テーブル values(2,'B',to_date(20050901,'yyyymmdd'));
insert into 社員テーブル values(3,'B',to_date(20051001,'yyyymmdd'));
insert into 社員テーブル values(4,'A',to_date(20051101,'yyyymmdd'));
commit;


SQL

col 勤務開始日 for a20

--■■■Keepを使う方法■■■
select 社員コード,
max(担当職務コード) Keep(Dense_Rank First order by 勤務開始日) as 担当職務コード,
to_char(max(勤務開始日),'YYYY/MM/DD') as 勤務開始日
from (select 社員コード,担当職務コード,勤務開始日,
      Lag(担当職務コード) over(partition by 社員コード order by 勤務開始日) as LagVal
        from 社員テーブル)
where LagVal is null
   or 担当職務コード != LagVal
group by 社員コード
order by 社員コード;

--■■■分析関数を使う方法1■■■
select distinct 社員コード,
Last_Value(担当職務コード) over(partition by 社員コード order by 勤務開始日
Rows between Unbounded Preceding and Unbounded Following) as 担当職務コード,
to_char(max(勤務開始日) over(partition by 社員コード),'YYYY/MM/DD') as 勤務開始日
from (select 社員コード,担当職務コード,
      Lag(担当職務コード) over(partition by 社員コード order by 勤務開始日) as Lag担当職務コード,
      勤務開始日
      from 社員テーブル)
where Lag担当職務コード is null
   or Lag担当職務コード != 担当職務コード
order by 社員コード;

--■■■分析関数を使う方法2■■■
select distinct 社員コード,
担当職務コード,
to_char(min(勤務開始日) over(partition by 社員コード),'YYYY/MM/DD') as 勤務開始日
from 社員テーブル a
where not exists(select 1 from 社員テーブル b
                  where b.社員コード=a.社員コード
                    and b.担当職務コード!=a.担当職務コード
                    and b.勤務開始日 > a.勤務開始日)
order by 社員コード;

--■■■相関サブクエリを使う方法■■■
select 社員コード,担当職務コード,to_char(勤務開始日,'YYYY/MM/DD') as 勤務開始日
  from 社員テーブル a
 where 勤務開始日 =
(select min(b.勤務開始日) from 社員テーブル b
  where b.社員コード=a.社員コード
    and b.担当職務コード=(select c.担当職務コード from 社員テーブル c
                           where c.社員コード=b.社員コード
                             and c.勤務開始日=(select max(d.勤務開始日) from 社員テーブル d
                                                where d.社員コード=c.社員コード))
    and not exists(select 1 from 社員テーブル e
                    where e.社員コード = b.社員コード
                      and e.担当職務コード != b.担当職務コード
                      and e.勤務開始日 > b.勤務開始日))
order by 社員コード;

--■■■相関サブクエリを使う方法(上のクエリを簡略化)■■■
select 社員コード,担当職務コード,to_char(勤務開始日,'YYYY/MM/DD') as 勤務開始日
  from 社員テーブル a
 where 勤務開始日 =
(select min(b.勤務開始日) from 社員テーブル b
 where b.社員コード=a.社員コード
   and not exists(select 1 from 社員テーブル c
                   where c.社員コード = b.社員コード
                     and c.担当職務コード != b.担当職務コード
                     and c.勤務開始日 > b.勤務開始日))
order by 社員コード;


解説

相関サブクエリを使う方法での自己結合の結合条件では、
a.社員コード=b.社員コードかつ
b.社員コード=c.社員コードかつ
c.社員コード=d.社員コードかつ
b.社員コード=e.社員コード
ゆえにa.社員コード=b.社員コード=c.社員コード=d.社員コード=e.社員コード
といった三段論法を使ってます。
三段論法の知識があると、結合の多いSQLを書きやすくなります。


三段論法とは - 数学用語集|数学能力検定 TOMAC
余談:三段論法の実用的な意味と使い方