【数据库】【Oracle】事务与约束详解

Oracle 事务与约束详解

事务和约束是 Oracle 数据库数据完整性和一致性 的两大基石。事务确保操作的原子性 ,约束确保数据的业务规则


一、事务(Transaction)基础

1.1 ACID 特性

特性 说明 Oracle 实现
原子性 (Atomicity) 要么全部成功,要么全部回滚 Undo 表空间、回滚段
一致性 (Consistency) 事务前后数据状态合法 约束检查、触发器
隔离性 (Isolation) 并发事务互不干扰 锁机制、MVCC
持久性 (Durability) 提交后永久保存 Redo 日志、数据文件

1.2 事务生命周期

sql 复制代码
-- 1. 开始事务(Oracle 自动开始)
UPDATE employees SET salary = 8000 WHERE employee_id = 101;

-- 2. 执行 DML 操作
INSERT INTO departments VALUES (280, 'Cloud Engineering', 1700);

-- 3. 保存点(可选)
SAVEPOINT sp1;

-- 4. 继续操作
DELETE FROM employees WHERE employee_id = 999;

-- 5. 回滚到保存点
ROLLBACK TO sp1;  -- 仅撤销 DELETE

-- 6. 提交事务
COMMIT;  -- 所有更改永久生效

-- 或回滚整个事务
ROLLBACK;  -- 撤销所有更改

二、Oracle 事务控制语句

2.1 显式控制

sql 复制代码
-- COMMIT:提交事务
COMMIT [WORK] [COMMENT '注释'];  -- COMMENT 用于分布式事务

-- ROLLBACK:回滚事务
ROLLBACK [WORK];
ROLLBACK [WORK] TO [SAVEPOINT] 保存点名称;  -- 部分回滚

-- SAVEPOINT:设置保存点
SAVEPOINT 保存点名称;

-- SET TRANSACTION:设置事务属性
SET TRANSACTION READ ONLY;  -- 只读事务
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;  -- 设置隔离级别
SET TRANSACTION NAME '订单处理';  -- 命名事务

2.2 隐式提交

以下操作会自动提交当前事务:

  • DDL 语句CREATE, ALTER, DROP, TRUNCATE
  • DCL 语句GRANT, REVOKE
  • 退出 SQL*Plus(正常退出)

陷阱示例

sql 复制代码
UPDATE employees SET salary = 9000 WHERE employee_id = 101;
CREATE TABLE temp_table (id NUMBER);  -- 自动提交 UPDATE!
ROLLBACK;  -- 已无法回滚 salary 的修改

2.3 自治事务

在触发器或存储过程中独立提交,不影响主事务。

sql 复制代码
CREATE OR REPLACE PROCEDURE log_error(err_msg VARCHAR2) IS
    PRAGMA AUTONOMOUS_TRANSACTION;  -- 声明自治事务
BEGIN
    INSERT INTO error_log (message, log_date) 
    VALUES (err_msg, SYSDATE);
    COMMIT;  -- 独立提交
END;
/

三、约束(Constraint)类型

3.1 约束分类总览

sql 复制代码
CREATE TABLE employees (
    employee_id     NUMBER(6) CONSTRAINT emp_pk PRIMARY KEY,  -- 主键约束
    email           VARCHAR2(25) CONSTRAINT emp_email_uk UNIQUE,  -- 唯一约束
    first_name      VARCHAR2(20) NOT NULL,  -- 非空约束
    last_name       VARCHAR2(25) NOT NULL,
    salary          NUMBER(8,2) CONSTRAINT emp_salary_ck CHECK (salary > 0),  -- 检查约束
    hire_date       DATE DEFAULT SYSDATE,
    department_id   NUMBER(4) CONSTRAINT emp_dept_fk 
                    REFERENCES departments(department_id)  -- 外键约束
);

3.2 主键约束(PRIMARY KEY)

作用:唯一标识一行,非空且唯一

sql 复制代码
-- 建表时创建
CREATE TABLE products (
    product_id NUMBER PRIMARY KEY,
    name VARCHAR2(100)
);

-- 表级约束(推荐,可命名)
CREATE TABLE products (
    product_id NUMBER,
    name VARCHAR2(100),
    CONSTRAINT prod_pk PRIMARY KEY (product_id)
);

