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

9-57 集合が等しい組み合わせを求める(複数列版)

SQLパズル

ValTable
Code  Val1  Val2
----  ----  ----
AAAA     1     2
AAAA     2     3
AAAA     3     3
BBBB     5     6
BBBB     7     8
CCCC     1     2
CCCC     2     3
CCCC     3     3
DDDD     5     6
DDDD     7     8
EEEE     1     2
EEEE     5     6
EEEE     6     5
FFFF     6     5
FFFF     7     8
GGGG     6     8
GGGG     7     5
HHHH     8     6
IIII     8     6

Codeごとの、Val1とVal2の集合の組み合わせで、
集合が等しい組み合わせを、
以下の形式で出力する。

ValTableに重複行は存在しないものとする。

出力結果
S1    S2
----  ----
AAAA  CCCC
BBBB  DDDD
HHHH  IIII
9-27 集合の包含関係を調べるのアレンジです


データ作成スクリプト

create table ValTable(
Code char(4),
Val1 number(1),
Val2 number(1),
primary key(Code,Val1,Val2));

insert into ValTable
select 'AAAA',1,2 from dual union
select 'AAAA',2,3 from dual union
select 'AAAA',3,3 from dual union
select 'BBBB',5,6 from dual union
select 'BBBB',7,8 from dual union
select 'CCCC',1,2 from dual union
select 'CCCC',2,3 from dual union
select 'CCCC',3,3 from dual union
select 'DDDD',5,6 from dual union
select 'DDDD',7,8 from dual union
select 'EEEE',1,2 from dual union
select 'EEEE',5,6 from dual union
select 'EEEE',6,5 from dual union
select 'FFFF',6,5 from dual union
select 'FFFF',7,8 from dual union
select 'GGGG',6,8 from dual union
select 'GGGG',7,5 from dual union
select 'HHHH',8,6 from dual union
select 'IIII',8,6 from dual;
commit;


SQL

--■■■グループ化する方法■■■
select a.Code as s1,b.Code as s2
  from ValTable a,ValTable b
 where a.Code < b.Code
group by a.Code,b.Code
having count(case when a.Val1 = b.Val1
                   and a.Val2 = b.Val2
                  then 1 end)
= all(-1+Dense_Rank(null,null) within group(order by a.Val1,a.Val2),
      -1+Dense_Rank(null,null) within group(order by b.Val1,b.Val2));

--■■■グループ化しない方法■■■
select distinct c1,c2
from (select a.Code as c1,b.Code as c2,
      count(case when a.Val1 = b.Val1
                  and a.Val2 = b.Val2
                 then 1 end) over(partition by a.Code,b.Code) as cnt1,
      -1+dense_rank() over(partition by a.Code,b.Code order by a.Val1  asc,a.Val2  asc)
        +dense_rank() over(partition by a.Code,b.Code order by a.Val1 desc,a.Val2 desc) as cnt2,
      -1+dense_rank() over(partition by a.Code,b.Code order by a.Val1  asc,a.Val2  asc)
        +dense_rank() over(partition by a.Code,b.Code order by a.Val1 desc,a.Val2 desc) as cnt3
        from ValTable a,ValTable b
       where a.Code < b.Code)
where cnt1 = all(cnt2,cnt3)
order by c1,c2;

--■■■件数を内部結合の条件にする方法■■■
with work as(
select Code,Val1,Val2,count(*) over(partition by Code) as cnt
  from ValTable)
select a.Code as s1,b.Code as s2
  from work a,work b
 where a.Code < b.Code
   and a.cnt  = b.cnt
   and a.Val1 = b.Val1
   and a.Val2 = b.Val2
group by a.Code,b.Code,a.cnt
having count(*) = a.cnt
order by a.Code,b.Code;


解説

集合の一致
ふたつの集合AとBに対して、AとBとが等しいとは、
A⊂B と B⊂A が両方とも成り立つときをいう。
またこのときA=Bと書く。

だそうですが

(集合Aに重複した要素が存在しない、かつ、集合Bに重複した要素が存在しない)
かつ
(A⊂B、または、B⊃Aが成立する)
かつ
集合Aと集合Bの要素数が等しい

と、A=Bも同値です。

という論理を使ってます。

2-3-25 集合関数のdense_rank関数
10-311 紐づく集合の一致を調べる

CodeZine:SQLで集合演算
CodeZine:分析関数の衝撃(完結編)