CodeGym /コース /SQL SELF /外部キーを使うときによくあるミス

外部キーを使うときによくあるミス

SQL SELF
レベル 20, レッスン 4
使用可能

みんな人間だから、ミスはつきものだよね。特に外部キーの細かいところでやらかしがち。今回のレクチャーでは、よくあるミスや落とし穴を一緒に回避しよう。いいデータベースは頑丈な橋みたいなもの。どこかでミスると全部崩れるかも。じゃあ、「データの橋」をちゃんと保つ方法を見ていこう!

ミス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は長期プロジェクトの頼れる基盤になる!

2
タスク
SQL SELF, レベル 20, レッスン 4
ロック未解除
カスケード更新
カスケード更新
2
タスク
SQL SELF, レベル 20, レッスン 4
ロック未解除
ON DELETE CASCADEの利用
ON DELETE CASCADEの利用
1
アンケート/クイズ
データ整合性のチェック、レベル 20、レッスン 4
使用不可
データ整合性のチェック
データ整合性のチェック
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION