Oracle约束管理 #

一、约束概述 #

1.1 什么是约束 #

约束(Constraint)是Oracle数据库中用于保证数据完整性的规则。约束定义了表中数据必须满足的条件,确保数据的准确性和一致性。

text
约束的作用
├── 保证数据完整性
│   ├── 实体完整性(主键)
│   ├── 参照完整性(外键)
│   └── 域完整性(检查、非空)
├── 防止无效数据
│   └── 在数据插入/更新时验证
└── 维护数据一致性
    └── 确保关联数据正确

1.2 约束类型 #

text
Oracle约束类型
├── PRIMARY KEY(主键约束)
│   └── 唯一标识每一行,不允许NULL
├── UNIQUE(唯一约束)
│   └── 列值唯一,允许NULL
├── NOT NULL(非空约束)
│   └── 列值不能为空
├── FOREIGN KEY(外键约束)
│   └── 引用其他表的主键或唯一键
├── CHECK(检查约束)
│   └── 自定义条件验证
└── REF(引用约束)
    └── 引用对象类型(较少使用)

二、主键约束 #

2.1 创建主键 #

sql
-- 列级主键约束
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    first_name VARCHAR2(50),
    last_name VARCHAR2(50)
);

-- 表级主键约束(命名)
CREATE TABLE employees (
    employee_id NUMBER(6),
    first_name VARCHAR2(50),
    last_name VARCHAR2(50),
    CONSTRAINT pk_employees PRIMARY KEY (employee_id)
);

-- 复合主键
CREATE TABLE order_items (
    order_id NUMBER(10),
    item_id NUMBER(5),
    product_id NUMBER(6),
    quantity NUMBER(5),
    CONSTRAINT pk_order_items PRIMARY KEY (order_id, item_id)
);

-- 使用序列创建自增主键
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    first_name VARCHAR2(50)
);

CREATE SEQUENCE seq_employees START WITH 1 INCREMENT BY 1;

INSERT INTO employees VALUES (seq_employees.NEXTVAL, 'John');

-- 12c+ 使用IDENTITY列
CREATE TABLE employees (
    employee_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    first_name VARCHAR2(50),
    last_name VARCHAR2(50)
);

2.2 管理主键 #

sql
-- 添加主键
ALTER TABLE employees ADD PRIMARY KEY (employee_id);
ALTER TABLE employees ADD CONSTRAINT pk_employees PRIMARY KEY (employee_id);

-- 删除主键
ALTER TABLE employees DROP PRIMARY KEY;
ALTER TABLE employees DROP CONSTRAINT pk_employees;

-- 删除主键(级联删除外键)
ALTER TABLE employees DROP PRIMARY KEY CASCADE;

-- 禁用/启用主键
ALTER TABLE employees DISABLE PRIMARY KEY;
ALTER TABLE employees ENABLE PRIMARY KEY;

-- 查看主键信息
SELECT 
    c.CONSTRAINT_NAME,
    cc.COLUMN_NAME,
    c.STATUS
FROM USER_CONSTRAINTS c
JOIN USER_CONS_COLUMNS cc ON c.CONSTRAINT_NAME = cc.CONSTRAINT_NAME
WHERE c.TABLE_NAME = 'EMPLOYEES' AND c.CONSTRAINT_TYPE = 'P';

2.3 主键特点 #

text
主键约束特点:
├── 唯一性:每行主键值必须唯一
├── 非空性:主键列不允许NULL值
├── 每表只能有一个主键
├── 自动创建唯一索引
└── 可由多列组成(复合主键)

三、外键约束 #

3.1 创建外键 #

sql
-- 列级外键约束
CREATE TABLE orders (
    order_id NUMBER(10) PRIMARY KEY,
    customer_id NUMBER(6) REFERENCES customers(customer_id),
    order_date DATE
);

-- 表级外键约束(命名)
CREATE TABLE orders (
    order_id NUMBER(10) PRIMARY KEY,
    customer_id NUMBER(6),
    order_date DATE,
    CONSTRAINT fk_orders_customer 
        FOREIGN KEY (customer_id) 
        REFERENCES customers(customer_id)
);

-- 自引用外键(父子关系在同一表)
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    employee_name VARCHAR2(50),
    manager_id NUMBER(6),
    CONSTRAINT fk_manager 
        FOREIGN KEY (manager_id) 
        REFERENCES employees(employee_id)
);

