データ分析SQLのコーディング規約

# データ分析SQLのコーディング規約

## 文字に関する規約

- 予約語/関数は、大文字にする。
  - SQL自体は大文字小文字を区別しないが、予約語や関数を大文字にすることで可読性が向上する。
- JOINのASに単文字を使わない。（変数名も）
  - 単文字だとテーブルの意味が分かりにくくなるため、明確な別名をつける。
- スネークケースで書く。
  - 他のデータベースとの互換性のため、スネークケースを使用する。
- 接頭辞(prefix) / 接尾辞(suffix)をつける。
  - 集計関数や時間単位などを接頭辞・接尾辞で明示する。
- `id`というカラム名は使わない。
  - 結合ミスを防ぐため、他のデータベースから読み込む際にカラム名を変更する。

## 体裁に関する規約

- 1行に1カラムにする。
  - 複数カラムを1行に書くと分かりにくくなるため、1行1カラムを徹底する。
- インデントは2文字（スペース）にする。
  - 分析クエリは横長になりがちなので、2スペースのインデントを使う。
- AND,ORは改行して先頭に配置する。
  - 条件式が複数になる場合、改行して視認性を高める。
- SELECT単位でインデントを揃える。
  - JOINが多い分析SQLでは、SELECT単位でインデントを揃えて階層を明確にする。

## 機能的規約

- `INNER JOIN`を使わない。
  - データ除外の危険性があるため、`LEFT JOIN`とWHERE句を使って丁寧に処理する。
- 一定の量が増えたらサブクエリを`WITH`句にする。
  - サブクエリが深くなりすぎないよう、3階層程度で`WITH`句を使う。
- 集計時刻のカラムを作る。
  - データマート作成時に`CURRENT_TIMESTAMP()`で集計時刻を記録する。
- 集計期間には、ファンクション変数を使う。
  - 日時変数は関数化し、変更に強いクエリにする。

## 例外

- `UNION`する場合は、上記の規約に従わなくてもよい。
  - 縦型から横型に変換する際など、可読性のために規約を変更してもよい。

# まとめ

以上が、データ分析SQLのコーディング規約をまとめたものです。分析SQLは可読性とエラー防止が特に重要なので、これらの規約を意識してコーディングすることが大切です。ただし、状況に応じて柔軟に対応することも必要です。

----

# データ分析SQLのコーディング規約

## 文字に関する規約

- 予約語/関数は、大文字にする。
  - SQL自体は大文字小文字を区別しないが、予約語や関数を大文字にすることで可読性が向上する。

Bad:❌
```sql
select 
  max(created_at) 
from
  users
where
 id = 1;
```

Good:✅
```sql
SELECT
  MAX(created_at)
FROM
  users
WHERE
  id = 1;
```

- JOINのASに単文字を使わない。（変数名も）
  - 単文字だとテーブルの意味が分かりにくくなるため、明確な別名をつける。

Bad:❌
```sql
SELECT 
  u.name,
  p.product_name 
FROM
 users AS u
JOIN 
 purchases AS p ON u.id = p.user_id;
```

Good:✅
```sql
SELECT
  users.name,
  purchases.product_name
FROM
  users
JOIN 
  purchases ON users.id = purchases.user_id;
```

- スネークケースで書く。
  - 他のデータベースとの互換性のため、スネークケースを使用する。

Bad:❌
```sql
SELECT 
  userId,
  createdAt 
FROM
  users;
```

Good:✅
```sql
SELECT
  user_id,
  created_at
FROM
  users;
```

- 接頭辞(prefix) / 接尾辞(suffix)をつける。
  - 集計関数や時間単位などを接頭辞・接尾辞で明示する。

Bad:❌
```sql
SELECT 
  COUNT(*) AS count,
  SUM(price) AS sum
FROM
  orders;
```

Good:✅
```sql
SELECT
  COUNT(*) AS total_orders,
  SUM(price) AS total_revenue
FROM
  orders;
```

- `id`というカラム名は使わない。
  - 結合ミスを防ぐため、他のデータベースから読み込む際にカラム名を変更する。

Bad:❌
```sql
SELECT
  users.id,
  orders.i
FROM
  users
JOIN 
  orders ON users.id = orders.user_id;
```

Good:✅
```sql
SELECT 
  users.user_id,
  orders.order_id 
FROM
  users
JOIN
  orders ON users.user_id = orders.user_id;
```

## 体裁に関する規約

- 1行に1カラムにする。
  - 複数カラムを1行に書くと分かりにくくなるため、1行1カラムを徹底する。

Bad:❌
```sql
SELECT user_id, created_at, updated_at FROM users;
```

Good:✅
```sql
SELECT
  user_id,
  created_at,
  updated_at
FROM
  users;
```

- インデントは2文字（スペース）にする。
  - 分析クエリは横長になりがちなので、2スペースのインデントを使う。

