SQLのコーディング規約を考えていたところ、Kickstarter のSQLスタイルガイドがあったのでテキトーに意訳してみる。
※は私なりの補足や疑問を記載
目的
再現性・透明性を維持していくことは、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
,とTHEN
とELSE
を同じ位置に揃え、CASE
とEND
の内側に入れよう。
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
- Sublime Textはあなたの味方だ。タブをスペース2つに設定したり、末尾のスペースをハイライトするパッケージが存在する。
- 他にもSublimeのパッケージの Githubinator, SendText, and Package Controlとか超便利。
- コードやGithubを頻繁にチェックしとこう
以上。
HAVING使うな、ってところがよくわからん(か、情報が古い)。
大体の規約は普段使っているのと同じだけど、個人的には
- 結合条件(ON)は別行に書く
- 後ろカンマではなく前カンマ(明示はされていないが、コード例は後ろカンマ)
- セミコロンは最終行に単独で置く
の方が好きだなー。
なお、似たようなルールのスタイルガイドとして、以下が理由付きで書いてるので参考にしたいところ。
SQLスタイルガイド