-- 复合外键
CREATE TABLE order_items (
    order_id NUMBER(10),
    item_id NUMBER(5),
    product_id NUMBER(6),
    CONSTRAINT fk_order_items_orders 
        FOREIGN KEY (order_id) 
        REFERENCES orders(order_id)
);

3.2 外键选项 #

sql
-- ON DELETE CASCADE:删除父记录时自动删除子记录
CREATE TABLE orders (
    order_id NUMBER(10) PRIMARY KEY,
    customer_id NUMBER(6),
    CONSTRAINT fk_customer 
        FOREIGN KEY (customer_id) 
        REFERENCES customers(customer_id)
        ON DELETE CASCADE
);

-- ON DELETE SET NULL:删除父记录时设置子记录为NULL
CREATE TABLE orders (
    order_id NUMBER(10) PRIMARY KEY,
    customer_id NUMBER(6),
    CONSTRAINT fk_customer 
        FOREIGN KEY (customer_id) 
        REFERENCES customers(customer_id)
        ON DELETE SET NULL
);

-- ON DELETE NO ACTION:默认行为,不允许删除有子记录的父记录
CREATE TABLE orders (
    order_id NUMBER(10) PRIMARY KEY,
    customer_id NUMBER(6),
    CONSTRAINT fk_customer 
        FOREIGN KEY (customer_id) 
        REFERENCES customers(customer_id)
        ON DELETE NO ACTION
);

3.3 管理外键 #

sql
-- 添加外键
ALTER TABLE orders ADD CONSTRAINT fk_customer
FOREIGN KEY (customer_id) REFERENCES customers(customer_id);

-- 删除外键
ALTER TABLE orders DROP CONSTRAINT fk_customer;

-- 禁用/启用外键
ALTER TABLE orders DISABLE CONSTRAINT fk_customer;
ALTER TABLE orders ENABLE CONSTRAINT fk_customer;

-- 启用外键(不验证现有数据)
ALTER TABLE orders ENABLE NOVALIDATE CONSTRAINT fk_customer;

-- 查看外键信息
SELECT 
    c.CONSTRAINT_NAME,
    cc.COLUMN_NAME,
    c.R_CONSTRAINT_NAME AS REFERENCED_CONSTRAINT,
    c.DELETE_RULE
FROM USER_CONSTRAINTS c
JOIN USER_CONS_COLUMNS cc ON c.CONSTRAINT_NAME = cc.CONSTRAINT_NAME
WHERE c.TABLE_NAME = 'ORDERS' AND c.CONSTRAINT_TYPE = 'R';

3.4 外键注意事项 #

text
外键约束注意事项:
├── 父表必须有主键或唯一约束
├── 子表外键列数据类型必须与父表匹配
├── 外键列允许NULL值(除非另有非空约束)
├── 删除父表需要先删除外键或使用CASCADE
└── 大量数据操作时考虑禁用外键提升性能

四、唯一约束 #

4.1 创建唯一约束 #

sql
-- 列级唯一约束
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    email VARCHAR2(100) UNIQUE,
    phone VARCHAR2(20)
);

-- 表级唯一约束(命名)
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    email VARCHAR2(100),
    phone VARCHAR2(20),
    CONSTRAINT uk_email UNIQUE (email)
);

-- 复合唯一约束
CREATE TABLE user_roles (
    user_id NUMBER(6),
    role_id NUMBER(6),
    CONSTRAINT uk_user_role UNIQUE (user_id, role_id)
);

-- 多个唯一约束
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    email VARCHAR2(100),
    phone VARCHAR2(20),
    CONSTRAINT uk_email UNIQUE (email),
    CONSTRAINT uk_phone UNIQUE (phone)
);

4.2 管理唯一约束 #

sql
-- 添加唯一约束
ALTER TABLE employees ADD CONSTRAINT uk_email UNIQUE (email);
ALTER TABLE employees ADD UNIQUE (email);

-- 删除唯一约束
ALTER TABLE employees DROP CONSTRAINT uk_email;
ALTER TABLE employees DROP UNIQUE (email);

-- 禁用/启用唯一约束
ALTER TABLE employees DISABLE CONSTRAINT uk_email;
ALTER TABLE employees ENABLE CONSTRAINT uk_email;

-- 查看唯一约束
SELECT 
    c.CONSTRAINT_NAME,
    cc.COLUMN_NAME,
    c.STATUS
