Oracle数据删除 #

一、删除语句概述 #

1.1 删除方式 #

text
Oracle数据删除方式
├── DELETE语句
│   ├── DML操作
│   ├── 可以回滚
│   ├── 支持WHERE条件
│   └── 触发触发器
├── TRUNCATE语句
│   ├── DDL操作
│   ├── 不能回滚
│   ├── 删除所有数据
│   └── 不触发触发器
└── DROP语句
    ├── DDL操作
    ├── 删除表结构和数据
    └── 可闪回恢复

1.2 示例表结构 #

sql
-- 创建示例表
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    first_name VARCHAR2(50),
    last_name VARCHAR2(50),
    email VARCHAR2(100),
    salary NUMBER(8,2),
    department_id NUMBER(4),
    manager_id NUMBER(6),
    hire_date DATE
);

-- 插入测试数据
INSERT INTO employees VALUES (1, 'John', 'Doe', 'john@example.com', 5000, 10, 100, SYSDATE);
INSERT INTO employees VALUES (2, 'Jane', 'Smith', 'jane@example.com', 6000, 10, 100, SYSDATE);
INSERT INTO employees VALUES (3, 'Bob', 'Johnson', 'bob@example.com', 7000, 20, 101, SYSDATE);
INSERT INTO employees VALUES (4, 'Alice', 'Williams', 'alice@example.com', 8000, 20, 101, SYSDATE);
INSERT INTO employees VALUES (5, 'Charlie', 'Brown', 'charlie@example.com', 9000, 30, 102, SYSDATE);

-- 创建子表
CREATE TABLE orders (
    order_id NUMBER(10) PRIMARY KEY,
    customer_id NUMBER(6),
    order_date DATE,
    total_amount NUMBER(12,2),
    CONSTRAINT fk_customer FOREIGN KEY (customer_id) 
        REFERENCES employees(employee_id)
);

二、DELETE语句 #

2.1 基本语法 #

sql
-- DELETE基本语法
DELETE FROM table_name
WHERE condition;

-- 删除所有数据
DELETE FROM employees;

-- 删除指定条件数据
DELETE FROM employees
WHERE employee_id = 1;

2.2 条件删除 #

sql
-- 使用WHERE条件
DELETE FROM employees
WHERE department_id = 10;

-- 使用多个条件
DELETE FROM employees
WHERE department_id = 10 AND salary < 5000;

-- 使用IN条件
DELETE FROM employees
WHERE employee_id IN (1, 2, 3);

-- 使用BETWEEN条件
DELETE FROM employees
WHERE salary BETWEEN 5000 AND 7000;

-- 使用LIKE条件
DELETE FROM employees
WHERE email LIKE '%@oldcompany.com';

-- 使用IS NULL条件
DELETE FROM employees
WHERE manager_id IS NULL;

-- 使用子查询条件
DELETE FROM employees
WHERE department_id IN (
    SELECT department_id 
    FROM departments 
    WHERE location_id = 1700
);

2.3 使用子查询删除 #

sql
-- 使用相关子查询删除
DELETE FROM employees e
WHERE EXISTS (
    SELECT 1
    FROM departments d
    WHERE d.department_id = e.department_id
    AND d.location_id = 1700
);

-- 使用子查询比较删除
DELETE FROM employees
WHERE salary < (
    SELECT AVG(salary)
    FROM employees
);

-- 使用WITH子句删除(12c+)
WITH low_salary AS (
    SELECT employee_id
    FROM employees
    WHERE salary < 5000
)
DELETE FROM employees
WHERE employee_id IN (SELECT employee_id FROM low_salary);

2.4 删除重复数据 #

sql
-- 删除重复数据(保留一条)
-- 方法1:使用ROWID
DELETE FROM employees
WHERE ROWID NOT IN (
    SELECT MAX(ROWID)
    FROM employees
    GROUP BY employee_id
);

-- 方法2:使用ROW_NUMBER()
DELETE FROM employees
WHERE ROWID IN (
    SELECT ROWID
    FROM (
        SELECT 
            ROWID,
            ROW_NUMBER() OVER (PARTITION BY employee_id ORDER BY hire_date) AS rn
        FROM employees
    )
    WHERE rn > 1
);

