sql スタイルガイド・コーディング規約 を考える

sqlのスタイルガイドについて、自分の経験や様々な記事の内容を踏まえて自分なりにベストなものを考えてみた。

## 予約語/関数は、大文字か小文字か?

予約語は大文字を使うという意見が多い中、私は"小文字"をオススメします。
##### 小文字をオススメする理由
:slight_smile:大文字より小文字の方が表示に使う面積を削減できる！
大文字の方が小文字に比べて面積を取るので予約語が増えてくるとクエリ全体が窮屈な感じになって見にくくなると思っています。  

:slight_smile:レビューのコストを削減する！
予約語を大文字にするスタイルで統一していたとき、たまに「あれ?これ予約語だけど小文字のままだよ」みたいな指摘をする機会がありました。レビューする側も実装する側も予約語がちゃんと大文字になっているかという観点で気にしないといけないためその分コストがかかっていました。小文字ならそんなことは考えなくてOKです。

例を以下に書きます。

```sql:
-- 大文字の例
SELECT
  id,
  email,
  name
FROM
  users
WHERE
  users.id = '1'
;

-- 小文字の例
select
  id,
  email,
  name
from
  users
where
  users.id = '1'
;
```
予約語が少ないうちは大文字でも違和感ない。
次に予約語が増えた場合の例を書きます。 大文字の場合は少し詰まっている印象を受けます。

```sql:
-- 大文字の例
SELECT
  id,
  CASE
    WHEN users.email IS NULL
      THEN 'emailなし'
    ELSE
      THEN email
  END AS email,
  REPLACE(address, "住所", ""),
  CONCAT(first_name, last_name) as name
FROM
  users
WHERE
  users.id = '1'
;

-- 小文字の例
select
  id,
  case
    when users.email is null
      then 'emailなし'
    else
      then email
  end as email,
  replace(address, "住所", "") 
  concat(first_name, last_name) as name
from
  users
where
  users.id = '1'
;
```

## XX句の後は改行&先頭を揃える

以下は後ろを改行して先頭は全て揃えています。
句を先頭に持ってくることで、句の切れ目がわかりやすくなり読みやすくなります。

- select
- from
- where
- group by
- order by

```sql
select
  id,
  email,
  name
where
  users
order by id
;
```
## where句のandについて
### where句のand後ろは改行&where句と同じインデント 

句で改行という書き方は結構あると思いますが、where句のandをwhereと同じインデントで書くというのはないようですので以下に例を書きます。

- :thinking:where句のandの後ろに条件を書く例  
気になる点:where句の最初の条件だけ他とずれてみえてしまう。

```sql

where
  users.id > 1
  and users.email is not null
  and users.point > 1000
  and users.active_flag = true
order by id
;
```

- :thinking: where句のandの後は改行  
気になる点:andと条件が同じインデントで見づらい。

```sql
where
  users.id > 1
  and
  users.email is not null
  and
  users.point > 1000
  and
  users.active_flag = true
order by id
;
```

- :innocent:where句のandの後は改行&インデントはwhere句と同じにする  
  条件とandのインデントを分けて条件だけを見れるようになりました！

```sql:
where
  users.id > 1
and
  users.email is not null
and
  users.point > 1000
and
  users.active_flag = true
order by id
;


```

##文末のセミコロンは最終行に書く

これは他の記事などでもみなさん同じことを書かれていました。
クエリを作成する際に、セミコロンは最終行に書くようにした方が、セミコロンがある位置は必ず先頭と決まるのでつけ忘れのミスを防止できます。

```sql:
select
  id,
  name
from
  users
;

-- 最終行につけない場合の例
-- バッチ処理などでクエリを続けて何本か書くときにつけ忘れてしまいます。。
-- 以下は2番目のクエリのセミコロンを忘れている例

select
  id,
  email
from
  users;

select
  id,
  email
from
  users

select
  id,
  email
from
  users;


```

##インデントはスペース2つ

スペース2つが全体としてある程度まとまって見える。それより多いと逆に読みにくくなる。

```sql
-- スペース2つ
select
  id,
  name
from
  users
where
  id = 1
and
  name = 'test'
;

-- スペース4つ
select
    id,
    name
from
    users
where
    id = 1
and
    name = 'test'
;
```
## テーブルには適切な別名をつける

長いテーブル名なんて作らなければいい。と言ってしまいたいところなんですが、すでに長いテーブル名のものがあったり現実はそうもいかない。。  
別名は頭文字1文字とする書き方などもありますが省略しすぎると、コードを読みながら何のテーブルなのかが頭に入って来ないので極端に削らずある程度ルールを持たせて初見でも意味が推測できる略し方くらいがいいかなと思います。3文字で書くなど厳密に決めず別名はある第三者がみてわかる。長すぎない。無駄がない。くらいで十分かと。  
以下の場合は、user_xxxのテーブルを複数結合するケースです。この場合は、user_xxxというテーブルしか登場してこないのでその後ろ(user_xxxのxxx)を別名としてしまえばいいと思います。

```sql
select
  setting.id,
  log.id,
  hist.id
from
  user_setting setting,
  user_log log,
  user_history hist
where
  setting.user_id = log.user_id
and
  log.user_id = hist.user_id
;
```
以下の場合は、user_logとaccess_logと似た名前を結合するケースです。この場合は、logという別名をつけるわけにはいかないのでu_xxxという別名をつけています。access_logはa_log。

```sql
select
  u_setting.id,
  u_log.id,
  u_hist.id,
  a_log.id
from
  user_setting u_setting,
  user_log u_log,
  user_history u_hist,
  access_log a_log
where
  u_setting.user_id = u_log.user_id
and
  u_log.user_id = u_hist.user_id
and
  u_log.user_id = a_log.user_id
;
```





## 内部結合(INNER JOIN)の書き方ついて

内部結合の場合、inner join句を使用せずに記載することもできます。(mysql、
sqlserverで確認済み) 
from句に結合したいテーブルを書いてwhere句で結合条件を書けばOKです。 
この書き方の場合のポイントは以下です。

:slight_smile:inner join句がないので少ないコード量でかける
:thinking:where句で結合条件をまとめて書くので結合するテーブル数が多いと一部のテーブルの結合条件が抜けてしまうことがある(抜けているのに気がつかない)
:thinking:外部結合の場合はleft join句を使用する必要があるのでこの書き方ができるのは内部結合の場合のみ

2つか3つくらいのテーブルを内部結合をする場合にはこの書き方の方が速いですが、結合するテーブルが増えてきたときは、向いていないかもしれません。

```sql

-- inner join句を使う
select
  id,
  name
from
  users
inner join books
  on (users.id = books.user_id
       and
      users.active_flg = true)
;

-- inner join句を使わずに内部結合
select
  id,
  name
from
  users,
  books
where
  users.id = books.user_id
and
  users.active_flg = true
;
```

以上です。ここにあるのを正解としたい訳ではなく、良いと思うものがあれば参考にしていただければと思います。また以下記事も非常に参考になりました。
https://qiita.com/taise/items/18c14d9b01a5dfd6d35e