FROM USER_CONSTRAINTS c
JOIN USER_CONS_COLUMNS cc ON c.CONSTRAINT_NAME = cc.CONSTRAINT_NAME
WHERE c.TABLE_NAME = 'EMPLOYEES' AND c.CONSTRAINT_TYPE = 'U';

4.3 唯一约束与主键区别 #

特性 主键 唯一约束
每表数量 只能一个 可以多个
NULL值 不允许 允许(但只能一个)
索引 自动创建唯一索引 自动创建唯一索引
用途 标识行 保证列值唯一

五、检查约束 #

5.1 创建检查约束 #

sql
-- 列级检查约束
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    salary NUMBER(10,2) CHECK (salary > 0),
    age NUMBER(3) CHECK (age >= 18 AND age <= 65)
);

-- 表级检查约束(命名)
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    salary NUMBER(10,2),
    CONSTRAINT chk_salary CHECK (salary > 0)
);

-- 复杂检查约束
CREATE TABLE products (
    product_id NUMBER(6) PRIMARY KEY,
    product_name VARCHAR2(100),
    price NUMBER(10,2),
    discount NUMBER(5,2),
    stock_quantity NUMBER(5),
    status VARCHAR2(20),
    
    CONSTRAINT chk_price CHECK (price > 0),
    CONSTRAINT chk_discount CHECK (discount >= 0 AND discount <= 100),
    CONSTRAINT chk_stock CHECK (stock_quantity >= 0),
    CONSTRAINT chk_status CHECK (status IN ('ACTIVE', 'INACTIVE', 'DISCONTINUED'))
);

-- 多列检查约束
CREATE TABLE orders (
    order_id NUMBER(10) PRIMARY KEY,
    start_date DATE,
    end_date DATE,
    CONSTRAINT chk_dates CHECK (end_date >= start_date)
);

-- 条件检查约束
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    salary NUMBER(10,2),
    commission_pct NUMBER(5,2),
    CONSTRAINT chk_commission CHECK (
        commission_pct IS NULL OR 
        (commission_pct >= 0 AND commission_pct <= 100)
    )
);

5.2 管理检查约束 #

sql
-- 添加检查约束
ALTER TABLE employees ADD CONSTRAINT chk_salary CHECK (salary > 0);

-- 删除检查约束
ALTER TABLE employees DROP CONSTRAINT chk_salary;

-- 禁用/启用检查约束
ALTER TABLE employees DISABLE CONSTRAINT chk_salary;
ALTER TABLE employees ENABLE CONSTRAINT chk_salary;

-- 查看检查约束
SELECT 
    c.CONSTRAINT_NAME,
    c.SEARCH_CONDITION,
    c.STATUS
FROM USER_CONSTRAINTS c
WHERE c.TABLE_NAME = 'EMPLOYEES' AND c.CONSTRAINT_TYPE = 'C';

5.3 检查约束限制 #

sql
-- 检查约束的限制:
-- 1. 不能包含子查询
-- 2. 不能包含SYSDATE、USER等函数
-- 3. 不能引用其他行的值
-- 4. 不能调用用户定义函数

-- 无效的检查约束示例
-- 错误:包含子查询
ALTER TABLE employees ADD CONSTRAINT chk_dept 
CHECK (department_id IN (SELECT department_id FROM departments));

-- 错误:包含SYSDATE
ALTER TABLE employees ADD CONSTRAINT chk_hire_date 
CHECK (hire_date <= SYSDATE);

-- 替代方案:使用触发器
CREATE OR REPLACE TRIGGER trg_check_dept
BEFORE INSERT OR UPDATE ON employees
FOR EACH ROW
BEGIN
    IF :NEW.department_id NOT IN (SELECT department_id FROM departments) THEN
        RAISE_APPLICATION_ERROR(-20001, 'Invalid department');
    END IF;
END;
/

六、非空约束 #

6.1 创建非空约束 #

sql
-- 列级非空约束
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    first_name VARCHAR2(50) NOT NULL,
    last_name VARCHAR2(50) NOT NULL,
    email VARCHAR2(100)
);

-- 非空约束与默认值
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    status VARCHAR2(20) DEFAULT 'ACTIVE' NOT NULL,
    create_time TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);

6.2 管理非空约束 #

sql
-- 添加非空约束
ALTER TABLE employees MODIFY last_name NOT NULL;

-- 删除非空约束
ALTER TABLE employees MODIFY last_name NULL;

-- 查看非空约束
SELECT 
    COLUMN_NAME,
    NULLABLE,
    DATA_DEFAULT
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'EMPLOYEES' AND NULLABLE = 'N';

