Kickstarter SQL Style Guide和訳

SQLのコーディング規約を考えていたところ、Kickstarter のSQLスタイルガイドがあったのでテキトーに意訳してみる。  
※は私なりの補足や疑問を記載  

[Kickstarter SQL Style Guide](https://gist.github.com/fredbenenson/7bb92718e19138c20591)

# 目的  
再現性・透明性を維持していくことは、Kickstarterデータチームにとってのコアバリューである。    
スタイルガイドを使うことは、コアバリューを達成する手助けとなっている。  

このドキュメントはKickstarterのデータチームによって作成されたが、理路整然としたSQLを書きたい全ての人にとって有益なので公開してみる。  

**注意** :このスタイルガイドは <u>[AWS Redshift](https://aws.amazon.com/jp/redshift/)/[Postgres 8.0.2](https://www.postgresql.org/docs/8.0/static/release-8-0-2.html)</u>向けとなっています。多くのことは標準SQLにも当てはまりますが、その点ご注意ください。  

# 原則  
- コード規約に沿ってコード例を記載。  
- 定常的にGithubでコードチェックをおこなっています。  
- 一貫性のあるスタイルを守ることは非常に重要です。  
- 必要に応じてコメントを記載したり、可読性が高くなるようなネスト構造を用いてコードを記載しています。  

# ルール  
## 全体的なもの  
- インデントを用いる場合、タブじゃなくてスペース2つを使おう  
- 末尾に空白を入れるな  
- SQLの予約語は必ず大文字にしよう（例: `SELECT` , `AS` ...)  
- カラム名はアンダースコアで分けよう（※キャメルケースやパスカルケースではなく、スネークケースを用いよう）。

**GOOD:**

```sql
SELECT COUNT(*) AS backers_count
```
**BAD:**

```sql
SELECT COUNT(*) AS backersCounts
```
- コメントは必ずクエリの最上部、あるいは `SELECT` に近い部分に記載しよう  
- わかりづらい部分についてのみコメントをするようにしよう(例:何故特定のIDがハードコードされているか）  
- 一文字のカラム名は避け、わかりやすいカラム名にしよう  

**GOOD:**

```sql
SELECT ksr.backings AS backings_with_creators
```
**BAD:**

```sql
SELECT ksr.backings AS b
```
- できるだけCommon Table Expression(CTEs)を使おう（※サブクエリではなくWITH句を使おう）  
- RedshiftはHAVING句をサポートしていないのでCTE経由で抽出をしよう。言っている意味がわからん場合はDataチームの優しい誰かに理由を聞いてくれ。（※Redshiftドキュメントにその旨の記載を見当たらないけど執筆当時はサポートしてなかっただけか？）  

## SELECT  
全てのカラムは第一カラムと同じネストにして整列させよう。

```sql
SELECT  
  projects.name,
  users.email,
  projects.country,
  COUNT(backings.id) AS backings_count
FROM ...
```

`SELECT`を記載する行は`SELECT`以外書かない。
  
```sql
SELECT  
  name,  
  ...  
```
  
集約関数はいつもエイリアス名を付けよう。  
また、カラム名は「集約関数名_集約対象カラム名」にする。

```sql
SELECT  
  name,  
  SUM(amount) AS sum_amount  
FROM ...  
```
  
複数テーブルを用いている場合はいつも、テーブルのエイリアス名をカラム名に使おう。  

```  sql
SELECT  
  projects.name AS project_name,  
  COUNT(backings.id) AS backings_count  
FROM ksr.backings AS backings  
INNER JOIN ksr.projects AS projects ON ...  
```  

エイリアスを使う場合は、いつでも`AS`は省略しないようにしよう  
 **GOOD:**   

```  sql
SELECT  
  projects.name AS project_name,  
  COUNT(backings.id) AS backings_count  
...  
```  

 **BAD:**   

```  sql
SELECT  
  projects.name project_name,  
  COUNT(backings.id) backings_count  
...  
```  
WINDOW関数が長くなった場合は、`PARTITION`,`ORDER`および`frame clauses`毎に別の行に分けよう。  
また、それらは`PARTITION`の文字位置に左揃えをしよう。  
さらに、`PARTITION`キーワードは1行につき1つのみとし、はじめのキーワードの文字地に左揃えをしよう（※`ORDER BY`キーワードは？）。  
`ASC`,`DESC`は明示するようにしよう。  

```  sql
SUM(1) OVER (PARTITION BY category_id,  
                          year  
             ORDER BY pledged DESC  
             ROWS UNBOUNDED PRECEDING) AS category_year  
```  

## FROM  
`FROM`には1テーブルのみ記載するようにし、JOINをしたい場合は明示するようにしよう。  
**GOOD:**

```  sql
SELECT  
  projects.name AS project_name,  
  COUNT(backings.id) AS backings_count  
FROM ksr.projects AS projects  
INNER JOIN ksr.backings AS backings ON backings.project_id = projects.id  
...  
```  
 **BAD:**

```  sql
SELECT  
  projects.name AS project_name,  
  COUNT(backings.id) AS backings_count  
FROM ksr.projects AS projects, ksr.backings AS backings  
WHERE  
  backings.project_id = projects.id  
...  
```  

## JOIN  
内部結合のときは`JOIN`とだけ記載するのではなく、`INNER JOIN`と明示するようにしよう  
（※左外部結合は`LEFT OUTER JOIN`ではなくて`LEFT JOIN`って書き方なのは外部を明示してないけどいいのか？）  

 **GOOD:**

```  sql
SELECT  
  projects.name AS project_name,  
  COUNT(backings.id) AS backings_count  
FROM ksr.projects AS projects  
INNER JOIN ksr.backings AS backings ON ...  
INNER JOIN ...  
LEFT JOIN ksr.backer_rewards AS backer_rewards ON ...  
LEFT JOIN ...  
```  

 **BAD:**

```  sql
SELECT  
  projects.name AS project_name,  
  COUNT(backings.id) AS backings_count  
FROM ksr.projects AS projects  
JOIN ksr.backings AS backings ON ...  
LEFT JOIN ksr.backer_rewards AS backer_rewards ON ...  
LEFT JOIN ...  
```  
1つ目の結合条件は`INNER JOIN`と同じ行に書こう。  
また、2つ目以降の結合条件は行を変えて、`INNER JOIN`より1つ深いネストに書こう。  

```  sql
SELECT  
  projects.name AS project_name,  
  COUNT(backings.id) AS backings_count  
FROM ksr.projects AS projects  
INNER JOIN ksr.backings AS backings ON projects.id = backings.project_id  
  AND backings.project_country != 'US'  
...  
```  
`INNER JOIN`からはじまり、`LEFT JOIN`はその後に書くようにしよう（※わかりやすさと、結合効率の関係？）  。
 **GOOD:**

```sql
INNER JOIN ksr.backings AS backings ON ...  
INNER JOIN ksr.users AS users ON ...  
INNER JOIN ksr.locations AS locations ON ...  
LEFT JOIN ksr.backer_rewards AS backer_rewards ON ...  
LEFT JOIN ...  
```
 **BAD:**

```sql
LEFT JOIN ksr.backer_rewards AS backer_rewards ON backings  
INNER JOIN ksr.users AS users ON ...  
LEFT JOIN ...  
INNER JOIN ksr.locations AS locations ON ...  
```  

## WHERE  
複数の`WHERE`条件は別の行に、`AND`などのSQL修飾子はじまりで書こう  

```  sql
SELECT  
  name,  
  goal  
FROM ksr.projects AS projects  
WHERE  
  country = 'US'  
  AND deadline >= '2015-01-01'  
...  
```  

## CASE  
`CASE`文をフォーマットするのは簡単ではないが、`WHEN`,と`THEN`と`ELSE`を同じ位置に揃え、`CASE`と`END`の内側に入れよう。  

```  sql
CASE WHEN category = 'Art'  
     THEN backer_id  
     ELSE NULL  
END  
```  

## Common Table Expressions (CTEs)  
[AWSのドキュメント](https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/r_WITH_clause.html)では以下のように記載されてます。  
>WITH 句のサブクエリは、単一のクエリ実行中に、使用可能なテーブルを効率的に定義します。SELECT ステートメントの本文内でサブクエリを使用することで、すべてのケースで同じ結果を実現できますが、WITH 句のサブクエリの方が、読み書きが簡単になることがあります  

CTEでは`WITH`キーワードよりも1つ深いネストにしなければいけない。  
`WITH`キーワードがある行の末尾に`(`を用いて開始し、終了するときは新しい行に単独で`)`を用いるようにしよう。

```  sql
WITH backings_per_category AS (  
  SELECT  
    category_id,  
    deadline,  
    ...  
)  
```  
複数のCTEでは以下のように、閉じる部分`)`と、新規CTEの開始`(`を同じ行に記載しよう。

```  sql
WITH backings_per_category AS (  
  SELECT  
    ...  
), backers AS (  
  SELECT  
    ...  
), backers_and_creators AS (  
  ...  
)  
SELECT * FROM backers;  
```  
また、可能であれば`JOIN`が含まれるCTEは、メイン部分ではなくWITH句内に記載するようにしよう（※効率の観点？）  
 **GOOD:**

```  sql
WITH backings_per_category AS (  
  SELECT  
    ...  
), backers AS (  
  SELECT  
    backer_id,  
    COUNT(backings_per_category.id) AS projects_backed_per_category  
  INNER JOIN ksr.users AS users ON users.id = backings_per_category.backer_id  
), backers_and_creators AS (  
  ...  
)  
SELECT * FROM backers_and_creators;  
```  
 **BAD:**

```  sql
WITH backings_per_category AS (  
  SELECT  
    ...  
), backers AS (  
  SELECT  
    backer_id,  
    COUNT(backings_per_category.id) AS projects_backed_per_category  
), backers_and_creators AS (  
  ...  
)  
SELECT * FROM backers_and_creators  
INNER JOIN backers ON backers_and_creators ON backers.backer_id = backers_and_creators.backer_id  
```  
常にサブクエリではなくCTEを使うようにしよう  

## Tips  
- [Sublime Text](http://www.sublimetext.com/3)はあなたの味方だ。タブをスペース2つに設定したり、[末尾のスペースをハイライトするパッケージ](https://github.com/SublimeText/TrailingSpaces)が存在する。  
- 他にもSublimeのパッケージの [Githubinator](https://github.com/ehamiter/GitHubinator), [SendText](https://github.com/wch/SendText), and [Package Control](https://packagecontrol.io/installation)とか超便利。  
- コードやGithubを頻繁にチェックしとこう  


*******************************
以上。

HAVING使うな、ってところがよくわからん（か、情報が古い）。
大体の規約は普段使っているのと同じだけど、個人的には

- 結合条件(ON)は別行に書く
- 後ろカンマではなく前カンマ（明示はされていないが、コード例は後ろカンマ）
- セミコロンは最終行に単独で置く

の方が好きだなー。
なお、似たようなルールのスタイルガイドとして、以下が理由付きで書いてるので参考にしたいところ。
[SQLスタイルガイド](https://qiita.com/taise/items/18c14d9b01a5dfd6d35e)