「分析SQLスタイルガイド」をかなり真面目に考えた

# 目次

- [なぜSQLのスタイルガイドが重要なのか](#なぜSQLのスタイルガイドが重要なのか)
- [この記事の目的](#この記事の目的)
- [この記事の対象者](#この記事の対象者)
- [分析SQLスタイルガイドの指針](#分析SQLスタイルガイドの指針)
- [基本ルール](#基本ルール)
- [命名規則](#命名規則)
- [インデントルール](#インデントルール)
- [別名ルール](#インデントルール)
- [joinルール](#joinルール)
- [クエリ分割ルール](#クエリ分割ルール)

⭐ **コメント欄で「いや私はこう思う！」という意見をたくさんいただきました！ぜひそちらも御覧ください！**（決して揶揄ではないです）

# なぜSQLのスタイルガイドが重要なのか
SQLはプログラミング未経験者でもとっつきやすい言語と言われ、エンジニアや分析を本業としていない人でもSQLを使う機会が増えてきていると思います。

そんなSQLですが、[こちらのブログ](https://techlife.cookpad.com/entry/2016/11/09/000033)でも指摘されている通り、**一般的なスタイルガイドが定まっていません**。スタイルガイドとはコードの書き方マナーようなもので、どこで改行するか、空白はいくつ入れるか、大文字を使うかなどの諸々を指します。

もしスタイルガイドが無いとこんな事が起こります

- コードに一貫性が無いためミスを発見しづらくなる
- クエリを書くたびに書き方を検討するのに頭を使う
- コードを読みづらくレビュー効率が下がる
- コードを解読するのが大変で再利用されず、組織の資産になりにくい

# この記事の目的

この記事は自分のコーディングスタイルを他人に押し付けるものでも、自分以外のコーディングスタイルを批判するものでもありません。
あくまで、**分析SQLスタイルガイドの一例**を紹介するもので、どこかのチームがSQLのスタイルガイドを決める際、このブログが参考になればと思っております。

また、このコーディングガイドに対する反論も[Twitter](https://twitter.com/kai_data)等でお待ちしております！

# この記事の対象者

私は普段データ分析を職にしていて、エンジニアではありません。
この記事の対象者も**SQLを使って分析をする分析者向け**のものとなります。

よってシステムに組み込まれるようなSQLを想定しているわけではありませんが、共通点はあるはずですのでエンジニアの方の意見もお待ちしております。

# 分析SQLスタイルガイドの指針

スタイルガイドの紹介の前に、自分が大事にしている３つの指針をお伝えします。すべてのルールはこの指針に基づいて作られています、

## 手間を減らす

これは**コーディングガイドを定める目的**とも言いかえられる、最も大事にしている指針です。すべてのルールは手間を減らすためにあり、手間が増えて見栄えが美しくなるガイドは無意味です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/b9a19079-210d-c559-7ce0-c59f24237367.png)


## 縦と横を意識する
私はSQLを書く際、縦と横のラインを強く意識しています。縦のラインはインデントによって作られ、横のラインは改行によって作られます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/ddacacce-e6bd-5d39-f3b5-9273203f22a2.png)


## 役割ごとに分ける
オブジェクト指向とまでは言いませんが、役割の異なるクエリを同じ場所に書くことは人間の理解を妨げます。データの種類や処理の粒度が大きく異なるときはクエリを分けるべきです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/692076e4-0248-5d37-056a-a00fb4594e08.png)

# 基本ルール
SQLを書く上で全体に影響する基本のルールです

## 予約語も全部小文字で書く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/f40cb6f8-2b8a-eba6-5445-2a4e4402369c.png)
**【メリット】コーディングが楽になる**
いきなり好みが分かれるルールですみません。**私はすべて小文字を強く押します**。慣習で大文字にしている人が多いと思いますが、一度小文字で書いてみてください。楽さにびっくりしますよ。

見ずらいという人も居ますが、普通のエディターならシンタックスハイライトされますし、キレイにインデントしていれば容易に分かります。一番使う予約語はselect,from,and,orですがこれを毎回大文字にする苦労と、見分けやすくなるメリットどちらが大きいかは明らかだと思います。
いきなり熱が入りすぎました。ここからは落ち着いていきます。

## 左揃えで書く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/a42e800e-c248-81eb-f74d-0d6f78ed0243.png)
**【メリット】コーディングが楽になる** 
この記事を書くにあたり、ネット上の色々なコーディングルールを調べたところ、中央線に寄せて書く流派が一定数いることにかなり驚きました。

右揃えの場合、左端のインデント数がselectやfromで異なるので毎回打たないといけませんが、左揃えならタブを半角スペース4つに設定しておけば、１ステップでインデントできます。

## インデントはタブを使わず半角スペース4つで統一
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/62e2c24f-02a2-3a53-c633-cfae274dc1ad.png)
**【メリット】クエリの書き換えが楽になる**

多くの場合、タブと半角スペースは肉眼で見分けが付きません。混在していると、クエリを書き換えるときに「....っおいタブかい！」となります。タブはエディターによって見え方が半角スペース4つ分だったり8つ分だったりするので、半角スペースに統一します。

半角スペースの数は４が視認性が良いのでそうしていますが、正直2でもいいと思ってます。戦う意志はありません。4つだと横に長くなりすぎると言うのであればそれはクエリに問題があると思います

## カンマ（,）/and/orは先頭に置く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/8dbcfe52-93e4-3ccc-5fd4-72b2f5fc9240.png)
**【メリット】クエリのミスが減る**
後ろよりも先頭にある方がカンマ/and/orの視認性がよく、入力忘れが減ります
先頭カンマが違和感あるという人が多いですが、個人的にはandが先頭にあるのにカンマが文末にあるチグハグ感の方が違和感があるほか、列の増減作業が楽というメリットもあります。
⭐ **私はカンマ文頭派ですがカンマ文末派のほうが多いようです！**

andやorを単独行に置く人も居ますが、先頭に置くことでwhere句の行数＝条件の数になるのでクエリを読み解きやすいです。

## 1つのselectごとにクエリの説明を書く

**【メリット】クエリを読みやすくなる** 

with句やサブクエリによって、ひとつのクエリの中に複数のselectが入ることがあります。最低限その単位でクエリの説明を書くことは、小さな苦労ですが読む側にとってはとてもありがたいものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/3f3b29c3-d3da-005a-a9e6-101d8bff896c.png)


# 命名規則
asで列名やテーブル名を決める際の命名規則を定めます。

## 基本ルール

**すべて小文字で書き、アンダーバーでつなぐ（[snake case](https://qiita.com/deerboy/items/f035b9044edf9a51aff7)）**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/b468daec-7b32-561a-3ccd-d8353f2686d7.png)

**略語は使わない** 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/63b53492-b8b9-a8a4-5255-478896c45dfa.png)

## 列名

列の内容によって接尾辞を定めておくことで、統一性があって見やすく毎回考える手間が省ける

| 接尾辞 | 利用タイミング |
|:-:|:-:|
| _id  | primary keyなどのidカラム  |
| _flag   | フラグなどの何らかのステータス  |
| _date   | 日付に関するカラム  |
| _name   | 何かの名前を意味するカラム  |
| _ratio   | 比率を表すカラム  |
| _sum   | sum()の取得カラム  |
| _count   | count()の取得カラム  |
| _min   | min()の取得カラム  |
| _max   | max()の取得カラム  |

## テーブル名
分析においてテーブル名は柔軟に決めるべきで、決めすぎるべきではないと思っています。ここで示す接尾時はルールではなく考え方の参考としてください。

| 接尾辞 | 利用タイミング |
|:-:|:-:|
| _act  | 行動単位のテーブル  |
| _user   | ユーザユニークのテーブル  |
| _day  | 日別のテーブル  |
| _month  | 月別のテーブル  |

# インデントルール
1段目から3段目にかけて徐々に処理の粒度を細かくしていく。同じ階層に異なる粒度の処理を書かない。
**【メリット】コードを構造的に読むことができ、理解しやすい**

## 1段目は処理の大区分を示す句だけ（select,from,groupby,with,orderby）

一段目は大区分にすることで、コード全体のどこに何が書いてあるかがすぐに分かる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/4fcecf78-f064-26a4-f651-2f8c5eb812f3.png)

## 2段目は処理関数、列名、テーブル名を並べる（列名、テーブル名、case whenのcase）
2段目を見ることで処理の概要とインプットとアウトプットが理解できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/a72fcf01-87d1-03be-91f8-c13e660e267c.png)

## 3段目は処理の補足を記載する（joinのon、case whenのwhen）
3段目を見ることで、より詳細な処理の内容を詳しく知ることができる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/5e47cc31-31ea-e1fd-c945-19dac697a613.png)

# 別名ルール
SQLの別名は、うまく使えば読みやすくなり、下手に使うと読みづらくもなります。
## **テーブルの別名は先頭の文字を使う。重複する場合は連番を振る。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/c7352004-4246-6db0-720a-b344d42b8c60.png)
別名をa,b,cやt1,t2と使う人は多く存在しますが、考える手間を省く以外にはあまり意味がありません。参照するテーブル名の先頭の文字使い、重複してしまう場合は連番を振ることで機械的に別名を決められ、手間を増やさずわかりやすくなります。

## テーブルを複数結合したクエリでカラムを指定するときはテーブルの別名を明示する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/e2fa01c4-1896-e0c7-896d-07bbd0582b7c.png)
テーブルを複数結合しているクエリでこれを行わないと、クエリを読むだけではこのカラムはどのテーブルのカラムなのか分かりません。テーブル定義書を読んだり実際にテーブルの中身をselectして調べなければいけない手間が増えます。

# joinルール
SQLのミスで最も多いのがjoinです。ここではミスを減らすスタイルガイドを紹介します。
## right joinは絶対に使わない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/b1312f5f-44e1-02be-17d4-845cd16dc6ad.png)
right joinと全く同じことはleft joinで実現できます。両者が混在すると読む人に大きな負担を与えます。人間は左側を起点に考える傾向があり、それに逆らうメリットがない以上right joinを使う理由が全くありません。

## left joinを基本とし、絞り込む意図があるときのみinner joinを使う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/a5d9efb0-d44b-bf8a-0cea-ba496d429d28.png)
inner joinは意図せず行を削ってしまい、それに気づきにくいという欠点がある。例えば購買ログデータと商品マスタをでinner joinで結合する際、マスタに不備があったとしても、出力からその行そのものが無くなるので不備に気づきにくい。

「逐一データの行数をチェックすれば問題ない」という意見はもっともですが、そもそもそのリスクは減らすべきです。

ちなみに、筆者のleft joinとinner joinの利用頻度はだいたい9:1程度です。

# クエリ分割ルール
複雑なクエリは読む側にも負担を与えます。それを少しでも軽減するルールを紹介します。
## サブクエリではなくwith句を使う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1014265/51ccd12f-24f4-0d11-4940-eea0e2c1f326.png)
with句を使うことによって、クエリを上から下に読むことができます。サブクエリにしてしまうと、クエリの構造を理解するところからはじめる必要があり間違えずに読むことに脳の神経を取られます。

## 似た処理を何回もする場合は中間テーブルを作る

データ分析でSQLを書く際、一度やって終わりではなく途中から分岐して試行錯誤することは少なくありません。そのようなときに中間テーブルを作っておけば、処理時間の短縮につながるだけでなく、別の人がクエリを読む際に量が減る効果があります。

# 最後に
ここに書いたスタイルガイドは理想論ではなく、実務で実現可能なガイドだと思っています。また、**あくまでガイドであり、状況によってはガイドに従うべきでないときもあります**。サクッと確認するだけの残らないクエリは私もここのガイドをガッツリ無視して書いています。

ぜひ、「今のやり方に慣れてるから…」「大文字で書く癖が付いてるから…」と尻込みせずこのスタイルを試してみて、感想をお聞かせください！

Twitterもやっているので、DMやリプで意見いただけると嬉しいです！

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">今朝投稿したSQLスタイルガイドのQiita記事について、全体像を可視化しました！<br>SQLはオレオレ流がはびこりがちなので、この期にコーディング規則を定めてみてはいかがでしょうか？<a href="https://twitter.com/hashtag/100%E6%97%A5%E6%8A%95%E7%A8%BF?src=hash&amp;ref_src=twsrc%5Etfw">#100日投稿</a> <a href="https://twitter.com/hashtag/69%E6%97%A5%E7%9B%AE?src=hash&amp;ref_src=twsrc%5Etfw">#69日目</a> <a href="https://t.co/C6e8KwiJ5Q">pic.twitter.com/C6e8KwiJ5Q</a></p>&mdash; かい｜図解データサイエンティスト (@kai_data) <a href="https://twitter.com/kai_data/status/1348578258057498633?ref_src=twsrc%5Etfw">January 11, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 参考
- [分析SQLのコーディングスタイル - クックパッド開発者ブログ](https://techlife.cookpad.com/entry/2016/11/09/000033)
- [SQLスタイルガイド - Qiita](https://qiita.com/taise/items/18c14d9b01a5dfd6d35e)
- [SQLスタイルガイド · SQL style guide by Simon Holywell](https://www.sqlstyle.guide/ja/)
- [プログラミングスタイルガイドのスタイルガイド - Qiita](https://qiita.com/yugui/items/960b0da38a15c4d6917b#%E3%83%A6%E3%83%BC%E3%83%A2%E3%82%A2)