PostgreSQLでやってはいけないこと

PostgreSQL Wikiの「やってはいけないこと」ページの日本語訳です。


## データベースのエンコーディング

### SQL_ASCIIを使用しない

#### なぜ使用してはいけないのか？

`SQL_ASCII`は、すべてのエンコーディング変換関数の目的で「変換なし」を意味します。つまり、元のバイトは新しいエンコーディングでそのまま扱われ、意味に関係なく有効性チェックが行われます。極端な注意を払わない限り、`SQL_ASCII`データベースは通常、さまざまなエンコーディングの混在を保存し、元の文字を確実に復元する方法がなくなります。

#### いつ使用すべきか？

入力データがすでにラベル付けされていないエンコーディングの混在（例：IRCチャットログや非MIME準拠のメール）の場合、`SQL_ASCII`は最後の手段として有用かもしれませんが、まず`bytea`の使用を検討するか、UTF8を自動検出し、非UTF8データを特定のエンコーディング（例：WIN1252）と仮定することを検討してください。

## ツールの使用

### psqlの-Wまたは--passwordを使用しない

#### なぜ使用してはいけないのか？

`--password`または`-W`フラグを使用すると、サーバーに接続を試みる前にパスワードの入力を求められます。サーバーがパスワードを必要としない場合でもプロンプトが表示されるため、混乱を招く可能性があります。

サーバーがパスワードを必要とする場合、`psql`は自動的にパスワードを求めるため、これらのフラグは不要です。特に、`peer`認証を使用しているサーバーに`-W`で接続すると、パスワードが必要であると誤解する可能性があります。また、ログインユーザーにパスワードが設定されていない場合や、間違ったパスワードを入力してもログインできてしまい、他のクライアントやユーザーでの接続時に問題が発生する可能性があります。

#### いつ使用すべきか？

ほとんどの場合、使用すべきではありません。サーバーへの往復を節約できますが、それ以上の利点はほとんどありません。

### ルールを使用しない

ルールの代わりにトリガーを使用してください。

#### なぜ使用してはいけないのか？

ルールは非常に強力ですが、見た目とは異なる動作をします。条件付きロジックのように見えますが、実際にはクエリを書き換えたり、追加のクエリを加えたりします。

そのため、非自明なルールはすべて予期しない動作を引き起こす可能性があります。

#### いつ使用すべきか？

ルールの使用は避け、代わりにトリガーを使用してください。

### テーブルの継承を使用しない

#### なぜ使用してはいけないのか？

PostgreSQLのテーブル継承は、オブジェクト指向プログラミングの継承とは異なります。多くの場合、期待通りに動作しません。

#### いつ使用すべきか？

パーティショニングのためにテーブル継承を使用する場合は、PostgreSQLのパーティショニング機能を使用してください。

## SQL構文

### NOT INを使用しない

#### なぜ使用してはいけないのか？

`NOT IN`は、NULL値を含む場合に予期しない結果をもたらす可能性があります。

#### いつ使用すべきか？

`NOT EXISTS`または外部結合を使用してください。

### 大文字のテーブル名や列名を使用しない

#### なぜ使用してはいけないのか？

大文字の識別子は、ダブルクォーテーションで囲む必要があり、クエリの可読性が低下します。

#### いつ使用すべきか？

小文字の識別子を使用してください。

### BETWEENを使用しない（特にタイムスタンプで）

#### なぜ使用してはいけないのか？

`BETWEEN`は、境界値を含むため、タイムスタンプの範囲検索で予期しない結果をもたらす可能性があります。

#### いつ使用すべきか？

明示的な範囲指定を使用してください。

## 日付/時刻の保存

### timestamp（タイムゾーンなし）を使用しない

#### なぜ使用してはいけないのか？

タイムゾーン情報が失われ、異なるタイムゾーン間でのデータの一貫性が保てません。

#### いつ使用すべきか？

タイムゾーンを考慮しない場合にのみ使用してください。

### UTC時刻を保存するためにtimestamp（タイムゾーンなし）を使用しない

#### なぜ使用してはいけないのか？

タイムゾーン情報が失われ、UTC時刻としての意味が失われます。

#### いつ使用すべきか？

`timestamptz`（タイムゾーン付きタイムスタンプ）を使用してください。

### timetz（タイムゾーン付き時間）を使用しない

#### なぜ使用してはいけないのか？

`timetz`型は、1日の特定の時間にタイムゾーン情報を追加するための型ですが、通常の使用シナリオでは曖昧さを招きます。タイムゾーンのデータが必要な場合は、`timestamptz`を使用して特定の日時を保存する方が適切です。

#### いつ使用すべきか？

極めて限られたシナリオでのみ使用してください。例えば、特定の日付に結び付かない時間（例：タイムゾーン付きの勤務シフト開始時間）を保存する場合です。

---

## インデックスに関する注意

### 巨大なテーブルにインデックスを作成する場合に一気に作成しない

#### なぜ一気に作成してはいけないのか？

大きなテーブルにインデックスを一気に作成すると、データベースのパフォーマンスが大幅に低下し、アプリケーションが停止する可能性があります。

#### どうすればよいのか？

新しいインデックスを作成する場合、可能であればテーブルに対する書き込みを一時停止してください。または`CONCURRENTLY`オプションを使用すると、テーブルがロックされるのを防ぎます。

---

### インデックスの再利用に注意する

#### なぜ再利用に注意が必要なのか？

ある種のクエリでは、PostgreSQLが既存のインデックスを最適に利用しない場合があります。特に、関数や演算子を含むクエリでは、インデックススキャンを回避する可能性があります。

#### どうすればよいのか？

クエリを見直し、関数や演算子がインデックスを利用できる形に調整してください。または、インデックスを補完するためのインデックスを作成します。

---

## パフォーマンスの最適化

### COUNT(*)を多用しない

#### なぜ使用してはいけないのか？

大規模なテーブルで`COUNT(*)`を頻繁に実行すると、全ての行をスキャンするため、クエリの実行時間が増加します。

#### どうすればよいのか？

代わりに、トリガーを使用して統計情報を管理する、もしくは必要なケースでのみ`COUNT(*)`を使用してください。

---

### アプリケーションでの論理チェックを過度に依存しない

#### なぜ依存してはいけないのか？

アプリケーション内でのみデータ整合性を管理すると、データベースに保存されるデータの一貫性が保証されなくなります。複数のアプリケーションやツールが同じデータを操作する場合、この問題がさらに悪化します。

#### どうすればよいのか？

データベース内で制約（`CHECK`制約、`UNIQUE`制約、外部キーなど）を設定し、一貫性を担保してください。

---

### 読み取り専用の作業にトランザクションを使用しない

#### なぜトランザクションを使用してはいけないのか？

読み取り専用クエリでトランザクションを使用すると、ロックやリソース使用が不要に増加し、パフォーマンスが低下する可能性があります。

#### どうすればよいのか？

読み取り専用クエリは通常、トランザクションを使用せずに実行します。

---

## 結論

PostgreSQLの強力な機能を活用する際には、その挙動を十分に理解し、慎重に使用することが重要です。このリストの指針に従うことで、よくある問題を回避し、データベースの健全性とパフォーマンスを維持することができます。