-- 方法3:创建临时表
CREATE TABLE employees_temp AS
SELECT DISTINCT * FROM employees;

TRUNCATE TABLE employees;
INSERT INTO employees SELECT * FROM employees_temp;
DROP TABLE employees_temp;

三、TRUNCATE语句 #

3.1 基本语法 #

sql
-- TRUNCATE基本语法
TRUNCATE TABLE table_name;

-- TRUNCATE选项
TRUNCATE TABLE employees;
TRUNCATE TABLE employees DROP ALL STORAGE;  -- 释放所有空间
TRUNCATE TABLE employees REUSE STORAGE;     -- 保留空间

-- TRUNCATE特点
-- 1. DDL操作,自动提交
-- 2. 不能回滚
-- 3. 不触发DELETE触发器
-- 4. 重置高水位线
-- 5. 比DELETE快

3.2 TRUNCATE vs DELETE #

特性 DELETE TRUNCATE
类型 DML DDL
回滚 可以 不可以
WHERE 支持 不支持
触发器 触发 不触发
高水位线 不重置 重置
速度
日志 行级日志 最小日志
空间释放 不释放 可选择释放

3.3 TRUNCATE注意事项 #

sql
-- TRUNCATE限制
-- 1. 不能TRUNCATE有外键引用的表
-- TRUNCATE TABLE employees;  -- 如果orders表引用employees,会报错

-- 解决方案:先禁用外键
ALTER TABLE orders DISABLE CONSTRAINT fk_customer;
TRUNCATE TABLE employees;
ALTER TABLE orders ENABLE CONSTRAINT fk_customer;

-- 2. TRUNCATE后数据不可恢复
-- 确保已备份重要数据

-- 3. TRUNCATE会重置序列(如果使用IDENTITY列)
-- 12c+ IDENTITY列
CREATE TABLE test (
    id NUMBER GENERATED BY DEFAULT AS IDENTITY,
    name VARCHAR2(50)
);

INSERT INTO test VALUES (DEFAULT, 'Test1');
INSERT INTO test VALUES (DEFAULT, 'Test2');

SELECT * FROM test;  -- id: 1, 2

TRUNCATE TABLE test;

INSERT INTO test VALUES (DEFAULT, 'Test3');
SELECT * FROM test;  -- id: 1(序列重置)

四、级联删除 #

4.1 使用ON DELETE CASCADE #

sql
-- 创建带级联删除的外键
CREATE TABLE orders (
    order_id NUMBER(10) PRIMARY KEY,
    customer_id NUMBER(6),
    order_date DATE,
    CONSTRAINT fk_customer 
        FOREIGN KEY (customer_id) 
        REFERENCES employees(employee_id)
        ON DELETE CASCADE
);

-- 删除父记录时自动删除子记录
DELETE FROM employees WHERE employee_id = 1;
-- orders表中customer_id=1的记录也会被删除

4.2 使用ON DELETE SET NULL #

sql
-- 创建带SET NULL的外键
CREATE TABLE orders (
    order_id NUMBER(10) PRIMARY KEY,
    customer_id NUMBER(6),
    order_date DATE,
    CONSTRAINT fk_customer 
        FOREIGN KEY (customer_id) 
        REFERENCES employees(employee_id)
        ON DELETE SET NULL
);

-- 删除父记录时子记录外键设为NULL
DELETE FROM employees WHERE employee_id = 1;
-- orders表中customer_id=1的记录的customer_id会变为NULL

4.3 手动级联删除 #

sql
-- 手动删除子记录再删除父记录
-- 方法1:分步删除
DELETE FROM orders WHERE customer_id = 1;
DELETE FROM employees WHERE employee_id = 1;
COMMIT;

-- 方法2:使用存储过程
CREATE OR REPLACE PROCEDURE delete_employee(p_emp_id NUMBER) AS
BEGIN
    -- 删除相关订单
    DELETE FROM orders WHERE customer_id = p_emp_id;
    
    -- 删除员工
    DELETE FROM employees WHERE employee_id = p_emp_id;
    
    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE;
