データベースを触るのって、プログラマーの人生みたいなもんで、予想外のことがいっぱい起きるんだよね。どんなに経験あるエンジニアでも、うっかりデータを消しちゃったり、重複を入れようとしたり、整合性制約を壊しちゃったりすることがある。でも大事なのは、そういうミスを避けるだけじゃなくて、もしやっちゃったときにどう直すかも知っておくこと。じゃあ、よくあるミスをいくつか見ていこう!
ミスその1:WHERE条件の付け忘れ
これ、初心者が一番やりがちなミス(てか、正直ベテランでもたまにやる…)。WHEREをUPDATEやDELETEのクエリに付け忘れると、テーブルの全行が更新・削除されちゃうんだよ。
-- こうしちゃダメな例:
UPDATE students SET status = '卒業';
-- あるいはこう:
DELETE FROM students;
どうなるかっていうと、こんなクエリを実行した後で、studentsテーブル(学生の全データが入ってるやつ)が空っぽになってるのに気づくんだよね。しかも一番ヤバいのは、バックアップやトランザクションがなかったらデータは戻せない(あってもストレスだけど)。
どう防ぐ?UPDATEやDELETEのクエリには必ず条件を付けて、どの行を変えたい・消したいのかちゃんと指定しよう。
-- こうやるべき:
UPDATE students
SET status = '卒業'
WHERE year_of_study = 4;
DELETE FROM students
WHERE status = '退学';
もう一つのコツは、削除操作の前に必ずSELECTで条件が正しいか確認すること:
-- まず確認しよう:
SELECT * FROM students WHERE status = '退学';
-- それから削除:
DELETE FROM students WHERE status = '退学';
ミスその2:データの一意性(UNIQUE)違反
テーブルにUNIQUE制約がある場合、重複したデータを入れようとするとエラーになるよ。
-- emailの重複でエラー:
INSERT INTO students (name, email) VALUES ('Otto Lin', 'otto.lin@email.com');
INSERT INTO students (name, email) VALUES ('Peter Pen', 'otto.lin@email.com');
エラー内容:
ERROR: duplicate key value violates unique constraint "students_email_key"
どう防ぐ?データを入れる前に、同じ値の行がすでにないかチェックしよう。
-- こんな感じで:
SELECT * FROM students WHERE email = 'otto.lin@email.com';
-- もしくはUPSERTを使う:
INSERT INTO students (name, email)
VALUES ('Peter Pen', 'otto.lin@email.com')
ON CONFLICT (email) DO NOTHING;
ミスその3:整合性制約(FOREIGN KEY)違反
例えば、studentsとenrollmentsという2つのテーブルがあって、enrollmentsのstudent_idがstudentsのidと外部キーでつながってるとする。もしstudentsに存在しないstudent_idでenrollmentsにデータを入れようとすると、エラーになるよ。
INSERT INTO enrollments (student_id, course_id)
VALUES (999, 101); -- エラー、student_id 999は存在しない
どう防ぐ?
- 関連テーブルにデータを入れる前に、親テーブルにそのレコードがあるか必ずチェックしよう:
SELECT * FROM students WHERE id = 999;
ON DELETE CASCADE制約を使えば、親テーブルのレコードを消したときに関連テーブルのデータも自動で消える(けど使い方は慎重に)。
CREATE TABLE enrollments (
id SERIAL PRIMARY KEY,
student_id INT REFERENCES students(id) ON DELETE CASCADE,
course_id INT
);
ミスその4:データ型のミスマッチ
データを入れたり更新したりするとき、PostgreSQLは型の一致をめっちゃ厳しくチェックする。数値フィールドに文字列を入れようとするとエラーになるよ。
-- 型が合わなくてエラー:
INSERT INTO students (id, name) VALUES ('abc', 'Alex Go');
エラー内容:
ERROR: invalid input syntax for type integer
どう防ぐ?入れる値の型をちゃんと確認しよう。ユーザーのフォームからデータが来る場合は、アプリ側で必ずバリデーションしよう。
ミスその5:同時アクセスによるデータ競合
例えば、2人のユーザーが同時に同じレコードを更新しようとしたら、トランザクションの分離レベルがちゃんとしてないと競合が起きやすい。
-- ユーザーA:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- ユーザーB:
BEGIN;
UPDATE accounts SET balance = balance - 50 WHERE id = 1;
どう防ぐ?トランザクションと分離レベルを使って、同時にデータが変わらないようにしよう。
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
ミスその6:TRUNCATEによるデータ消失
TRUNCATEはテーブルの全行を一瞬で消しちゃう。しかもROLLBACKできないし、トリガーも発動しないから、復元は無理。
-- 全部消えて戻せない:
TRUNCATE TABLE students;
どう防ぐ?TRUNCATEじゃなくて、条件付きDELETEを使えば、やっぱやめたってときにROLLBACKできるよ。
BEGIN;
DELETE FROM students WHERE year_of_study = 1;
-- やっぱやめたいとき:
ROLLBACK;
ミスその7:重要な操作でトランザクションを使わない
複数のステップからなる複雑な操作をしてるとき、途中でエラーが起きるとデータが中途半端な状態で残っちゃうことがある。
-- ステップ1: 学生を追加
INSERT INTO students (name, email) VALUES ('Otto Lin', 'otto.lin@email.com');
-- ステップ2: コースに登録
INSERT INTO enrollments (student_id, course_id) VALUES (LASTVAL(), 101); -- エラー
どう防ぐ?こういう操作はトランザクションでまとめよう:
BEGIN;
INSERT INTO students (name, email) VALUES ('イワン イワノフ', 'ivan.ivanov@email.com');
INSERT INTO enrollments (student_id, course_id) VALUES (LASTVAL(), 101);
COMMIT;
どこかでエラーが出たら、変更を全部取り消せる:
ROLLBACK;
ミスその8:NULLの扱いミス
NULLはよくトラップになる。0でも空文字でもなくて、比較するときに思わぬ結果になることが多い。
-- これは動かない:
SELECT * FROM students WHERE email = NULL;
どう防ぐ?IS NULLやIS NOT NULLを使おう:
SELECT * FROM students WHERE email IS NULL;
こういうミスは避けられないけど、どうやって見抜いて防ぐか知っていれば、データ操作も安全で効率的にできるよ。PostgreSQLは厳しいけどフェアなデータの番人で、何かおかしいとすぐエラーを返してくれる。ミスは敵じゃなくて、先生だって思っておこう!
GO TO FULL VERSION