咱们来看看往表里插新数据时常见的那些坑。
错误1:试图往必填字段插入NULL
PostgreSQL会很认真地保证你的数据库规则都被遵守。来看几个可能导致报错的约束例子:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL, -- 名字不能为空
age INT
);
-- 错误:name字段必须填写
INSERT INTO students (name, age) VALUES (NULL, 20);
结果:报错 null value in column "name" of relation "students" violates not-null constraint`。
你得注意你加了什么数据。也许这个字段以前能是NULL,现在已经是必填了。
错误2:唯一列里有重复数据。
CREATE TABLE courses (
course_id SERIAL PRIMARY KEY,
course_name TEXT UNIQUE -- 课程名必须唯一
);
-- 第一次插入没问题
INSERT INTO courses (course_name) VALUES ('SQL Basics');
-- 第二次插入会报错
INSERT INTO courses (course_name) VALUES ('SQL Basics');
结果:报错 duplicate key value violates unique constraint`。
通常这不是你的锅,是用户手滑重复点了某个操作。这种情况其实不用管。
错误3:外键约束被破坏。
CREATE TABLE enrollments (
enrollment_id SERIAL PRIMARY KEY,
student_id INT REFERENCES students(id), -- 必须有这个ID的学生
course_id INT REFERENCES courses(course_id)
);
-- 错误:ID=99的学生不存在
INSERT INTO enrollments (student_id, course_id) VALUES (99, 1);
结果:报错 insert or update on table "enrollments" violates foreign key constraint`。
其实报错是好事。数据完整性被破坏才是最糟糕的。大概率是你代码有bug,或者数据过时了。总之数据库没让你破坏它的完整性,这就很棒。
PostgreSQL里的错误处理
没错,错误总会发生。但关键不是发现它,而是会怎么处理它。
事务:数据保护神器
操作数据时我们经常用事务来保证数据一致性。如果出错了,可以回滚所有更改。
例子:往两张表里加数据。
BEGIN; -- 开始事务
-- 往students表插数据
INSERT INTO students (name, age) VALUES ('Otto Lin', 21);
-- 往enrollments表插记录
-- 如果ID=10的课程不存在,这里会报错
INSERT INTO enrollments (student_id, course_id) VALUES (1, 10);
-- 如果一切顺利
COMMIT;
-- 如果出错了,"撤销"所有更改
ROLLBACK;
如果course_id = 10的课程不存在,students表里的插入也会被撤销。
事务里的错误处理
在PostgreSQL里你可以预判错误并直接在SQL里用EXCEPTION块处理。
例子:加学生并让他选课。如果出错,把错误写进日志。
DO $$
BEGIN
-- 试着插入数据
INSERT INTO students (name, age) VALUES ('Anna Song', 22);
INSERT INTO enrollments (student_id, course_id) VALUES (2, 999); -- 报错
-- 如果都成功
RAISE NOTICE '记录添加成功!';
EXCEPTION
WHEN foreign_key_violation THEN
-- 处理外键冲突
RAISE WARNING '指定的course_id课程不存在。';
END $$;
用ON CONFLICT检查唯一性
你可以提前用ON CONFLICT语法防止UNIQUE约束冲突。这样可以指定遇到冲突时怎么处理。
例子:如果插入重复课程就跳过。
INSERT INTO courses (course_name)
VALUES ('SQL Basics')
ON CONFLICT (course_name) DO NOTHING; -- 跳过重复数据
或者直接更新已有行:
INSERT INTO courses (course_name)
VALUES ('SQL Basics')
ON CONFLICT (course_name) DO UPDATE
SET course_name = EXCLUDED.course_name || ' (Updated)';
关于ON CONFLICT操作符的更多内容我会在下一级讲,等我们聊批量数据导入的时候 :P
常见数据操作错误及预防
你已经看到,主要的错误来源有:
- 违反约束(
NOT NULL、UNIQUE、FOREIGN KEY)。 - 更新或删除数据时没加条件(
WHERE)。 - 事务执行顺序出错。
想保护自己:
- 大操作用事务和
ROLLBACK兜底。 - 插入前一定要检查数据。
- 把错误都写日志,方便分析。
- 用
ON CONFLICT避免重复记录。
现在你已经有了对抗错误的武器!记住:好程序员不是不犯错,而是会修错。
GO TO FULL VERSION