END;
/

-- 执行删除
EXEC delete_employee(1);

五、批量删除 #

5.1 分批删除 #

sql
-- 分批删除大量数据
DECLARE
    v_batch_size NUMBER := 10000;
    v_rows_deleted NUMBER;
BEGIN
    LOOP
        DELETE FROM employees
        WHERE department_id = 10
        AND ROWNUM <= v_batch_size;
        
        v_rows_deleted := SQL%ROWCOUNT;
        COMMIT;
        
        DBMS_OUTPUT.PUT_LINE('Deleted ' || v_rows_deleted || ' rows');
        
        EXIT WHEN v_rows_deleted < v_batch_size;
    END LOOP;
    
    DBMS_OUTPUT.PUT_LINE('Deletion completed');
END;
/

5.2 使用FORALL批量删除 #

sql
-- PL/SQL中使用FORALL批量删除
DECLARE
    TYPE id_array IS TABLE OF NUMBER;
    v_ids id_array;
BEGIN
    -- 获取要删除的ID
    SELECT employee_id
    BULK COLLECT INTO v_ids
    FROM employees
    WHERE department_id = 10;
    
    -- 批量删除
    FORALL i IN 1..v_ids.COUNT
        DELETE FROM employees
        WHERE employee_id = v_ids(i);
    
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('Deleted ' || SQL%ROWCOUNT || ' rows');
END;
/

5.3 大表删除优化 #

sql
-- 方法1:创建新表保留需要的数据
CREATE TABLE employees_new AS
SELECT * FROM employees
WHERE department_id != 10;

-- 替换原表
DROP TABLE employees;
RENAME employees_new TO employees;

-- 重建索引和约束
CREATE INDEX idx_emp_dept ON employees(department_id);

-- 方法2:使用分区表
-- 删除整个分区
ALTER TABLE sales DROP PARTITION p2023;

-- 截断分区
ALTER TABLE sales TRUNCATE PARTITION p2023;

六、删除性能优化 #

6.1 使用索引优化删除 #

sql
-- 创建索引加速删除
CREATE INDEX idx_emp_dept ON employees(department_id);

-- 使用索引列删除
DELETE FROM employees WHERE department_id = 10;

-- 查看执行计划
EXPLAIN PLAN FOR
DELETE FROM employees WHERE department_id = 10;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

6.2 禁用约束和触发器 #

sql
-- 大批量删除时禁用约束和触发器
-- 禁用触发器
ALTER TABLE employees DISABLE ALL TRIGGERS;

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

-- 执行删除
DELETE FROM employees WHERE department_id = 10;

-- 启用触发器
ALTER TABLE employees ENABLE ALL TRIGGERS;

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

6.3 使用并行删除 #

sql
-- 使用并行提示
DELETE /*+ PARALLEL(employees, 4) */ FROM employees
WHERE department_id = 10;

-- 设置并行度
ALTER SESSION ENABLE PARALLEL DML;

DELETE /*+ PARALLEL(employees, 8) */ FROM employees
WHERE department_id = 10;

COMMIT;

七、删除错误处理 #

7.1 处理删除异常 #

sql
-- 捕获删除异常
BEGIN
    DELETE FROM employees WHERE employee_id = 1;
EXCEPTION
    WHEN OTHERS THEN
        IF SQLCODE = -2292 THEN
            DBMS_OUTPUT.PUT_LINE('Foreign key constraint violated');
        ELSE
            RAISE;
        END IF;
END;
/

-- 检查删除行数
DECLARE
    v_rows_deleted NUMBER;
BEGIN
    DELETE FROM employees WHERE department_id = 99;
    
    v_rows_deleted := SQL%ROWCOUNT;
    
    IF v_rows_deleted = 0 THEN
        DBMS_OUTPUT.PUT_LINE('No rows deleted');
    ELSE
        DBMS_OUTPUT.PUT_LINE(v_rows_deleted || ' rows deleted');
    END IF;
END;
/

