読みやすいSQLについて

# 読みやすいSQLについて

はじめましてこんにちは
@cosemi001 と申します。


貴重なお時間に本記事の閲覧いただき、感謝いたします。


本題に入ります。

様々な案件の成果物のSQLを見たときに
可読性が低い書き方が多く含まれていました。


その事が気になったので
自分が読みやすいとSQL記述方法を
「NG例」と「OK例」 併せて紹介いたします。


なお私も勉強中の人間なので、すべて鵜呑みにせず
今後の参考になれば幸いです。
ご意見ございましたらコメントにてご指摘ください。




## ■改行とインデント（字下げ）を利用しましょう。

### ・NG例
```
select * from users;
```
### ・修正例
```
select
	 * 
from
	 users;
```


### ・解説
何故こうするか？
「SELECT」「FROM」はそれぞれ役割が異なるので
役割が異なるたび改行と字下げを行いましょう。

```
「SELEC」Tは取得したいフィールドをこれから指定するよ宣言です
select 
	-- フィールドを指定するときはインデントを一つ下げましょう
	-- そうするとSELECTで指定されたフィールド名がわかりやすくなります。
	
	フィールド名,「フィールド名毎に改行しましょう」
	フィールド名
	
「FROM」はデータを取得するテーブルを指定するよ宣言です
from
	-- テーブルを指定するときはインデントを一つ下げましょう
	-- そうするとFROMで指定されたテーブルがわかりやすくなります。
	
	テーブル名;
```
	
## ■予約語は大文字にしましょう。
SQLには予約語というものが存在します。

### [Tip]予約語とは？
役割を与えられた言葉です。

たとえば「SELECT」はこれからフィールドを指定するよ！という役割を持ちます。
細かいことは割愛いたします。（いっぱい出て来るからググってね！）

では早速書き直してみましょう

### ・NG例
```
select
	 name,
	 email,
	 address
from
	 users 
where 
	name like "名前";
```	

### ・修正例
```
SELECT
	name,
	 email,
	 address
FROM
	users
WHERE
	name LIKE "名前";
```	
### ・解説
いかがでしょうか？
「字下げ」＋「予約語を大文字」にするとメリハリが出て
可読性は向上すると思います。


### □補足
DB設計にて「テーブル名」、「フィールド名」を大文字で定義する案件が存在します。
その場合は予約語を小文字にするなどになるかと思いますが、臨機応変に使用してください。



## ■テーブル名に別名を使用してみよう
テーブル設計を行うときに
テーブルの役割を具体化するときどうしても名前が長くなるケースがあります。
その場合、テーブル名を英語読みした時の頭文字を大文字にして使用しましょう。

### ・普通に書いた例
```
SELECT
	mst_company_details.id,
	mst_company_details.name,
	mst_company_details.address,
FROM
	mst_company_details
WHERE
	mst_company_details.name LIKE "サンプル株式会社";
```

### ・修正例
```
SELECT
	MCD.id,
	MCD.name,
	MCD.address,
FROM
	-- 対象のテーブル記述した後に「AS」を使用して定義します（ASは省略可能で半角スペースあけた後に別名定義も可能です）
	mst_company_details AS MCD
WHERE
	MCD.name LIKE "サンプル株式会社";
```	

### ・解説
いかがでしょうか
SQL文の横伸びが短くなるだけでも、可読性は向上すると思います。


□補足
１．
別名をつける際にASを使用する可はプロジェクトの方針に従うようにしましょう。

私は「AS」をつけて今から別名つけるよ感を出して
別名をつけている事を認識し易くするのが好きです。


２．
テーブル名が短い場合[userなど]は無理に別名を使うとわかりにくいケースもあります。
その場合は無理に別名を使用せず、そのまま使ってもよいかもしれません



## ■TABLE JOINも改行インデントしてみよう

SQLの書き方で「JOIN」というものが存在します。


### [Tip]JOINとは
テーブル同士などを結合する事ができます。
具体的には、結合するテーブルのフィールド値の関係性を指定することにより
指定した条件で結合しデータを取得可能となります。

### ・NG例
```
SELECT
	*
FROM
	mst_company AS MC INNER JOIN mst_company_details AS MCD ON MC.id = MCD.company_id INNER JOIN mst_users AS MU ON MC.id = MU.company_id
FROM
	MC LIKE "テスト株式会社";
```



### ・修正例
```
SELECT
	*
FROM
	mst_company AS MC 
	INNER JOIN 
		mst_company_details AS MCD 
		ON 
			MC.id = MCD.company_id
			
	INNER JOIN 
		mst_users AS MU 
		ON 
			MC.id = MU.company_id
FROM
	MC LIKE "テスト株式会社";
```