Bad:❌
```sql
SELECT
    user_id,
    COUNT(*) AS total_orders
FROM
    orders
GROUP BY
    user_id;
```

Good:✅
```sql
SELECT
  user_id,
  COUNT(*) AS total_orders
FROM
  orders
GROUP BY
  user_id;
```

- AND,ORは改行して先頭に配置する。
  - 条件式が複数になる場合、改行して視認性を高める。

Bad:❌
```sql
SELECT * 
FROM
  users 
WHERE
  created_at >= '2022-01-01' AND created_at < '2023-01-01' AND status = 'active';
```

Good:✅
```sql
SELECT *
FROM 
  users
WHERE
  created_at >= '2022-01-01'
  AND created_at < '2023-01-01'
  AND status = 'active';
```

- SELECT単位でインデントを揃える。
  - JOINが多い分析SQLでは、SELECT単位でインデントを揃えて階層を明確にする。

Bad:❌
```sql
SELECT users.name, COUNT(orders.id) AS total_orders
FROM users
LEFT JOIN orders ON users.id = orders.user_id
WHERE users.created_at >= '2022-01-01'
GROUP BY users.id;
```

Good:✅
```sql
SELECT
  users.name,
  COUNT(orders.id) AS total_orders
FROM
  users
LEFT JOIN
  orders
    ON users.id = orders.user_id
WHERE
  users.created_at >= '2022-01-01'
GROUP BY
  users.id;
```

## 機能的規約

- `INNER JOIN`を使わない。
  - データ除外の危険性があるため、`LEFT JOIN`とWHERE句を使って丁寧に処理する。

Bad:❌
```sql
SELECT
  users.name,
  COUNT(orders.id) AS total_orders
FROM
  users
INNER JOIN
  orders
    ON users.id = orders.user_id
GROUP BY
  users.id;
```

Good:✅
```sql
SELECT
  users.name,
  COUNT(orders.id) AS total_orders
FROM
  users
LEFT JOIN
  orders
    ON users.id = orders.user_id
WHERE
  orders.id IS NOT NULL
GROUP BY
  users.id;
```

- 一定の量が増えたらサブクエリを`WITH`句にする。
  - サブクエリが深くなりすぎないよう、3階層程度で`WITH`句を使う。

Bad:❌
```sql
SELECT
  user_id,
  total_orders
FROM
  (
    SELECT
      user_id,
      COUNT(*) AS total_orders
    FROM
      (
        SELECT
          user_id,
          order_id
        FROM
          orders
        WHERE
          created_at >= '2022-01-01'
      )
    GROUP BY
      user_id
  );
```

Good:✅
```sql
WITH orders_filtered AS (
  SELECT
    user_id,
    order_id
  FROM
    orders
  WHERE
    created_at >= '2022-01-01'
)
SELECT
  user_id,
  COUNT(*) AS total_orders
FROM
  orders_filtered
GROUP BY
  user_id;
```

- 集計時刻のカラムを作る。
  - データマート作成時に`CURRENT_TIMESTAMP()`で集計時刻を記録する。

Bad:❌
```sql
SELECT
  user_id,
  COUNT(*) AS total_orders
FROM
  orders
GROUP BY
  user_id;
```

Good:✅
```sql
SELECT
  user_id,
  COUNT(*) AS total_orders,
  CURRENT_TIMESTAMP() AS aggregated_at
FROM
  orders
GROUP BY
  user_id;
```

- 集計期間には、ファンクション変数を使う。
  - 日時変数は関数化し、変更に強いクエリにする。

Bad:❌
```sql
SELECT
  user_id,
  COUNT(*) AS total_orders
FROM
  orders
WHERE
  created_at >= '2022-01-01'
  AND created_at < '2023-01-01'
GROUP BY
  user_id;
```

Good:✅
```sql
CREATE TEMPORARY FUNCTION start_date() AS (DATE '2022-01-01');
CREATE TEMPORARY FUNCTION end_date() AS (DATE '2023-01-01');

SELECT
  user_id,
  COUNT(*) AS total_orders
FROM
  orders
WHERE
  created_at >= start_date()
  AND created_at < end_date()
GROUP BY
  user_id;
```

## 例外

- `UNION`する場合は、上記の規約に従わなくてもよい。
  - 縦型から横型に変換する際など、可読性のために規約を変更してもよい。

Example:
```sql
SELECT
  user_id,
  'order_count' AS metric,
  COUNT(*) AS value
FROM
  orders
GROUP BY
  user_id

UNION ALL

SELECT
  user_id,
  'total_revenue' AS metric,
  SUM(total_amount) AS value
FROM
  orders
GROUP BY
  user_id;
```

以上が、データ分析SQLのコーディング規約をまとめたものです。各項目についてGood/Badの例を追加し、規約の意図をより明確に示しました。これらの例を参考に、可読性と保守性の高いSQLを書くことを心がけましょう。