みんな人間だから、ミスはつきものだよね。特に外部キーの細かいところでやらかしがち。今回のレクチャーでは、よくあるミスや落とし穴を一緒に回避しよう。いいデータベースは頑丈な橋みたいなもの。どこかでミスると全部崩れるかも。じゃあ、「データの橋」をちゃんと保つ方法を見ていこう!
ミス1: 外部キーにインデックスがない
外部キーを追加するとき、「このテーブルとあのテーブルをつなげてね」ってDBに伝えてる。でも、その外部キーにインデックスを明示的に作らないと、関連テーブルを使った複雑なクエリのときにパフォーマンスがめっちゃ落ちることがある。
問題の例:
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id)
);
見た目は完璧:テーブルも外部キーもある。でも、こんなクエリを実行すると:
SELECT *
FROM orders
JOIN customers ON orders.customer_id = customers.customer_id;
データ量が多いと、このクエリはめっちゃ遅くなる。なぜならPostgreSQLが最適化用のインデックスを見つけられないから。
どうやって避けるか:
外部キーが参照してるカラムには必ずインデックスを作ろう。PostgreSQLが自動でやってくれることもあるけど、自分でやったほうが安心。
CREATE INDEX idx_customer_id ON orders(customer_id);
ミス2: テーブル作成の順番ミス
テーブルを作るとき、参照先のテーブルがまだないのに外部キーを追加しようとすると、PostgreSQLが怒ってエラーを投げてくる。なぜなら、参照先のテーブルが見つからないから。
問題の例:
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id)
);
-- あれ、customersテーブルはどこ?..
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
結果:PostgreSQLはエラーを出す。だってcustomersテーブルがまだ存在しないから。
どうやって避けるか:
まず参照先のテーブルを作ってから、外部キーを追加しよう。順番が大事。正しいやり方はこれ:
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id)
);
ミス3: カスケード操作の構文ミス
外部キーにはON DELETE CASCADEやON UPDATE RESTRICTみたいなオプションをよく付ける。でも、うっかり書き間違えると、DBが予想外の動きをする。例えば、あるテーブルのデータを消しても、関連テーブルに反映されないとか。
問題の例:
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id) ON DELETE CASCADEE
);
よく見ると、CASCADEEってスペルミスしてる。PostgreSQLはこのエラーを許してくれない。
どうやって避けるか:
正しく書くのが半分勝ち! 迷ったらいつでもPostgreSQL公式ドキュメントをチェックしよう。
ミス4: データ整合性の破壊
データ整合性はDBの聖域で、外部キーはそれを守るためのもの。でも、外部キーを付け忘れると、全部ぐちゃぐちゃになることも。
問題の例:
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT
);
-- データを挿入
INSERT INTO orders (customer_id) VALUES (999);
ここでは、存在しない顧客の注文を追加してる。これでデータ整合性が壊れて、「ぶら下がり」注文ができちゃう。
どうやって避けるか:
外部キーを必ず使って、存在しないレコードへの参照を防ごう。正しい例はこれ:
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id)
);
これで「ぶら下がり」レコードを挿入しようとするとエラーになる。
ミス5: 外部キーエラーの握りつぶし
たまに開発者がINSERT ... ON CONFLICTを使って、無理やり不整合なデータを入れようとすることがある。一見便利だけど、外部キーと組み合わせると予想外のことが起きる。
問題の例:
INSERT INTO orders (order_id, customer_id)
VALUES (1, 999)
ON CONFLICT DO NOTHING;
結果:データは挿入されないけど、DBは理由を教えてくれない。これだと状況を把握できなくなる。
どうやって避けるか:
ON CONFLICTを使うなら、事前にデータをちゃんとチェックしよう。例えば:
INSERT INTO orders (order_id, customer_id)
SELECT 1, 999
WHERE EXISTS (
SELECT 1 FROM customers WHERE customer_id = 999
);
ミス6: ON DELETEなしで依存レコードを削除
外部キーが参照してるテーブルからレコードを消すとき、ON DELETE CASCADEを付けてないと、依存レコードがDBに残って、リレーションの意味がなくなる。
問題の例:
DELETE FROM customers WHERE customer_id = 1;
-- ordersのcustomer_id = 1のレコードはまだ残ってる。
どうやって避けるか:
ON DELETE CASCADEを付けて、関連レコードも自動で消えるようにしよう:
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id) ON DELETE CASCADE
);
これで顧客を消したら、対応する注文も一緒に消える。
ミス7: MANY-TO-MANYリレーションの落とし穴
MANY-TO-MANYリレーションを作るとき、複合主キーやインデックスを忘れがち。
問題の例:
CREATE TABLE enrollments (
student_id INT REFERENCES students(student_id),
course_id INT REFERENCES courses(course_id)
);
-- あっ!PRIMARY KEYを忘れた。
どうやって避けるか:
複合主キーかユニークインデックスを付けよう:
CREATE TABLE enrollments (
student_id INT REFERENCES students(student_id),
course_id INT REFERENCES courses(course_id),
PRIMARY KEY (student_id, course_id)
);
ミス8: 循環参照
循環参照は、2つのテーブルがお互いを外部キーで参照し合うときに発生する。これでループができて、データ挿入時に問題が起きる。
問題の例:
CREATE TABLE table_a (
id SERIAL PRIMARY KEY,
table_b_id INT REFERENCES table_b(id)
);
CREATE TABLE table_b (
id SERIAL PRIMARY KEY,
table_a_id INT REFERENCES table_a(id)
);
どうやって避けるか:
DEFERRABLE INITIALLY DEFERREDを使って、PostgreSQLがトランザクション終了後に整合性チェックできるようにしよう:
CREATE TABLE table_a (
id SERIAL PRIMARY KEY,
table_b_id INT REFERENCES table_b(id) DEFERRABLE INITIALLY DEFERRED
);
外部キーのミスは開発を遅くするだけじゃなく、データの大問題にもつながる。このリストをチートシートとして使って、よくある「地雷」を避けよう。覚えておいて:外部キーは味方だよ。正しく使えば、DBは長期プロジェクトの頼れる基盤になる!
GO TO FULL VERSION