調べ物した結果

現役SEが仕事と直接関係ないことを調べた結果を。

SQLのたてもちよこもち。たてもちからよこもちへ

SQLを触っているとよくでてきますが。そのたびにGoogle先生に問い合わせをしているような
気がしています。ということで定着させるためにも一回まとめることにしました。

とりあえず検索

SQLもテーブル構造も出ていてとてもよくまとまっているので、このURLを覚えておくだけで事足りるのでは・・・
dev.classmethod.jp

定着が目的なので、自分でもやってみます。

検証環境

AWS RDS(SQL Server Express Edition)
SSMS v18.4
を使いました。
AWSSQL Serverをたてて、SSMSで接続するシンプルな形です。

テーブル

こんなノリのやつを用意しました。
gyazo.com
ステータス事の値を横に持ちたい。ということはあるでしょうから、こんな感じでしょう。

とりあえずくっつけてみる。

WINDOW関数もなく、そのまま「えいや!」でくっつけるとこんな形のSQLになるかとおもいます。

select
 emp.employee_id,
 st1.qualification_id,
 st1.value as status_1,
 st2.value as status_2
from
 employee_qualification emp
 left join qualification as st1
 on emp.qualification_id = st1.qualification_id and st1.status = 1
 left join qualification as st2
 on emp.qualification_id = st2.qualification_id and st2.status = 2

実行結果は
gyazo.com

こんな感じで。性能がどうのこうのなければ、このままでもいいんでないかと思います。
せっかくなので、プロファイル情報も取得します。
「SETSTATISTICS PROFILE ON」で取得を有効にすると
gyazo.com

こんな形で取得できます。TotalCostは「0.0104844」
実行計画も以下の通りで。
gyazo.com

JOINをしてるので当然かもしれないですけど、やたらとNastedLoopsがでてきます。
ただただ横持ちを取りたいだけなのに、なんだかこれはいただけない感じがします。

いまはステータスが2つだけなのでいいですが。4つ5つとなった時にこれは無駄なことをしている感じが大きいです。

ということでいい感じにしていく。

今回はRow_Number等でとる必要がなさそうです。Statusわかっているから
まずは横持ちのテーブルぶぶん

select
	st.qualification_id,
	max(case st.status when 1 then st.value else '' end) as value_st1,
	max(case st.status when 2 then st.value else '' end) as value_st2
from 
	(
		select
			qualification_id
			,status
			,value
		from qualification
	) st
group by
	st.qualification_id

gyazo.com
実行計画もいい感じに落ち着いてます。これをそのまま先ほどのクエリに結合していきます。

こんな感じかと。

SET STATISTICS PROFILE ON
select
 emp.employee_id,
 st.qualification_id,
 st.status_1 as status_1,
 st.status_2 as status_2
from
 employee_qualification emp
 left join
 (
	select
		st.qualification_id,
		max(case st.status when 1 then st.value else null end) as status_1,
		max(case st.status when 2 then st.value else null end) as status_2
	from 
		(
			select
				qualification_id
				,status
				,value
			from qualification
		) st
	group by
		st.qualification_id
 ) st
 on emp.qualification_id = st.qualification_id

実行結果は同じようです。ここがずれていたら意味ないですからね。
gyazo.com
コストの方もよいほうに収まりました(0.00686062)
gyazo.com
実行計画も見ておきましょう。
gyazo.com
コンピューティングスカラーは気になりますが・・・気にしていたループの本数が減りました。やったね。
このままでも大分いい感じですが、今後のメンテナンス(出したいステータスが増えたときに)
そこそこ厄介なので、以下のようにしておきます。

select
 emp.employee_id,
 st.*
from
 employee_qualification emp
 left join
 (
	select
		st.qualification_id,
		max(case st.status when 1 then st.value else null end) as status_1,
		max(case st.status when 2 then st.value else null end) as status_2
	from 
		(
			select
				qualification_id
				,status
				,value
			from qualification
		) st
	group by
		st.qualification_id
 ) st
 on emp.qualification_id = st.qualification_id

これで単に必要なステータスが増えた場合でも、一か所修正すればOKとなります。
めでたしめでたし。欲をいえばもう少しわかりやすい位置に移動したいですが、今回はここまでとします。