-- 组合主键
CREATE TABLE order_items (
    order_id NUMBER,
    item_id NUMBER,
    quantity NUMBER,
    CONSTRAINT pk_order_items PRIMARY KEY (order_id, item_id)
);

-- 修改表添加主键
ALTER TABLE products ADD CONSTRAINT prod_pk PRIMARY KEY (product_id);

-- 禁用主键(数据仓库批量加载时)
ALTER TABLE products DISABLE CONSTRAINT prod_pk;
ALTER TABLE products ENABLE CONSTRAINT prod_pk;

3.3 外键约束(FOREIGN KEY)

作用:维护表间引用完整性

sql 复制代码
-- 基础外键
CREATE TABLE employees (
    employee_id NUMBER PRIMARY KEY,
    department_id NUMBER CONSTRAINT emp_dept_fk 
        REFERENCES departments(department_id)
);

-- 级联删除
ALTER TABLE employees 
ADD CONSTRAINT emp_dept_fk FOREIGN KEY (department_id)
REFERENCES departments(department_id)
ON DELETE CASCADE;  -- 删除部门时,员工自动删除

-- 级联置空
ALTER TABLE employees 
ADD CONSTRAINT emp_dept_fk FOREIGN KEY (department_id)
REFERENCES departments(department_id)
ON DELETE SET NULL;  -- 删除部门时,员工 dept_id 设为 NULL

-- 外键限制
- 外键列必须是主键或唯一键
- 外键列类型必须与引用列一致
- 外键可以引用同一表(自引用)

外键检查时机

sql 复制代码
-- IMMEDIATE(默认):每行 DML 后立即检查
-- DEFERRED:事务提交时检查(用于循环依赖)
ALTER TABLE employees 
ADD CONSTRAINT emp_dept_fk FOREIGN KEY (department_id)
REFERENCES departments(department_id)
DEFERRABLE INITIALLY DEFERRED;  -- 可延迟约束

3.4 唯一约束(UNIQUE)

作用:确保列值唯一,但允许多个 NULL

sql 复制代码
-- 列级唯一约束
CREATE TABLE users (
    user_id NUMBER PRIMARY KEY,
    email VARCHAR2(100) UNIQUE
);

-- 表级唯一约束(可命名)
CREATE TABLE users (
    user_id NUMBER PRIMARY KEY,
    email VARCHAR2(100),
    CONSTRAINT users_email_uk UNIQUE (email)
);

-- 组合唯一约束
CREATE TABLE user_roles (
    user_id NUMBER,
    role_id NUMBER,
    CONSTRAINT uk_user_roles UNIQUE (user_id, role_id)
);

3.5 非空约束(NOT NULL)

作用:列不能为 NULL

sql 复制代码
-- 建表时定义
CREATE TABLE products (
    product_id NUMBER NOT NULL,
    name VARCHAR2(100) NOT NULL
);

-- 修改表添加非空约束
ALTER TABLE products MODIFY (name NOT NULL);

-- 删除非空约束(通过 MODIFY)
ALTER TABLE products MODIFY (name NULL);

3.6 检查约束(CHECK)

作用:强制列值满足条件

sql 复制代码
-- 检查 salary 范围
CREATE TABLE employees (
    employee_id NUMBER PRIMARY KEY,
    salary NUMBER(8,2) CHECK (salary BETWEEN 1000 AND 50000)
);

-- 复杂检查约束
CREATE TABLE employees (
    employee_id NUMBER PRIMARY KEY,
    salary NUMBER(8,2),
    commission_pct NUMBER(2,2),
    CONSTRAINT chk_salary_commission CHECK (
        (salary > 0 AND commission_pct IS NULL) OR
        (salary > 5000 AND commission_pct BETWEEN 0 AND 0.5)
    )
);

-- 修改表添加检查约束
ALTER TABLE employees ADD CONSTRAINT chk_hire_date CHECK (hire_date <= SYSDATE);

3.7 默认值(DEFAULT)

作用:插入时未指定值则使用默认值

sql 复制代码
CREATE TABLE orders (
    order_id NUMBER PRIMARY KEY,
    order_date DATE DEFAULT SYSDATE,  -- 当前日期
    status VARCHAR2(20) DEFAULT 'NEW',
    created_by VARCHAR2(50) DEFAULT USER  -- 当前用户
);

-- 插入时使用默认值
INSERT INTO orders (order_id) VALUES (101);  -- order_date=SYSDATE, status='NEW'

四、约束状态管理