6.3 非空约束特点 #

text
非空约束特点:
├── 只能定义在列级别
├── 不自动创建索引
├── 与CHECK约束类似但语法不同
├── NULL表示未知值,不等于空字符串
└── 主键列自动具有非空约束

七、约束状态管理 #

7.1 约束状态 #

sql
-- 约束状态组合
-- ENABLE VALIDATE:启用并验证所有数据(默认)
-- ENABLE NOVALIDATE:启用但不验证现有数据
-- DISABLE VALIDATE:禁用但验证新数据
-- DISABLE NOVALIDATE:禁用且不验证

-- 启用约束(验证所有数据)
ALTER TABLE employees ENABLE VALIDATE CONSTRAINT chk_salary;

-- 启用约束(不验证现有数据)
ALTER TABLE employees ENABLE NOVALIDATE CONSTRAINT chk_salary;

-- 禁用约束(保留索引)
ALTER TABLE employees DISABLE VALIDATE CONSTRAINT chk_salary;

-- 完全禁用约束
ALTER TABLE employees DISABLE NOVALIDATE CONSTRAINT chk_salary;

7.2 约束状态应用场景 #

sql
-- 场景1:大批量数据导入
-- 先禁用约束,导入数据,再启用约束
ALTER TABLE employees DISABLE CONSTRAINT fk_dept;
-- 导入数据...
ALTER TABLE employees ENABLE CONSTRAINT fk_dept;

-- 场景2:历史数据清理
-- 启用约束但不验证历史数据
ALTER TABLE employees ENABLE NOVALIDATE CONSTRAINT chk_salary;

-- 场景3:数据修复
-- 禁用约束修复数据后重新启用
ALTER TABLE employees DISABLE CONSTRAINT chk_salary;
-- 修复数据...
ALTER TABLE employees ENABLE VALIDATE CONSTRAINT chk_salary;

7.3 查看约束状态 #

sql
-- 查看约束状态
SELECT 
    CONSTRAINT_NAME,
    CONSTRAINT_TYPE,
    STATUS,
    VALIDATED,
    DEFERRED
FROM USER_CONSTRAINTS
WHERE TABLE_NAME = 'EMPLOYEES';

-- STATUS:ENABLED/DISABLED
-- VALIDATED:VALIDATED/NOT VALIDATED
-- DEFERRED:IMMEDIATE/DEFERRED

八、延迟约束 #

8.1 创建延迟约束 #

sql
-- 创建延迟约束
CREATE TABLE orders (
    order_id NUMBER(10) PRIMARY KEY,
    customer_id NUMBER(6),
    CONSTRAINT fk_customer 
        FOREIGN KEY (customer_id) 
        REFERENCES customers(customer_id)
        DEFERRABLE INITIALLY DEFERRED
);

-- DEFERRABLE:允许延迟检查
-- INITIALLY IMMEDIATE:默认立即检查(每条语句后)
-- INITIALLY DEFERRED:默认延迟检查(事务提交时)

8.2 使用延迟约束 #

sql
-- 设置约束延迟
SET CONSTRAINTS ALL DEFERRED;
SET CONSTRAINTS fk_customer DEFERRED;

-- 设置约束立即
SET CONSTRAINTS ALL IMMEDIATE;
SET CONSTRAINTS fk_customer IMMEDIATE;

-- 延迟约束应用场景
-- 场景:循环引用
-- 表A引用表B,表B引用表A

-- 使用延迟约束解决循环引用
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    department_id NUMBER(4),
    CONSTRAINT fk_dept 
        FOREIGN KEY (department_id) 
        REFERENCES departments(department_id)
        DEFERRABLE INITIALLY DEFERRED
);

CREATE TABLE departments (
    department_id NUMBER(4) PRIMARY KEY,
    manager_id NUMBER(6),
    CONSTRAINT fk_manager 
        FOREIGN KEY (manager_id) 
        REFERENCES employees(employee_id)
        DEFERRABLE INITIALLY DEFERRED
);

-- 插入数据
SET CONSTRAINTS ALL DEFERRED;
INSERT INTO employees VALUES (1, 10);
INSERT INTO departments VALUES (10, 1);
COMMIT;  -- 提交时验证约束

九、约束异常处理 #

9.1 查找约束违规数据 #

sql
-- 启用约束时创建异常表
-- 创建异常表
BEGIN
    DBMS_IOT.CREATE_EXCEPTION_TABLE('EXCEPTIONS');