7.2 使用DML ERROR LOGGING #

sql
-- 创建错误日志表
EXEC DBMS_ERRLOG.CREATE_ERROR_LOG('employees', 'err_employees');

-- 使用LOG ERRORS记录错误
DELETE FROM employees
WHERE department_id = 10
LOG ERRORS INTO err_employees REJECT LIMIT UNLIMITED;

-- 查看错误记录
SELECT * FROM err_employees;

八、删除恢复 #

8.1 使用闪回查询 #

sql
-- 查看删除前的数据
SELECT * FROM employees
AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' HOUR);

-- 恢复误删的数据
INSERT INTO employees
SELECT * FROM employees
AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' HOUR)
WHERE employee_id = 1;

8.2 使用闪回表 #

sql
-- 启用行移动
ALTER TABLE employees ENABLE ROW MOVEMENT;

-- 闪回表到指定时间
FLASHBACK TABLE employees
TO TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' HOUR);

-- 闪回表到指定SCN
FLASHBACK TABLE employees TO SCN 1234567;

8.3 从回收站恢复 #

sql
-- DROP后的表可以从回收站恢复
DROP TABLE employees;

-- 查看回收站
SELECT * FROM USER_RECYCLEBIN;

-- 闪回恢复
FLASHBACK TABLE employees TO BEFORE DROP;

-- 彻底删除(不进回收站)
DROP TABLE employees PURGE;

九、删除触发器 #

9.1 创建删除触发器 #

sql
-- 创建删除触发器
CREATE OR REPLACE TRIGGER trg_employees_delete
BEFORE DELETE ON employees
FOR EACH ROW
BEGIN
    -- 记录删除日志
    INSERT INTO employees_audit (
        employee_id,
        first_name,
        last_name,
        action,
        action_date
    ) VALUES (
        :OLD.employee_id,
        :OLD.first_name,
        :OLD.last_name,
        'DELETE',
        SYSDATE
    );
END;
/

-- 测试触发器
DELETE FROM employees WHERE employee_id = 1;
SELECT * FROM employees_audit;

9.2 级联删除触发器 #

sql
-- 级联删除触发器
CREATE OR REPLACE TRIGGER trg_employees_cascade_delete
BEFORE DELETE ON employees
FOR EACH ROW
BEGIN
    -- 删除相关订单
    DELETE FROM orders WHERE customer_id = :OLD.employee_id;
    
    -- 删除相关记录
    DELETE FROM employee_history WHERE employee_id = :OLD.employee_id;
END;
/

十、删除最佳实践 #

10.1 安全删除 #

sql
-- 1. 先查询再删除
SELECT COUNT(*) FROM employees WHERE department_id = 10;
-- 确认数量后执行删除

-- 2. 使用事务
BEGIN
    DELETE FROM employees WHERE department_id = 10;
    -- 检查结果
    DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT || ' rows will be deleted');
    -- 确认后提交
    COMMIT;
END;
/

-- 3. 备份重要数据
CREATE TABLE employees_backup AS
SELECT * FROM employees WHERE department_id = 10;

-- 4. 使用软删除
ALTER TABLE employees ADD is_deleted NUMBER(1) DEFAULT 0;

UPDATE employees SET is_deleted = 1 WHERE department_id = 10;

-- 查询时过滤
SELECT * FROM employees WHERE is_deleted = 0;

10.2 性能最佳实践 #

sql
-- 1. 大批量删除使用分批处理
-- 2. 禁用不必要的索引和约束
-- 3. 使用并行处理
-- 4. 考虑使用TRUNCATE代替DELETE
-- 5. 使用分区表按分区删除

十一、总结 #

删除数据要点:

删除方式 特点 适用场景
DELETE 可回滚,支持WHERE 条件删除
TRUNCATE 快速,不可回滚 清空表
DROP 删除表结构 删除表

最佳实践:

  1. 先查询确认再删除
  2. 使用事务保证一致性
  3. 大批量删除分批处理
  4. 重要数据先备份
  5. 考虑使用软删除

下一步,让我们学习基础查询!

最后更新:2026-03-27