4.1 约束状态组合

每个约束有四种状态组合:

  • ENABLE VALIDATE:默认状态,启用且验证现有数据(最严格)
  • ENABLE NOVALIDATE:启用但不验证现有数据(仅对新数据生效)
  • DISABLE VALIDATE:禁用但验证现有数据(阻止新DML)
  • DISABLE NOVALIDATE:禁用且不验证(数据仓库场景)
sql 复制代码
-- 查看约束状态
SELECT constraint_name, status, validated 
FROM user_constraints 
WHERE table_name = 'EMPLOYEES';

-- 禁用约束(数据加载时提升性能)
ALTER TABLE employees DISABLE CONSTRAINT emp_salary_ck;

-- 启用并验证(加载后重新启用)
ALTER TABLE employees ENABLE VALIDATE CONSTRAINT emp_salary_ck;

-- 启用但不验证(存在脏数据时)
ALTER TABLE employees ENABLE NOVALIDATE CONSTRAINT emp_salary_ck;

-- 延迟约束检查(事务级)
ALTER TABLE employees MODIFY CONSTRAINT emp_dept_fk 
DEFERRABLE INITIALLY IMMEDIATE;

-- 在事务中临时延迟
SET CONSTRAINT emp_dept_fk DEFERRED;
INSERT INTO employees (...) VALUES (...);  -- 暂时违反外键
UPDATE departments SET department_id = ...;  -- 使引用有效
COMMIT;  -- 提交时检查

五、事务与约束的交互

5.1 约束检查时机

IMMEDIATE 约束(默认):

sql 复制代码
-- 每行 DML 后立即检查
UPDATE employees SET department_id = 999 WHERE employee_id = 101;
-- 报错:ORA-02291: 违反外键约束

DEFERRED 约束

sql 复制代码
-- 事务提交时检查
SET CONSTRAINT emp_dept_fk DEFERRED;

INSERT INTO employees (employee_id, department_id) VALUES (999, 999);  -- 成功
-- 在提交前插入被引用的部门
INSERT INTO departments (department_id) VALUES (999);

COMMIT;  -- 检查通过,成功提交

5.2 违反约束的事务回滚

sql 复制代码
BEGIN
    INSERT INTO employees (employee_id, salary) VALUES (999, -100);  -- 违反检查约束
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('违反约束: ' || SQLERRM);
        ROLLBACK;  -- 回滚整个事务
END;
/

5.3 约束与性能

sql 复制代码
-- 批量插入时禁用约束可提升性能
ALTER TABLE employees DISABLE CONSTRAINT emp_salary_ck;

-- 批量插入
INSERT INTO employees SELECT * FROM employees_staging;

-- 启用并验证
ALTER TABLE employees ENABLE VALIDATE CONSTRAINT emp_salary_ck;

六、高级主题

6.1 约束异常处理

sql 复制代码
-- 创建异常表
EXEC DBMS_ERRLOG.CREATE_ERROR_LOG('EMPLOYEES', 'EMPLOYEES_ERRLOG');

-- 批量插入时记录异常
INSERT INTO employees 
SELECT * FROM employees_temp
LOG ERRORS INTO EMPLOYEES_ERRLOG ('Batch Load') REJECT LIMIT UNLIMITED;

-- 查看异常记录
SELECT ora_err_number$, ora_err_mesg$, first_name 
FROM EMPLOYEES_ERRLOG;

6.2 视图上的约束

sql 复制代码
-- WITH CHECK OPTION:防止通过视图插入不符合视图条件的数据
CREATE VIEW high_emp AS
SELECT * FROM employees WHERE salary > 10000
WITH CHECK OPTION;

-- 尝试插入 low salary 将失败
INSERT INTO high_emp VALUES (..., 5000);  -- ORA-01402: 违反 CHECK OPTION

6.3 物化视图约束

sql 复制代码
-- 物化视图可添加约束
CREATE MATERIALIZED VIEW emp_mv
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
ENABLE QUERY REWRITE
AS
SELECT employee_id, salary FROM employees;

-- 添加主键约束
ALTER MATERIALIZED VIEW emp_mv ADD CONSTRAINT emp_mv_pk PRIMARY KEY (employee_id);

七、最佳实践与避坑指南

7.1 约束设计最佳实践