### ・解説
```
SELECT
	*
FROM
	mst_company AS MC 
	-- 「INNER JOIN」は結合するテーブル定義宣言なので「改行」＋「インデント下げ」を行います
	INNER JOIN 
		mst_company_details AS MCD 
		-- 「ON」はよくあるケースはテーブル同士の結合条件なので「改行」＋「インデント下げ」を行います
		ON 
			MC.id = MCD.company_id
	-- 次の「JOIN」はJOINのインデントまで戻りましょう
	INNER JOIN 
		mst_users AS MU 
		ON 
			以下略～
```

NG例と見比べると読みやすくなっていると思います。

この改行で何故見やすくなったか解説すると
NG例では
SQLを上から下に読みながら
左右にも読み解かなければいけません


修正例では
SQLを上から下に読む事が可能です。
インデントがある事により役割ごとに階層が分かれている為
各行が何の役割かすぐ理解できるようになります。


## ■CASE句も改行してみよう

###・普通の例
```
SELECT
    CASE WHEN users.age > 20 THEN "下級生" ELSE "上級生" END AS class_type
FROM
	users;
```


### ・修正例
```
SELECT
    CASE 
		WHEN users.age > 20 THEN
			"下級生"
		ELSE
			"上級生"
	END AS class_type
FROM
	users;
```
	

### ・解説
上の解説同様
上から下に読む事が出来てると思います。

某初心者向けサイトは
条件と結果を一行で書いたサンプルを上げていますが
条件分岐と出力結果が同じ行に存在し可読性を低下させます。

NGではないがGoodではない例：
CASE 
	WHEN users.age > 20 THEN "下級生"
	ELSE "上級生"
END AS class_type



## ■サブクエリも「改行」＋「インデント下げ」+その他諸々

### ・NG例
```
SELECT
	*
FROM
	mst_users AS MU
    INNER JOIN
		(select trn_user_options.* from trn_user_options where trn_user_options.type like "A" ) AS TUO
        ON
			users.email = TUO.email
```

### ・修正例
```
SELECT
	*
FROM
	mst_users AS MU
    INNER JOIN
		(
			SELECT 
				TUO.* 
			FROM 
				trn_user_options AS TUO
			WHERE 
				TUO.type like "A" 
		) AS TUO
        ON
			users.email = TUO.email
```

### ・解説
サブクエリの中身もわかりやすくなったと思います。

サンプルサブクエリ自体はかなり無意味な処理なのでそこはご愛嬌で・・




## ■まとめ（長い構文も同じように書き換えよう）

### ・業務で見たことある酷い例の再現
```
		SELECT MC.name,MC.sex,TLC.name AS "chileren_cat",
CASE WHEN MC.age > 20 THEN"若い猫"ELSE"年老いた猫"END AS age_type
	FROM mst_cat AS MC INNER JOIN tbl_lettle_cat AS TLC ON MC.id = TLC.channel_id
INNER JOIN(SELECT AVG(height) AS height,AVG(weight) AS weight FROM tbl_cat_status WHERE alive = true AND delete_flag = false GROUP BY cat_id) AS TCS
WHERE MC.id = 1 AND TLC like "ミケ"AND TCS < 40 GROUP BY MC.id ORDER BY MC.id LIMIT 10
```
### ・修正例
```
SELECT 
    MC.name,
    MC.sex,
    TLC.name AS "chileren_cat",
    CASE 
		WHEN MC.age > 20 THEN
			"若い猫"
		ELSE
			"年老いた猫"
	END AS age_type
FROM
    mst_cat AS MC
    INNER JOIN
		tbl_lettle_cat AS TLC
        ON MC.id = TLC.channel_id
	INNER JOIN
		(
			SELECT
				AVG(height) AS height,
                AVG(weight) AS weight
			FROM
				tbl_cat_status
			WHERE
				alive = true
                AND delete_flag = false
			GROUP BY
				cat_id
        ) AS TCS
WHERE
	MC.id = 1
    AND TLC like "ミケ"
    AND TCS < 40
GROUP BY 
	MC.id 
ORDER BY
	MC.id
LIMIT
	10
```



後者のほうが見やすいでしょ！？


未来の自分の為にも
引継ぐ後任の為にも
見やすい書き方を意識してみんなで幸せになりましょう。

# 「めんどくせぇ！！」「何もしてないけど勝手に壊れた！！」という人向け
## おまけ
細かいことは端折りますが
MySQL WorkBenchなら「Ctrl」+「B」を押すと自動インデントしてくれます
（完璧ではないケースがあります）

上記紹介したケースはこの機能では網羅できませんが、
ある程度整いますよ