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

8-29 Partitioned Inner Joinもどき

SQLパズル

NoTable
  ID  No
----  --
CAT1   1
CAT1   2
CAT1   3
CAT1   5
CAT1   7
CAT2   1
CAT2   3
CAT2   4
CAT2   6

IDごとの、最小のNoから最大のNoまでの間の歯抜けのNoを出力する。

出力結果
  ID  No
----  --
CAT1   4
CAT1   6
CAT2   2
CAT2   5


データ作成スクリプト

create table NoTable(ID,No) as
select 'CAT1', 1 from dual union all
select 'CAT1', 2 from dual union all
select 'CAT1', 3 from dual union all
select 'CAT1', 5 from dual union all
select 'CAT1', 7 from dual union all
select 'CAT2', 1 from dual union all
select 'CAT2', 3 from dual union all
select 'CAT2', 4 from dual union all
select 'CAT2', 6 from dual;


SQL

--■■■Partitioned Outer Joinを使う方法(10g以降)■■■
select b.ID,a.Counter as No
  from (select RowNum as Counter from dict) a
  Left Join (select ID,No,
             max(No) over(partition by ID) as maxNo
             from NoTable) b
  partition by (b.ID,b.maxNo)
    on (a.Counter = b.No)
 where b.No is null
   and a.Counter < b.maxNo;

--■■■Partitioned Outer Joinを使わない方法■■■
select a.ID,b.Counter
from (select ID,max(No) as maxNo
        from NoTable
      group by ID) a,
     (select RowNum as Counter from dict) b
 where a.maxNo >= b.Counter
   and not exists(select 1 from NoTable c
                   where c.ID = a.ID
                     and c.No = b.Counter)
order by a.ID,b.Counter;

--■■■再帰with句を使う方法(11gR2以降)■■■
with rec(ID,No,LeadNo) as(
select ID,No+1,LeadNo
  from (select ID,No,
        Lead(No) over(partition by ID order by No) as LeadNo
        from NoTable)
 where No+1 < LeadNo
union all
select ID,No+1,LeadNo
  from rec
 where No+1 < LeadNo)
select * from rec;


解説

Partitioned Outer Joinを使う方法では、
実質、外部結合してからwhere句でフィルタをかけてるだけですが
Partitioned Outer Joinによく似た
Partitioned Inner Joinと考えていいでしょう。

日本語の用語だと、
パーティション化された外部結合によく似た
パーティション化された内部結合と考えていいでしょう。

Find missing sequences by category(英語)

3-34 Partitioned Outer Join
10-241 Partitioned Anti Joinもどき