用資料庫就像工程師的人生一樣:充滿驚喜。就算是超有經驗的開發者也會犯錯,比如不小心把資料刪光、插入重複資料,或是破壞了資料完整性。重點不只是要避免這些錯誤,還要知道如果真的發生了該怎麼補救。來看看幾個最常見的錯誤吧。
錯誤一:忘記加 WHERE 條件
這是新手最經典的錯誤(老實說,有時候老鳥也會中招)——在更新或刪除資料時忘了加 WHERE。沒有 WHERE 的查詢會把 整個資料表的所有列 都更新或刪掉。
-- 千萬不要這樣寫:
UPDATE students SET status = '已畢業';
-- 或像這樣:
DELETE FROM students;
後果:想像一下,執行完這種查詢後,你發現 students 這個存著所有學生資料的表整個空了。最慘的是,如果你沒備份或沒用 transaction,資料就真的回不來(就算有 transaction 也很緊張)。
怎麼避免:每次寫 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 = '已退學';
錯誤二:違反唯一性限制 (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;
錯誤三:違反完整性限制 (FOREIGN KEY)
假設你有兩個表:students 跟 enrollments,enrollments 裡的 student_id 是外鍵,連到 students 的 id。如果你插入一筆 student_id 在 students 沒有的資料,就會報錯。
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
);
錯誤四:資料型別不正確
插入或更新資料時,PostgreSQL 會嚴格檢查型別。如果你把字串塞進數字欄位,就會報錯。
-- 型別不合會報錯:
INSERT INTO students (id, name) VALUES ('abc', 'Alex Go');
錯誤訊息:
ERROR: invalid input syntax for type integer
怎麼避免?插入的值要注意型別。如果資料來自使用者表單,記得在應用程式端先驗證。
錯誤五:平行存取問題(資料外洩)
想像兩個使用者同時要改同一筆資料。如果沒做好 transaction 隔離,很容易出現衝突。
-- 使用者 A:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 使用者 B:
BEGIN;
UPDATE accounts SET balance = balance - 50 WHERE id = 1;
怎麼避免?用 transaction 跟隔離等級,避免同時改資料。
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
錯誤六:用 TRUNCATE 造成資料遺失
TRUNCATE 會把整個表的資料瞬間清空,沒辦法復原,因為這個指令不支援 ROLLBACK(不會觸發 trigger,執行超快)。
-- 這樣資料就全沒了:
TRUNCATE TABLE students;
怎麼避免:如果想保留 rollback 的可能,用有條件的 DELETE 取代 TRUNCATE。
BEGIN;
DELETE FROM students WHERE year_of_study = 1;
-- 如果後悔了:
ROLLBACK;
錯誤七:重要操作沒用 transaction
如果一個複雜操作有好幾個步驟,中間出錯的話,資料可能會變得不一致。
-- 步驟一:新增學生
INSERT INTO students (name, email) VALUES ('Otto Lin', 'otto.lin@email.com');
-- 步驟二:幫他選課
INSERT INTO enrollments (student_id, course_id) VALUES (LASTVAL(), 101); -- 這裡出錯
怎麼避免?把這種操作包進 transaction:
BEGIN;
INSERT INTO students (name, email) VALUES ('伊萬 伊萬諾夫', 'ivan.ivanov@email.com');
INSERT INTO enrollments (student_id, course_id) VALUES (LASTVAL(), 101);
COMMIT;
只要哪一步出錯,你都可以把變更撤回:
ROLLBACK;
錯誤八:不小心處理 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