SlideShare a Scribd company logo
前回の「みんなでプログラムを作ろう」
セッションのLT
第2問目のリレーションロジック解説
開催日:2019/1/26
主催:JAPAN SQL SERVER USER GROUP
作成:柳 大介
発表者自己紹介
名 前 :柳 大介
SQLSERVER歴 :15年目
仕 事 :オンプレミスの自治体向けPKGシステム開発
基盤開発、システム導入、保守
インフラ構築などなど
課題の説明
■仕様説明
・JSSUG_DEVデータベースに、RELATIONテーブル(関連テーブル)があります。
■求めること
・RELATIONテーブルのColAカラム列の値とColBカラム列のお互いの関係を探し、
グループピングを行い、グループIDを1から付番し、ColA及び、ColBの値を昇順で
並び替えます。
ColA ColB
A B
B C
D E
GroupID ColValue
1 A
1 B
1 C
2 D
2 E
課題の説明
■コーディング規約
・T-SQLで行うこと。他のCLRや言語を使ってはいけません。
・ユーザファンクション、ストアドプロシージャの実装可能の数は制限はありません。
・変数名、一時テーブルを使う場合は読みやすく書きましょう。
・なるべくループやカーソル処理を避けましょう。
つまり
SELECT文のみでの取得!
仕様についてもう少し詳しく
■RELATIONテーブルのColA、ColBの関連性について
お互いのColは一方向でなく相互に関連しています。
ColA ColB
A B
B C
D E
ColA ColB
A B
B C
D E
仕様についてもう少し詳しく
■複数のColに同じ文字が存在した場合、
同グループとして扱います。
ColA ColB
A B
A C
A G
GroupID ColValue
1 A
1 B
1 C
1 G
ColA ColB
D C
F C
E C
GroupID ColValue
1 C
1 D
1 E
1 F
■逆もしかり
作成したT-SQLについて
・クエリが長く、同じサブクエリが重複するケースがあるため、
共通式テーブル(CTE)を使用して、簡略化しています。
・一部の処理に再帰CTEを使用しています。
・CTEのセクションが4つ、最後のSELECTが1つの
5セクションで実装しています。
セクションに毎に解説していきます。
①関連性を洗い出す。一つ先まで
・処理をする上で、ColA⇒ColB、ColB⇒ColAといった判断を
行うことが面倒です。
・全てのケースを洗い出し、ColA⇒ColBの処理で完結するように
「main」共通テーブルを作成します。
・関連性はColA⇒ColBだけでなく、その一つ先まで加味します。
RELATION
ColA ColB
A B
B C
C D
E F
A G
B H
N F
RELATION 逆
ColA ColB
B A
C B
D C
F E
G A
H B
F N
with
main as (
select * from RELATION
union
select ColB,ColA from RELATION as A
union
select A.ColA,B.ColB from RELATION as A
inner join RELATION as B on A.ColB = B.ColA
union
select A.ColA,B.ColA from RELATION as A
inner join RELATION as B on A.ColA = B.ColB
union
select A.ColA,B.ColA from RELATION as A
inner join RELATION as B on A.ColB = B.ColB
union
select A.ColB,B.ColB from RELATION as A
inner join RELATION as B on A.ColB = B.ColA
union
select A.ColB,B.ColA from RELATION as A
inner join RELATION as B on A.ColA = B.ColB
union
select A.ColA,B.ColA from RELATION as A
inner join RELATION as B on A.ColB = B.ColB
)
ColAから見た一つ先(ColB)ColBとColA結合
ColA ColB ColA ColB ColA ColB
A B B C A C
結合 B H 結果 A H
B C C D B D
C D
E F
A G
B H
N F
ColAから見た一つ先(ColA)ColAとColB結合
ColA ColB ColA ColB ColA ColB
A B
B C 結合 A B 結果 B A
C D B C C B
E F
A G
B H A B B A
N F
①関連性を洗い出す。一つ先まで
ColAから見た一つ先(ColA)ColBとColB結合
ColA ColB ColA ColB ColA ColB
A B A B A A
B C 結合 B C 結果 B B
C D C D C C
E F E F E E
N F E N
A G A G A A
B H B H B B
N F E F N E
N F N N
ColBから見た一つ先(ColB)ColBとColA結合
ColA ColB ColA ColB ColA ColB
A B B C B C
結合 B H 結果 B H
B C C D C D
C D
E F
A G
B H
N F
ColBから見た一つ先(ColA)ColAとColB結合
ColA ColB ColA ColB ColA ColB
A B
B C 結合 A B 結果 C A
C D B C D B
E F
A G
B H A B H A
N F
①関連性を洗い出す。一つ先まで
ColBから見た一つ先(ColA)ColBとColB結合
ColA ColB ColA ColB ColA ColB
A B A B B A
B C 結合 B C 結果 C B
C D C D D C
E F E F F E
N F F N
A G A G G A
B H B H H B
N F E F F E
N F F N
①関連性を洗い出す。一つ先まで
・結果を全てつなげると、A,Bから見た一つ先までのテーブルができます。
ColA ColB
A A
A B
A C
A G
A H
B A
B B
B C
B D
B H
C A
C B
C C
C D
D B
D C
E E
E F
E N
F E
F N
G A
H A
H B
N E
N F
N N
②関連するデータをまとめて、連番を付番
・ColAに属するColBのデータをString_aggでまとめて一つにします。
まとめ方はColBの昇順。(以降Sortkey)
今回は、String_aggを使用して結合していますが、
2016以前の場合は、xml pathを使用してください。
・SortKey基の最初の1文字とColAの昇順で連番を振ります。
(以降no)
・SortKeyは関連性の近いグループの集まりになります。
また集まりを使用して昇順に連番を振ることで、
SortKeyのグループの中で上から順番に若いアルファベットに
なります。
・SortKeyの一桁を使って連番を振るのは、データの前後の仕方によって、
グループ内の順番がずれてしまう場合があるためです。
・テーブルは,no,ColA(以降Col),Sortkeyとします。
,sub as (
select
ROW_NUMBER() over (order by left(Sortkey,1),ColA) as no
, tbl.ColA as Col
,tbl.Sortkey
from (
select
*
,(
select
string_agg(tbl.ColB,'' ) as Sortkey
from
main as tbl
where
main.ColA = tbl.ColA
group by ColA
) as Sortkey
from main
) as tbl
group by tbl.ColA,Sortkey
)
ColA ColB
A A
A B
A C
A G
A H
B A
B B
B C
B D
B H
C A
C B
C C
C D
D B
D C
E E
E F
E N
F E
F N
G A
H A
H B
N E
N F
N N
②関連するデータをまとめて、連番を付番
・ColAに属するColBのデータをString_aggでまとめて一つにします。
Col SortKey
A ABCGH
B ABCDH
C ABCD
D BC
E EFN
F EN
G A
H AB
N EFN
②関連するデータをまとめて、連番を付番
Col SortKey
A ABCGH
B ABCDH
C ABCD
G A
H AB
D BC
E EFN
F EN
N EFN
・SortKey基の最初の1文字とColAの昇順で連番を振ります。(以降no)
・SortKeyは関連性の近いグループの集まりになります。
また集まりを使用して昇順に連番を振ることで、
SortKeyのグループの中で上から順番に若いアルファベットに
なります。
no Col SortKey
1 A ABCGH
2 B ABCDH
3 C ABCD
4 G A
5 H AB
6 D BC
7 E EFN
8 F EN
9 N EFN
Aが属するグループ
Bが属するグループ
Eが属するグループ
各グループの昇順に付番
Col SortKey
A ABCGH
B ABCDH
C ABCD
G A
H AB
D BC
E EFN
F EN
N EFN
②関連するデータをまとめて、連番を付番
・SortKeyの一桁を使って連番を振るのは、今回はグループ内の昇順としたいのですが、
データの関連性の仕方によって、グループ内の順番がずれてしまうケースがあるためです。
この場合、次の処理でNGになります。
no Col SortKey
1 G A
2 H AB
3 C ABCD
4 B ABCDH
5 A ABCGH
6 D BC
7 E EFN
8 N EFN
9 F EN
Aが属するグループ
Bが属するグループ
Eが属するグループ
全ての桁で付番号
グループごとの
Colの昇順に
なってない!
③グループの開始のデータとそれ以外を分ける
・②のデータは、グループのアルファベット昇順になっており、
かつ、SortKeyはColに対する関連するアルファベットの集まり。
・自分の上にあるデータのSortKeyに自分のColが含まれている
場合は、関連性が続いている。
逆に存在しない場合は、グループの最初のアルファベットに
なるということ。
・ここでは、開始のアルファベットと2番目以降のアルファベット
を区分わけします。
・区分けしたら、開始と2番目以降のグループに分けてnoの昇順で
再付番します。
,sub2 as (
select
ROW_NUMBER() over (partition by chk order by no) as no
,Col,Sortkey,chk
from
(
select
no
,Col
,Sortkey
,(
select
case count(*) when 0 then 0 else 1 end
from
sub as sub2
where
sub.no > sub2.no
and sub2.Sortkey like '%' + sub.Col + '%’
) as chk
from sub
) as chk2
)
・自分の上にあるデータのSortKeyに自分のColが含まれるデータがない場合は、0(開始)
・含まれるデータがある場合は、1(2番以降)を付番
no Col SortKey
1 A ABCGH
Aの場合、自分より上にデータがないので0(開始)
no Col SortKey
2 B ABCDH
Bの場合、自分より上にAデータがあり、AのSortKeyにBが含まれるので1(2番目以降)
no Col SortKey
6 D BC
no Col SortKey
1 A ABCGH
Dまで同じ1(2番目以降)
no Col SortKey
1 A ABCGH
2 B ABCDH
3 C ABCD
4 G A
5 H AB
③グループの開始のデータとそれ以外を分ける
no Col SortKey
1 A ABCGH
2 B ABCDH
3 C ABCD
4 G A
5 H AB
6 D BC
7 E EFN
8 F EN
9 N EFN
・自分の上にあるデータのSortKeyに自分のColが含まれるデータがない場合は、0(開始)
・含まれるデータがある場合は、1(2番以降)を付番
no Col SortKey
7 E EFN
Eの場合、自分より上にデータはあるが、SortKeyにEがないので0(開始)
no Col SortKey
8 F EN
9 N EFN
F以降は、自分より上にデータがあり、かつSortKeyにColが含まれるので1(2番目以降)
no Col SortKey
7 E EFN
8 F EN
no Col SortKey
1 A ABCGH
2 B ABCDH
3 C ABCD
4 G A
5 H AB
6 D BC
SortKeyにEがない
③グループの開始のデータとそれ以外を分ける
・区分けしたら、開始と2番目以降のグループに分けてnoの昇順で再付番します。
no Col SortKey chk
1 A ABCGH 0
2 B ABCDH 1
3 C ABCD 1
4 G A 1
5 H AB 1
6 D BC 1
7 E EFN 0
8 F EN 1
9 N EFN 1
Chk列に0or1を追加
再付番
no Col SortKey chk
1 A ABCGH 0
2 E EFN 0
1 B ABCDH 1
2 C ABCD 1
3 G A 1
4 H AB 1
5 D BC 1
6 F EN 1
7 N EFN 1
③グループの開始のデータとそれ以外を分ける
④グループの開始とそれ以外を再帰で比較、結合していく。
,fin (Grp_no,Col,Sortkey,Row_no) as (
select
anc.no
,anc.Col
,anc.Sortkey
,0
from
sub2 as anc
where
anc.chk = 0
union all
select
fin.Grp_no
,case when fin.Sortkey like '%'+ Rec.Col + '%' then Rec.Col else fin.Col end
,fin.Sortkey + case when fin.Sortkey like '%'+ Rec.Col + '%' then Rec.Sortkey else '' end
,fin.Row_no + 1
from
fin
inner join
sub2 as Rec
on (fin.Row_no + 1 = Rec.no and Rec.chk = 1)
)
④グループの開始とそれ以外を再帰で比較、結合していく。
・再帰で作成するテーブルは、以下の4つ
Group_No ⇒ chk=0(開始)のno
Col ⇒ chk=0 または chk=1 のCol
SortKey ⇒ chk=0 または chk=0 + chk=1 のSortKey
Row_no ⇒ chk=0(アンカー)のno + 1 (再帰時にchk=1のnoとの結合条件に使用)
・再帰CTEを使って、chk=0をアンカーにし、chk=1をno1から順番に結合します。
・例題ではchk=1が7件なので、7回ループします。
・chk=0のSortKeyにchk=1のColが含まれている場合は、関連性のあるデータと判断
⇒chk=1のColをセット、chk=0のSortKeyとchk=1のSortKeyを結合してセット
・ chk=0のSortKeyにchk=1のColが含まれない場合は、関連性のないデータと判断
⇒chk=0のColをセット、chk=0のSortKeyをセット
④グループの開始とそれ以外を再帰で比較、結合していく。
Group_No Col SortKey Row_No
1 A ABCGH 0 + 1(結合時)
2 E EFN 0 + 1
1回目 Row_No + 1 とnoを結合
Col=Bは、Row_No=0のSortKeyに含まれる
no Col SortKey chk
1 B ABCDH 1
Group_No Col SortKey Row_No
1 B ABCGHABCDH 1
2 E EFN 1
作成データ
Group_No=1のSortkeyを結合
1回目終了時のテーブル(アンカー+1回目データのUnion all)
Group_No Col SortKey Row_No
1 A ABCGH 0
1 B ABCGHABCDH 1
2 E EFN 0
2 E EFN 1
④グループの開始とそれ以外を再帰で比較、結合していく。
Group_No Col SortKey Row_No
1 B ABCGHABCDH 1 + 1(結合時)
2 E EFN 1 + 1
2回目 Row_No + 1 とnoを結合
Col=Bは、Row_no=1のSortKeyに含まれる
no Col SortKey chk
2 C ABCD 1
Group_No Col SortKey Row_No
1 C ABCGHABCDHABCD 1
2 E EFN 1
2回目終了時のテーブル(2回目データのUnion all)
Group_No Col SortKey Row_No
1 A ABCGH 0
1 B ABCGHABCDH 1
1 C ABCGHABCDHABCD 2
2 E EFN 0
2 E EFN 1
2 E EFN 2
・左記の様に、関連するデータについては、
次の情報が更新され、関連しない場合は、
前回の情報が引き継がれるため、次も同じ
条件で比較できる。
・自分より上にデータが存在するかというやり方も
可能だが、再帰の回数にしたがってデータが反比例に
増えていくので激重になります。
作成データ
Group_No=1のSortkeyを結合
④グループの開始とそれ以外を再帰で比較、結合していく。
3から飛ばして6回目 Row_No + 1 とnoを結合
Col=Fは、Row_no=5のSortKeyに含まれる
Grp_no Col Sortkey Row_no
1D ABCGHABCDHABCDAABBC 5 + 1 (結合時)
2E EFN 5 + 1 (結合時)
no Col SortKey chk
6 F EN 1
Group_No Col SortKey Row_No
1 D ABCGHABCDHABCDAABBC 1
2 F EFNEN 1
6回目で二つ目のグループの処理が始まる。
終了時のテーブル(6回目データのUnion all)
Grp_no Col Sortkey Row_no
1 A ABCGH 0
1 B ABCGHABCDH 1
1 C ABCGHABCDHABCD 2
1 G ABCGHABCDHABCDA 3
1 H ABCGHABCDHABCDAAB 4
1 D ABCGHABCDHABCDAABBC 5
1 D ABCGHABCDHABCDAABBC 6
2 E EFN 0
2 E EFN 1
2 E EFN 2
2 E EFN 3
2 E EFN 4
2 E EFN 5
2 F EFNEN 6
作成データ
Group_No=2のSortkeyを結合
④グループの開始とそれ以外を再帰で比較、結合していく。
最後の処理が終わった状態
Grp_no Col Sortkey Row_no
1A ABCGH 0
1B ABCGHABCDH 1
1C ABCGHABCDHABCD 2
1G ABCGHABCDHABCDA 3
1H ABCGHABCDHABCDAAB 4
1D ABCGHABCDHABCDAABBC 5
1D ABCGHABCDHABCDAABBC 6
1D ABCGHABCDHABCDAABBC 7
2E EFN 0
2E EFN 1
2E EFN 2
2E EFN 3
2E EFN 4
2E EFN 5
2F EFNEN 6
2N EFNENEFN 7
⑤Group_noとColで重複除外し、並べ替える。
select
distinct Grp_no,Col
from fin
order by Grp_no,Col
Grp_no Col Sortkey Row_no
1 A ABCGH 0
1 B ABCGHABCDH 1
1 C ABCGHABCDHABCD 2
1 G ABCGHABCDHABCDA 3
1 H ABCGHABCDHABCDAAB 4
1 D ABCGHABCDHABCDAABBC 5
1 D ABCGHABCDHABCDAABBC 6
1 D ABCGHABCDHABCDAABBC 7
2 E EFN 0
2 E EFN 1
2 E EFN 2
2 E EFN 3
2 E EFN 4
2 E EFN 5
2 F EFNEN 6
2 N EFNENEFN 7
Grp_no Col
1 A
1 B
1 C
1 D
1 G
1 H
2 E
2 F
2 N
完成!

More Related Content

PPTX
Procedure、function create hanson
PPTX
Recommend parameter query
PPTX
Sqlserver window function
PDF
2024 Trend Updates: What Really Works In SEO & Content Marketing
PDF
Storytelling For The Web: Integrate Storytelling in your Design Process
PDF
Artificial Intelligence, Data and Competition – SCHREPEL – June 2024 OECD dis...
PDF
How to Leverage AI to Boost Employee Wellness - Lydia Di Francesco - SocialHR...
PDF
2024 State of Marketing Report – by Hubspot
Procedure、function create hanson
Recommend parameter query
Sqlserver window function
2024 Trend Updates: What Really Works In SEO & Content Marketing
Storytelling For The Web: Integrate Storytelling in your Design Process
Artificial Intelligence, Data and Competition – SCHREPEL – June 2024 OECD dis...
How to Leverage AI to Boost Employee Wellness - Lydia Di Francesco - SocialHR...
2024 State of Marketing Report – by Hubspot
Ad

Lt second