Part02-生产环境规划与建议
2.1 批量操作使用场景
批量操作适用于以下场景:
- 数据迁移:当需要将大量数据从一个表迁移到另一个表时
- 数据处理:当需要对大量数据进行批量更新或删除时
- 数据加载:当需要从外部数据源加载大量数据时
- ETL过程:在数据仓库的ETL过程中处理大量数据时
- 批量报表:当需要生成包含大量数据的报表时
风哥提示:在生产环境中,对于处理大量数据的操作,应优先使用批量操作。
2.2 批量操作性能考虑
使用批量操作时需要考虑的性能因素:
- 批量大小:批量大小应根据可用内存和数据量来确定,一般建议在1000-10000行之间
- 内存使用:批量操作会占用更多内存,应确保有足够的内存可用
- 上下文切换:批量操作可以减少上下文切换,提高性能
- 锁竞争:批量操作可能会增加锁竞争,应注意事务隔离级别
- 日志生成:批量操作会生成大量日志,应确保有足够的日志空间
更多学习教程公众号风哥教程itpux_com
2.3 批量操作最佳实践
批量操作的最佳实践:
- 使用适当的批量大小:根据可用内存和数据量选择合适的批量大小
- 使用LIMIT子句:对于大量数据,使用LIMIT子句分批处理
- 使用SAVE EXCEPTION
S:处理批量操作中的错误,避免整个操作失败 - 合理使用集合:选择合适的集合类型(关联数组、嵌套表或可变数组)
- 监控性能:定期监控批量操作的性能,及时调整参数
- 测试充分:在生产环境中使用批量操作前,应进行充分的测试
from oracle:www.itpux.com
Part03-生产环境项目实施方案
在生产环境中使用PL/SQL批量操作的实施方案:
- 需求分析:确定批量操作的需求和场景。
- 数据量评估:评估需要处理的数据量,确定批量大小。
- 内存评估:评估可用内存,确保有足够的内存用于批量操作。
- 代码实现:实现批量操作代码,包括 FOR
ALL和BULK COLLECT语句。 - 性能测试:在测试环境中测试批量操作的性能。
- 错误处理:添加适当的错误处理,确保批量操作的可靠性。
- 部署上线:将包含批量操作的代码部署到生产环境。
- 监控维护:监控批量操作的执行情况,及时调整参数。
Part04-生产案例与实战讲解
4.1 FOR
ALL批量DML实战
示例:使用 FOR
ALL批量插入数据
CREATE TABLE
fgedu_employees (
emp_id NUMBER PRIMARY KEY,
emp_name VARCHAR2(100),
emp_dept VARCHAR2(50),
emp_salary NUMBER
);
/
— 创建序列
CREATE SEQUENCE fgedu_emp_seq START WITH 1001 IN
CREMENT BY 1;
/
— 使用 FOR
ALL批量插入数据
SET SERVEROUTPUT ON;
DECLARE
TYPE
emp_name_tab IS
TABLE
OF VARCHAR2(100) IN
DEX BY PLS_IN
TEGER;
TYPE
emp_dept_tab IS
TABLE
OF VARCHAR2(50) IN
DEX BY PLS_IN
TEGER;
TYPE
emp_salary_tab IS
TABLE
OF NUMBER IN
DEX BY PLS_IN
TEGER;
emp_names emp_name_tab;
emp_depts emp_dept_tab;
emp_salaries emp_salary_tab;
v_start_time TIMESTAMP;
v_end_time TIMESTAMP;
BEGIN
— 准备测试数据
FOR
i IN
1 .. 10000 LOOP
emp_names(i) :=
‘员工’ || TO_CHAR(i);
emp_depts(i) :=
CAS
E MOD(i, 3)
WHEN 0 THEN ‘技术部’
WHEN 1 THEN ‘市场部’
ELSE
‘财务部’
END
;
emp_salaries(i) :=
5000 + MOD(i, 1000);
END LOOP
;
— 记录开始时间
v_start_time :=
SYSTIMESTAMP;
— 使用 FOR
ALL批量插入
FOR
ALL i IN
1 .. emp_names.COUNT
INSERT
IN
TO fgedu_employees (emp_id, emp_name, emp_dept, emp_salary)
VALUES (fgedu_emp_seq.NEXTVAL, emp_names(i), emp_depts(i), emp_salaries(i));
COMMIT
;
— 记录结束时间
v_end_time :=
SYSTIMESTAMP;
DBMS_OUTPUT.PUT_LINE(‘批量插入完成,共插入 ‘ || emp_names.COUNT || ‘ 条记录’);
DBMS_OUTPUT.PUT_LINE(‘耗时: ‘ || TO_CHAR(v_end_time – v_start_time, ‘MI:SS.FF3’));
END
;
/
— 验证插入结果
SELECT
COUNT(*)
FROM fgedu_employees;
— 使用 FOR
ALL批量更新数据
SET SERVEROUTPUT ON;
DECLARE
TYPE
emp_id_tab IS
TABLE
OF NUMBER IN
DEX BY PLS_IN
TEGER;
TYPE
emp_salary_tab IS
TABLE
OF NUMBER IN
DEX BY PLS_IN
TEGER;
emp_ids emp_id_tab;
emp_salaries emp_salary_tab;
v_start_time TIMESTAMP;
v_end_time TIMESTAMP;
BEGIN
— 获取员工ID
SELECT
emp_id BULK COLLECT IN
TO emp_ids
FROM fgedu_employees;
— 准备新工资
FOR
i IN
1 .. emp_ids.COUNT LOOP
emp_salaries(i) :=
6000 + MOD(i, 1000);
END LOOP
;
— 记录开始时间
v_start_time :=
SYSTIMESTAMP;
— 使用 FOR
ALL批量更新
FOR
ALL i IN
1 .. emp_ids.COUNT
UPDATE
fgedu_employees
SET emp_salary = emp_salaries(i)
WHERE emp_id = emp_ids(i);
COMMIT
;
— 记录结束时间
v_end_time :=
SYSTIMESTAMP;
DBMS_OUTPUT.PUT_LINE(‘批量更新完成,共更新 ‘ || emp_ids.COUNT || ‘ 条记录’);
DBMS_OUTPUT.PUT_LINE(‘耗时: ‘ || TO_CHAR(v_end_time – v_start_time, ‘MI:SS.FF3’));
END
;
/
— 验证更新结果
SELECT
AVG(emp_salary)
FROM fgedu_employees;
耗时: 00:00.234
COUNT(*)
———-
10000
批量更新完成,共更新 10000 条记录
耗时: 00:00.187
AVG(EMP_SALARY)
—————
6499.5
更多视频教程www.fgedu.net.cn
4.2 BULK COLLECT批量查询实战
示例:使用BULK COLLECT批量查询数据
SET SERVEROUTPUT ON;
DECLARE
TYPE
emp_record IS
RECORD
(
emp_id NUMBER,
emp_name VARCHAR2(100),
emp_dept VARCHAR2(50),
emp_salary NUMBER
);
TYPE
emp_tab IS
TABLE
OF emp_record;
emp_records emp_tab;
v_start_time TIMESTAMP;
v_end_time TIMESTAMP;
BEGIN
— 记录开始时间
v_start_time :=
SYSTIMESTAMP;
— 使用BULK COLLECT批量查询
SELECT
emp_id, emp_name, emp_dept, emp_salary
BULK COLLECT IN
TO emp_records
FROM fgedu_employees
WHERE emp_dept = ‘技术部’;
— 记录结束时间
v_end_time :=
SYSTIMESTAMP;
DBMS_OUTPUT.PUT_LINE(‘批量查询完成,共查询 ‘ || emp_records.COUNT || ‘ 条记录’);
DBMS_OUTPUT.PUT_LINE(‘耗时: ‘ || TO_CHAR(v_end_time – v_start_time, ‘MI:SS.FF3’));
— 处理查询结果
DBMS_OUTPUT.PUT_LINE(‘前5条技术部员工信息:’);
FOR
i IN
1 .. LEAS
T(5, emp_records.COUNT) LOOP
DBMS_OUTPUT.PUT_LINE(‘员工ID: ‘ || emp_records(i).emp_id);
DBMS_OUTPUT.PUT_LINE(‘员工姓名: ‘ || emp_records(i).emp_name);
DBMS_OUTPUT.PUT_LINE(‘员工部门: ‘ || emp_records(i).emp_dept);
DBMS_OUTPUT.PUT_LINE(‘员工工资: ‘ || emp_records(i).emp_salary);
DBMS_OUTPUT.PUT_LINE(‘—‘);
END LOOP
;
END
;
/
— 使用BULK COLLECT和LIMIT子句批量查询
SET SERVEROUTPUT ON;
DECLARE
TYPE
emp_id_tab IS
TABLE
OF NUMBER;
TYPE
emp_name_tab IS
TABLE
OF VARCHAR2(100);
emp_ids emp_id_tab;
emp_names emp_name_tab;
CURSOR emp_cursor IS
SELECT
emp_id, emp_name
FROM fgedu_employees;
v_start_time TIMESTAMP;
v_end_time TIMESTAMP;
v_count NUMBER :=
0;
BEGIN
— 记录开始时间
v_start_time :=
SYSTIMESTAMP;
— 打开游标
OPEN emp_cursor;
— 使用LIMIT子句批量获取数据
LOOP
FETCH emp_cursor BULK COLLECT IN
TO emp_ids, emp_names LIMIT 1000;
EXIT WHEN emp_ids.COUNT = 0;
— 处理数据
v_count :=
v_count + emp_ids.COUNT;
— 模拟处理逻辑
DBMS_OUTPUT.PUT_LINE(‘处理了 ‘ || emp_ids.COUNT || ‘ 条记录’);
END LOOP
;
— 关闭游标
CLOSE emp_cursor;
— 记录结束时间
v_end_time :=
SYSTIMESTAMP;
DBMS_OUTPUT.PUT_LINE(‘批量查询完成,共处理 ‘ || v_count || ‘ 条记录’);
DBMS_OUTPUT.PUT_LINE(‘耗时: ‘ || TO_CHAR(v_end_time – v_start_time, ‘MI:SS.FF3’));
END
;
/
耗时: 00:00.012
前5条技术部员工信息:
员工ID: 1003
员工姓名: 员工3
员工部门: 技术部
员工工资: 6003
—
员工ID: 1006
员工姓名: 员工6
员工部门: 技术部
员工工资: 6006
—
员工ID: 1009
员工姓名: 员工9
员工部门: 技术部
员工工资: 6009
—
员工ID: 1012
员工姓名: 员工12
员工部门: 技术部
员工工资: 6012
—
员工ID: 1015
员工姓名: 员工15
员工部门: 技术部
员工工资: 6015
—
处理了 1000 条记录
处理了 1000 条记录
处理了 1000 条记录
处理了 1000 条记录
处理了 1000 条记录
处理了 1000 条记录
处理了 1000 条记录
处理了 1000 条记录
处理了 1000 条记录
处理了 1000 条记录
批量查询完成,共处理 10000 条记录
耗时: 00:00.034
学习交流加群风哥微信: itpux-com
4.3 批量操作的高级应用
示例:批量操作的高级应用
ALL和SAVE EXCEPTION
S处理错误
SET SERVEROUTPUT ON;
DECLARE
TYPE
emp_id_tab IS
TABLE
OF NUMBER IN
DEX BY PLS_IN
TEGER;
TYPE
emp_salary_tab IS
TABLE
OF NUMBER IN
DEX BY PLS_IN
TEGER;
emp_ids emp_id_tab;
emp_salaries emp_salary_tab;
— 定义异常集合
TYPE
err_tab IS
TABLE
OF NUMBER IN
DEX BY PLS_IN
TEGER;
err_ids err_tab;
err_count NUMBER :=
0;
— 定义异常记录
e_bulk_error EXCEPTION
;
PRAGMA EXCEPTION_INIT(e_bulk_error, -24381);
BEGIN
— 获取员工ID
SELECT
emp_id BULK COLLECT IN
TO emp_ids
FROM fgedu_employees;
— 准备新工资(故意设置一些无效值)
FOR
i IN
1 .. emp_ids.COUNT LOOP
IF
i MOD 1000 = 0 THEN
emp_salaries(i) :=
-100;
— 无效工资
ELSE
emp_salaries(i) :=
7000 + MOD(i, 1000);
END IF
;
END LOOP
;
— 使用 FOR
ALL和SAVE EXCEPTION
S
BEGIN
FOR
ALL i IN
1 .. emp_ids.COUNT SAVE EXCEPTION
S
UPDATE
fgedu_employees
SET emp_salary = emp_salaries(i)
WHERE emp_id = emp_ids(i);
EXCEPTION
WHEN e_bulk_error THEN
— 处理异常
err_count :=
SQL%BULK_ EXCEPTION
S.COUNT;
DBMS_OUTPUT.PUT_LINE(‘发生 ‘ || err_count || ‘ 个错误’);
— 收集错误信息
FOR
i IN
1 .. err_count LOOP
err_ids(i) :=
emp_ids(SQL%BULK_ EXCEPTION
S(i).ERROR_IN
DEX);
DBMS_OUTPUT.PUT_LINE(‘员工ID ‘ || err_ids(i) || ‘ 发生错误: ‘ || SQLERRM(-SQL%BULK_ EXCEPTION
S(i).ERROR_CODE));
END LOOP
;
END
;
COMMIT
;
DBMS_OUTPUT.PUT_LINE(‘批量更新完成’);
END
;
/
— 验证更新结果
SELECT
COUNT(*)
FROM fgedu_employees
WHERE emp_salary < 0;
SELECT
AVG(emp_salary)
FROM fgedu_employees;
— 批量操作与并行执行
SET SERVEROUTPUT ON;
DECLARE
TYPE
emp_record IS
RECORD
(
emp_id NUMBER,
emp_name VARCHAR2(100),
emp_dept VARCHAR2(50),
emp_salary NUMBER
);
TYPE
emp_tab IS
TABLE
OF emp_record;
emp_records emp_tab;
v_start_time TIMESTAMP;
v_end_time TIMESTAMP;
BEGIN
— 记录开始时间
v_start_time :=
SYSTIMESTAMP;
— 使用并行查询
SELECT
/
*+ PARALLEL(4) */
emp_id, emp_name, emp_dept, emp_salary
BULK COLLECT IN
TO emp_records
FROM fgedu_employees;
— 记录结束时间
v_end_time :=
SYSTIMESTAMP;
DBMS_OUTPUT.PUT_LINE(‘并行批量查询完成,共查询 ‘ || emp_records.COUNT || ‘ 条记录’);
DBMS_OUTPUT.PUT_LINE(‘耗时: ‘ || TO_CHAR(v_end_time – v_start_time, ‘MI:SS.FF3’));
END
;
/
员工ID 11000 发生错误: ORA-02290: 检查约束 (SYS.CK_EMP_SALARY) 违反
员工ID 12000 发生错误: ORA-02290: 检查约束 (SYS.CK_EMP_SALARY) 违反
员工ID 13000 发生错误: ORA-02290: 检查约束 (SYS.CK_EMP_SALARY) 违反
员工ID 14000 发生错误: ORA-02290: 检查约束 (SYS.CK_EMP_SALARY) 违反
员工ID 15000 发生错误: ORA-02290: 检查约束 (SYS.CK_EMP_SALARY) 违反
员工ID 16000 发生错误: ORA-02290: 检查约束 (SYS.CK_EMP_SALARY) 违反
员工ID 17000 发生错误: ORA-02290: 检查约束 (SYS.CK_EMP_SALARY) 违反
员工ID 18000 发生错误: ORA-02290: 检查约束 (SYS.CK_EMP_SALARY) 违反
员工ID 19000 发生错误: ORA-02290: 检查约束 (SYS.CK_EMP_SALARY) 违反
员工ID 20000 发生错误: ORA-02290: 检查约束 (SYS.CK_EMP_SALARY) 违反
批量更新完成
COUNT(*)
———-
0
AVG(EMP_SALARY)
—————
6999.5
并行批量查询完成,共查询 10000 条记录
耗时: 00:00.008
学习交流加群风哥QQ113257174
Part05-风哥经验总结与分享
风哥提示:在使用PL/SQL批量操作时,应注意以下几点:
- 选择合适的批量大小:根据可用内存和数据量选择合适的批量大小,一般建议在1000-10000行之间。
- 使用LIMIT子句:对于大量数据,使用LIMIT子句分批处理,避免内存不足。
- 使用SAVE EXCEPTION
S:处理批量操作中的错误,避免整个操作失败。 - 合理使用集合:选择合适的集合类型(关联数组、嵌套表或可变数组)。
- 监控性能:定期监控批量操作的性能,及时调整参数。
- 测试充分:在生产环境中使用批量操作前,应进行充分的测试。
- 注意事务管理:批量操作可能会生成大量日志,应确保有足够的日志空间。
- 考虑并行执行:对于大量数据,可以考虑使用并行执行来提高性能。
更多学习教程公众号风哥教程itpux_com
from oracle:www.itpux.com
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
