用数据库就像程序员的生活一样:充满了意外。就算是老司机开发者也可能犯错,比如不小心删了数据、插入了重复值,或者违反了完整性约束。关键不是只会躲坑,更要知道如果踩坑了怎么补救。来,咱们看看几个最典型的错误。
错误1:漏写 WHERE 条件
最经典的错误,初学者(其实老司机偶尔也会)经常犯的,就是在更新或删除数据的SQL里忘了加 WHERE。没有 WHERE 的SQL会把整张表的所有行都更新或删除。
-- 千万别这样写:
UPDATE students SET status = '已毕业';
-- 或者这样:
DELETE FROM students;
后果:想象一下,执行完这种SQL后,你发现 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,enrollments 里的 student_id 是外键,关联 students 的 id。如果你往 enrollments 插入一个 student_id,但 students 里根本没有这个id,就会报错。
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:并发访问问题(数据泄露)
想象一下,两个用户同时改同一条记录。如果没有合适的事务隔离,很容易冲突。
-- 用户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;
怎么避免:如果想保留回滚的可能,用带条件的 DELETE 代替 TRUNCATE。
BEGIN;
DELETE FROM students WHERE year_of_study = 1;
-- 如果后悔了:
ROLLBACK;
错误7:重要操作没用事务
如果一个复杂操作分好几步,中间某一步出错,数据可能就不一致了。
-- 第一步:加学生
INSERT INTO students (name, email) VALUES ('Otto Lin', 'otto.lin@email.com');
-- 第二步:给他选课
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