命名规范表名_列名_约束类型(如 emp_salary_ck

尽早定义 :建表时定义约束,避免后期数据污染

优先使用 NOT NULL :约束开销最小,性能影响最低

外键加索引 :外键列必须创建索引,避免全表锁

检查约束简单 :避免复杂函数,影响 DML 性能

文档化约束:记录业务含义,便于维护

7.2 常见陷阱

外键无索引 :导致子表全表锁,并发性能差

循环外键 :A→B→C→A,设计缺陷

延迟约束滥用 :增加复杂性,难以调试

CHECK 中使用函数 :导致无法创建函数索引

大批量 DML 不禁用约束:性能慢且产生大量 UNDO

7.3 外键索引示例

sql 复制代码
-- 外键列必须创建索引
CREATE TABLE order_items (
    order_id NUMBER,
    item_id NUMBER,
    CONSTRAINT fk_order_items FOREIGN KEY (order_id) REFERENCES orders(order_id)
);

-- 创建索引(否则删除父表记录时会锁整个子表)
CREATE INDEX idx_order_items_order_id ON order_items(order_id);

八、查询与维护

8.1 查询约束信息

sql 复制代码
-- 查询表的所有约束
SELECT 
    constraint_name,
    constraint_type,
    search_condition,
    status,
    validated
FROM user_constraints
WHERE table_name = 'EMPLOYEES'
ORDER BY constraint_type;

-- 约束类型说明:
-- P: Primary Key, U: Unique, R: Foreign Key, C: Check

-- 查询外键引用关系
SELECT 
    a.constraint_name,
    a.table_name,
    b.column_name,
    a.r_constraint_name,
    c.table_name AS ref_table,
    d.column_name AS ref_column
FROM user_constraints a
JOIN user_cons_columns b ON a.constraint_name = b.constraint_name
JOIN user_constraints c ON a.r_constraint_name = c.constraint_name
JOIN user_cons_columns d ON c.constraint_name = d.constraint_name
WHERE a.constraint_type = 'R'
AND a.table_name = 'EMPLOYEES';

8.2 批量操作约束

sql 复制代码
-- 导出约束定义(数据迁移时使用)
SELECT DBMS_METADATA.GET_DDL('CONSTRAINT', constraint_name) 
FROM user_constraints 
WHERE table_name = 'EMPLOYEES';

-- 禁用表的所有约束
BEGIN
    FOR c IN (SELECT constraint_name FROM user_constraints WHERE table_name = 'EMPLOYEES') LOOP
        EXECUTE IMMEDIATE 'ALTER TABLE employees DISABLE CONSTRAINT ' || c.constraint_name;
    END LOOP;
END;
/

九、总结

事务 vs 约束对比

维度 事务 约束
作用 保证操作原子性 保证数据合法性
粒度 会话级(跨语句) 行级(单条数据)
生命周期 显式/隐式开始和结束 持久化在数据字典中
性能影响 UNDO/REDO 开销 索引维护、检查开销
典型场景 转账、订单创建 主键唯一、外键关联

设计原则

  • 事务要短:避免长事务占用资源
  • 约束要早:建表时定义,数据入库前校验
  • 外键要索引:避免性能陷阱
  • 延迟要谨慎:仅用于循环依赖场景

掌握事务和约束,是构建健壮、可靠、高性能 Oracle 应用的基石。

相关推荐
天然玩家2 小时前
【数据库知识】聚簇索引&二级索引
数据库·聚簇索引·回表·二级索引
斯普信专业组2 小时前
Redis Cluster 集群化部署全流程指南:从源码编译到容器化
数据库·redis·缓存
任子菲阳2 小时前
学JavaWeb第五天——MySQL
数据库·mysql
ZePingPingZe3 小时前
MySQL查看事务与锁
数据库·mysql
TDengine (老段)3 小时前
从“被动养护”到“主动预警”,TDengine IDMP 让智慧桥梁靠数据“说话”
大数据·数据库·人工智能·物联网·时序数据库·tdengine·涛思数据
白日做梦Q3 小时前
【MySQL】9.吃透关键SQL语法:从正则表达式、窗口函数、条件函数到结果集合并的实战拆解
数据库·sql·mysql·正则表达式
likuolei3 小时前
正则表达式 - 元字符
数据库·mysql·正则表达式
侧耳倾听1113 小时前
mysql中的binlog-介绍
数据库·mysql
少云清3 小时前
【接口测试】4_PyMySQL模块 _操作数据库
服务器·网络·数据库