PL/SQL(Procedural Language/SQL)是 Oracle 数据库对标准 SQL 的过程化扩展,它允许开发者在数据库中编写复杂的业务逻辑,将数据写入数据库是 PL/SQL 最核心和最常见的操作之一,本文将详细介绍在 PL/SQL 中写入数据的几种主要方法,从基础的单条操作到高效的批量处理,并探讨其最佳实践。

基础的 DML 操作
在 PL/SQL 中,写入数据主要依赖于数据操作语言(DML)语句,包括 INSERT、UPDATE 和 DELETE,这些语句可以直接嵌入到 PL/SQL 块中执行。
使用 INSERT 语句插入新数据
INSERT 语句用于向表中添加新的行,最简单的方式是直接在 PL/SQL 块中编写 SQL 语句。
BEGIN -- 向 employees 表中插入一条新员工记录 INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, job_id) VALUES (101, 'John', 'Doe', 'john.doe@example.com', SYSDATE, 'IT_PROG'); -- 提交事务,使更改永久生效 COMMIT; END; /
关键点:
BEGIN...END;定义了一个匿名的 PL/SQL 块。VALUES子句提供了要插入的数据。COMMIT;至关重要,在 Oracle 中,DML 操作是在事务中进行的,只有执行COMMIT后,更改才会被永久保存到数据库,如果执行ROLLBACK;,则该块内的所有未提交的更改都将被撤销。
使用 UPDATE 语句更新现有数据
UPDATE 语句用于修改表中已存在的数据,通常需要配合 WHERE 子句来指定要更新的行,否则会更新整个表。
BEGIN -- 更新员工号为 101 的员工的姓氏 UPDATE employees SET last_name = 'Smith' WHERE employee_id = 101; COMMIT; END; /
使用 DELETE 语句删除数据
DELETE 语句用于从表中移除行,同样,强烈建议使用 WHERE 子句以避免意外删除所有数据。
BEGIN -- 删除员工号为 101 的员工记录 DELETE FROM employees WHERE employee_id = 101; COMMIT; END; /
使用变量和动态逻辑
在实际应用中,数据往往是动态的,而不是硬编码的,PL/SQL 允许使用变量来构建更灵活和可重用的代码。
DECLARE
v_emp_id employees.employee_id%TYPE := 102;
v_first_name VARCHAR2(20) := 'Jane';
v_last_name VARCHAR2(25) := 'Doe';
v_email VARCHAR2(25) := 'jane.doe@example.com';
BEGIN
-- 使用变量进行数据插入
INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, job_id)
VALUES (v_emp_id, v_first_name, v_last_name, v_email, SYSDATE, 'SA_REP');
COMMIT;
DBMS_OUTPUT.PUT_LINE('成功插入员工: ' || v_first_name || ' ' || v_last_name);
END;
/ 说明:
DECLARE部分用于声明变量。%TYPE属性是一种很好的编程习惯,它使变量的数据类型与表中列的数据类型保持一致,提高了代码的健壮性。- 这种方式使得代码模块化,可以轻松地修改或重用。
高效的批量写入:FORALL 与 BULK COLLECT
当需要处理大量数据时(插入数千行),在循环中逐条执行 INSERT 语句效率极低,因为它会在 PL/SQL 引擎和 SQL 引擎之间进行频繁的上下文切换,为了解决这个问题,PL/SQL 提供了批量操作技术。