END;
/

-- 或手动创建
CREATE TABLE exceptions (
    row_id ROWID,
    owner VARCHAR2(30),
    table_name VARCHAR2(30),
    constraint VARCHAR2(30)
);

-- 启用约束并记录异常
ALTER TABLE employees ENABLE CONSTRAINT chk_salary 
EXCEPTIONS INTO exceptions;

-- 查找违规数据
SELECT e.* 
FROM employees e, exceptions ex
WHERE e.ROWID = ex.row_id
AND ex.table_name = 'EMPLOYEES';

9.2 处理约束违规 #

sql
-- 查找并修复违规数据
-- 1. 查看违规记录
SELECT * FROM exceptions WHERE table_name = 'EMPLOYEES';

-- 2. 查看具体数据
SELECT * FROM employees WHERE ROWID IN (
    SELECT row_id FROM exceptions WHERE table_name = 'EMPLOYEES'
);

-- 3. 修复数据
UPDATE employees SET salary = 0 WHERE salary < 0;

-- 4. 清空异常表
DELETE FROM exceptions;

-- 5. 重新启用约束
ALTER TABLE employees ENABLE CONSTRAINT chk_salary;

9.3 约束错误信息 #

sql
-- 常见约束错误
-- ORA-00001:违反唯一约束
-- ORA-02290:违反检查约束
-- ORA-02291:违反外键约束(父记录不存在)
-- ORA-02292:违反外键约束(子记录存在)
-- ORA-01400:违反非空约束

-- 查看约束错误详情
SELECT 
    c.CONSTRAINT_NAME,
    c.CONSTRAINT_TYPE,
    c.SEARCH_CONDITION,
    c.R_CONSTRAINT_NAME
FROM USER_CONSTRAINTS c
WHERE c.CONSTRAINT_NAME = '约束名';

十、约束信息查询 #

10.1 约束相关视图 #

视图名称 说明
USER_CONSTRAINTS 用户约束信息
ALL_CONSTRAINTS 所有可访问约束
DBA_CONSTRAINTS 所有约束
USER_CONS_COLUMNS 约束列信息
ALL_CONS_COLUMNS 所有约束列
DBA_CONS_COLUMNS 所有约束列

10.2 常用查询 #

sql
-- 查看表的所有约束
SELECT 
    c.CONSTRAINT_NAME,
    c.CONSTRAINT_TYPE,
    c.STATUS,
    c.VALIDATED,
    cc.COLUMN_NAME
FROM USER_CONSTRAINTS c
LEFT JOIN USER_CONS_COLUMNS cc ON c.CONSTRAINT_NAME = cc.CONSTRAINT_NAME
WHERE c.TABLE_NAME = 'EMPLOYEES'
ORDER BY c.CONSTRAINT_TYPE, c.CONSTRAINT_NAME;

-- 查看外键关系
SELECT 
    c.TABLE_NAME AS CHILD_TABLE,
    cc.COLUMN_NAME AS CHILD_COLUMN,
    r.TABLE_NAME AS PARENT_TABLE,
    rc.COLUMN_NAME AS PARENT_COLUMN,
    c.DELETE_RULE
FROM USER_CONSTRAINTS c
JOIN USER_CONS_COLUMNS cc ON c.CONSTRAINT_NAME = cc.CONSTRAINT_NAME
JOIN USER_CONSTRAINTS r ON c.R_CONSTRAINT_NAME = r.CONSTRAINT_NAME
JOIN USER_CONS_COLUMNS rc ON r.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
WHERE c.CONSTRAINT_TYPE = 'R';

-- 查看禁用的约束
SELECT 
    TABLE_NAME,
    CONSTRAINT_NAME,
    CONSTRAINT_TYPE,
    STATUS
FROM USER_CONSTRAINTS
WHERE STATUS = 'DISABLED';

十一、总结 #

约束管理要点:

约束类型 用途 关键字
PRIMARY KEY 主键 PRIMARY KEY
FOREIGN KEY 外键 FOREIGN KEY REFERENCES
UNIQUE 唯一 UNIQUE
CHECK 检查 CHECK
NOT NULL 非空 NOT NULL

最佳实践:

  1. 为所有约束命名,便于管理
  2. 合理使用外键,考虑性能影响
  3. 大批量操作时考虑禁用约束
  4. 使用延迟约束处理复杂依赖
  5. 定期检查约束状态

下一步,让我们学习序列与同义词!

最后更新:2026-03-27