読みやすい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文の横伸びが短くなるだけでも、可読性は向上すると思います。
□補足
1.
別名をつける際にASを使用する可はプロジェクトの方針に従うようにしましょう。
私は「AS」をつけて今から別名つけるよ感を出して
別名をつけている事を認識し易くするのが好きです。
2.
テーブル名が短い場合[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」を押すと自動インデントしてくれます
(完璧ではないケースがあります)
上記紹介したケースはこの機能では網羅できませんが、
ある程度整いますよ