BULK COLLECT 用于将查询结果一次性批量加载到集合(类似于数组)中,而 FORALL 则用于将集合中的数据批量地发送给 SQL 引擎执行 DML 操作。
DECLARE
-- 定义一个索引表(集合)来存储员工数据
TYPE emp_table_type IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER;
v_employees emp_table_type;
BEGIN
-- 使用 BULK COLLECT 批量获取数据到集合中
SELECT * BULK COLLECT INTO v_employees
FROM temp_employees; -- 假设有一个临时表 temp_employees 存储待导入的数据
-- 使用 FORALL 批量插入数据
-- 这条语句会将 v_employees 集合中的所有行一次性插入到 employees 表
FORALL i IN v_employees.FIRST .. v_employees.LAST
INSERT INTO employees VALUES v_employees(i);
COMMIT;
DBMS_OUTPUT.PUT_LINE('成功批量插入 ' || v_employees.COUNT || ' 条员工记录。');
END;
/ 优势:
- 性能卓越:
FORALL通过减少上下文切换,将批量操作的性能提升了几个数量级。 - 代码简洁:相比显式循环,
FORALL的代码更简洁,意图更明确。
封装逻辑:使用存储过程
为了实现代码的重用、安全性和更好的维护性,最佳实践是将写入逻辑封装在存储过程中。
CREATE OR REPLACE PROCEDURE add_employee (
p_emp_id IN employees.employee_id%TYPE,
p_first_name IN employees.first_name%TYPE,
p_last_name IN employees.last_name%TYPE,
p_email IN employees.email%TYPE,
p_job_id IN employees.job_id%TYPE
) AS
BEGIN
INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, job_id)
VALUES (p_emp_id, p_first_name, p_last_name, p_email, SYSDATE, p_job_id);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
-- 发生错误时回滚事务
ROLLBACK;
-- 可以选择重新抛出异常或记录错误日志
RAISE;
END add_employee;
/ 调用存储过程:
BEGIN add_employee(103, 'Peter', 'Jones', 'peter.jones@example.com', 'AD_ASST'); END; /
方法对比与选择
下表小编总结了不同写入方法的特点和适用场景:
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 直接 SQL | 一次性、临时的单条操作。 | 简单直接,无需额外定义。 | 代码无法重用,硬编码数据,不安全。 |
| 使用变量 | 需要动态数据的单条操作,或在匿名块中测试逻辑。 | 灵活,可读性好,比硬编码安全。 | 逻辑与调用者耦合,不利于大规模应用架构。 |
| 批量操作 (FORALL) | 需要处理大量数据(数百行以上)的 ETL、数据迁移等场景。 | 性能极高,显著减少数据库负载。 | 代码相对复杂,需要理解集合的概念。 |
| 存储过程 | 应用程序的核心业务逻辑,需要被多次调用。 | 高重用性、安全性(权限控制)、易于维护、事务管理集中。 | 需要额外创建和管理数据库对象。 |
相关问答FAQs
在 PL/SQL 中,COMMIT 和 ROLLBACK 有什么区别?为什么它们如此重要?
解答:COMMIT 和 ROLLBACK 是事务控制的关键命令。
COMMIT:用于提交当前事务中的所有 DML 操作(如INSERT,UPDATE,DELETE),一旦执行COMMIT,这些更改就会被永久保存到数据库中,并且其他会话也能看到这些更改,事务结束。ROLLBACK:用于撤销当前事务中所有未提交的 DML 操作,执行ROLLBACK后,数据库将恢复到事务开始之前的状态,所有更改都被丢弃,事务同样结束。
它们的重要性体现在维护数据的一致性和完整性上,许多业务操作包含多个步骤(银行转账需要从一个账户扣款并向另一个账户增款),如果其中任何一个步骤失败,ROLLBACK 可以确保整个操作被撤销,从而避免数据处于不一致的状态(钱只从一个账户消失,却没有进入另一个账户),只有当所有步骤都成功时,才应该使用 COMMIT 来确认整个操作。

什么时候应该使用 FORALL 进行批量写入,而不是在循环中使用简单的 INSERT?
解答:
选择使用 FORALL 还是循环中的 INSERT,主要取决于需要处理的数据量和对性能的要求。
使用循环中的
INSERT:适用于处理少量数据(少于几十行),在这种情况下,代码编写简单直观,性能差异不明显。:当需要处理大量数据时(几百行、几千行甚至更多), FORALL是毫无疑问的最佳选择。- 性能瓶颈:在
FOR...LOOP循环中执行INSERT,每一次循环都会在 PL/SQL 引擎和 SQL 引擎之间进行一次“上下文切换”,这是一个非常耗时的过程,处理 1000 行数据就需要 1000 次切换。 : FORALL将整个集合的数据一次性传递给 SQL 引擎,只进行一次上下文切换,SQL 引擎随后在内部高效地处理所有 DML 操作,这使得批量操作的速度可以比循环快 10 倍、100 倍甚至更多。
- 性能瓶颈:在
经验法则:如果数据量可能超过 100 行,就应该开始考虑使用 FORALL,对于任何数据仓库、ETL 或大批量数据加载任务,FORALL 都是标准且必要的做法。
【版权声明】:本站所有内容均来自网络,若无意侵犯到您的权利,请及时与我们联系将尽快删除相关内容!
发表回复