まずは蝋の翼から。

学んだことを書きながら確認・整理するためのメモブログ。こういうことなのかな?といったことをふわっと書いたりしていますが、理解が浅いゆえに的はずれなことも多々あると思うのでツッコミ歓迎

Kickstarter SQL Style Guide和訳

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

Kickstarter SQL Style Guide · GitHub

目的

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

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

注意 :このスタイルガイドは AWS Redshift/Postgres 8.0.2向けとなっています。多くのことは標準SQLにも当てはまりますが、その点ご注意ください。

原則

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

ルール

全体的なもの

  • インデントを用いる場合、タブじゃなくてスペース2つを使おう
  • 末尾に空白を入れるな
  • SQL予約語は必ず大文字にしよう(例: SELECT , AS ...)
  • カラム名はアンダースコアで分けよう(※キャメルケースやパスカルケースではなく、スネークケースを用いよう)。
      GOOD: SELECT COUNT(*) AS backers_count
      BAD: SELECT COUNT(*) AS backersCounts
  • コメントは必ずクエリの最上部、あるいは SELECT に近い部分に記載しよう
  • わかりづらい部分についてのみコメントをするようにしよう(例:何故特定のIDがハードコードされているか)
  • 一文字のカラム名は避け、わかりやすいカラム名にしよう
      GOOD: SELECT ksr.backings AS backings_with_creators
      BAD: SELECT ksr.backings AS b
  • できるだけCommon Table Expression(CTEs)を使おう(※サブクエリではなくWITH句を使おう)
  • RedshiftはHAVING句をサポートしていないのでCTE経由で抽出をしよう。言っている意味がわからん場合はDataチームの優しい誰かに理由を聞いてくれ。(※Redshiftドキュメントにその旨の記載を見当たらないけど執筆当時はサポートしてなかっただけか?)

SELECT

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

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

SELECTを記載する行はSELECT以外書かない。

SELECT  
  name,  
  ...  

集約関数はいつもエイリアス名を付けよう。
また、カラム名は「集約関数名_集約対象カラム名」にする。

SELECT  
  name,  
  SUM(amount) AS sum_amount  
FROM ...  

複数テーブルを用いている場合はいつも、テーブルのエイリアス名をカラム名に使おう。

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:

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

BAD:

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

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

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

FROM

FROMには1テーブルのみ記載するようにし、JOINをしたい場合は明示するようにしよう。
Good;

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:

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:

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:

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つ深いネストに書こう。

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:

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:

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修飾子はじまりで書こう

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

CASE

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

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

Common Table Expressions (CTEs)

AWSのドキュメントでは以下のように記載されてます。

WITH 句のサブクエリは、単一のクエリ実行中に、使用可能なテーブルを効率的に定義します。SELECT ステートメントの本文内でサブクエリを使用することで、すべてのケースで同じ結果を実現できますが、WITH 句のサブクエリの方が、読み書きが簡単になることがあります

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

WITH backings_per_category AS (  
  SELECT  
    category_id,  
    deadline,  
    ...  
)  

複数のCTEでは以下のように、閉じる部分)と、新規CTEの開始(を同じ行に記載しよう。

WITH backings_per_category AS (  
  SELECT  
    ...  
), backers AS (  
  SELECT  
    ...  
), backers_and_creators AS (  
  ...  
)  
SELECT * FROM backers;  

また、可能であればJOINが含まれるCTEは、メイン部分ではなくWITH句内に記載するようにしよう(※効率の観点?)
GOOD:

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:

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

以上。

HAVING使うな、ってところが多分解釈間違ってる(か、情報が古い)。 大体の規約は普段使っているのと同じだけど、個人的には
- 結合条件(ON)は別行に書く
- 後ろカンマではなく前カンマ(明示はされていないが、コード例は後ろカンマ)
- セミコロンは最終行に単独で置く
の方が好きだなー。 なお、似たようなルールのスタイルガイドとして、以下が理由付きで書いている。 SQLスタイルガイド - Qiita