实战BULK COLLECT(成批聚合类型)和数组集合type类型is table of 表%rowtype index by binary_integer

本文通过多个实例展示了如何使用PL/SQL进行批量查询、更新、插入和删除操作,这些操作能显著提升数据处理效率。
 

例1: 批量 查询部门号为 "10"  号的并把它们打印出来 .

DECLARE

   TYPE emp_table_type IS TABLE OF my_emp%ROWTYPE INDEX BY BINARY_INTEGER;

   v_emp_table emp_table_type;

BEGIN

    SELECT * BULK COLLECT INTO v_emp_table FROM my_emp WHERE deptno=&deptno;

    FOR i IN 1..v_emp_table.COUNT LOOP

           dbms_output.put_line('EMPLOYEE_INFO:'||v_emp_table(i).ename||

                                                      ','||v_emp_table(i).job||

                                                      ','||v_emp_table(i).hiredate);

   END LOOP;

END;

 

说明部分:

1. DECLARE  说明以下你要声明的部分

2. Type 声明是类型  emp_table_typ e 类型的名字

3. IS TABLE OF  指定是一个集合的表的数组类型, 简单的来说就是一个可以存储一列多行的数据类型 , my_emp 指出在哪个表上( 存在的表 ) %ROWTYPE 指在表上的行的数据类型.

4. INDEX BY BINARY_INTEGER  指索引组织类型

5. v_emp_table 定义一个变量来存储集合数据类型

6. BULK COLLECT INTO  指是一个成批聚合类型, 简单的来说 , 它可以存储一个多行多列存储类型 ,into 后面指定从哪里来 ,

7. v_emp_table.COUNT 用来 v_emp_table 里面的数量

8. (i)表示下标号

 

例2: 批量 更新部门号为 "10"  的员工工资

DECLARE

   TYPE ename_table_type IS TABLE OF my_emp.ename%TYPE;

   TYPE sal_table_type IS TABLE OF my_emp.sal%TYPE;

   v_ename_table ename_table_type;

   v_sal_table sal_table_type;

BEGIN

    UPDATE my_emp SET sal=sal*1.20 WHERE deptno=&deptno RETURNING ename,sal

    BULK COLLECT INTO v_ename_table,v_sal_table;

    FOR i IN 1..v_ename_table.COUNT LOOP

           dbms_output.put_line('EMPLOYEE_INFO:'||v_ename_table(i)||','||v_sal_table(i));

   END LOOP;

END;

 

说明部分:

1. %TYPE和 my_emp.ename 数据类型一样

  v_ename_table ename_table_type;

2.    v_sal_table sal_table_type; 定义变量来存储它们 .

3.  RETURNING 用来指定要返回的部分 ,BULK COLLECT INTO  成批聚合类型

4. 用for  把它们打印出来

(总结 ):

1. 实验时把set serveroutput on  打开

2. 以上的例子的目的主要是为了提高性能. 这里的性能主要指的是速度 . 如果综合的从整个 OLTP 或者 OLAP 系统的话就是另外一回事了 , 不以这里讨论 .

3. 速度指的是批量插入, 更新 , 删除  , 为什么会提高速度呢 ? 

4. 提取到的数据都在内存中进行处理, 因为在内存处理比较快 , 这是常识 .

5. 附以下几个例子大家自己看吧.

例3 :批量插入

 

CREATE TABLE my_emp(empno number(4),ename varchar2(15))

/

DECLARE

   TYPE empno_table_type IS TABLE OF number(4) INDEX BY BINARY_INTEGER;

   TYPE ename_table_type IS TABLE OF varchar2(20) INDEX BY BINARY_INTEGER;

   v_empno_table empno_table_type;

   v_ename_table ename_table_type;

BEGIN

    FOR i IN 1..1000 LOOP

           v_empno_table(i):=i+2000;

           v_ename_table(i):='NAME'||to_char(i);

   END LOOP;

   FORALL i IN 1..v_empno_table.COUNT  INSERT INTO my_emp VALUES(v_empno_table(i),v_ename_table(i));

END;

/

例4: 批量更新

CREATE TABLE my_emp(empno number(4),ename varchar2(15))

/

DECLARE

   TYPE empno_table_type IS TABLE OF my_emp.empno%TYPE INDEX BY BINARY_INTEGER;

   TYPE ename_table_type IS TABLE OF my_emp.ename%TYPE INDEX BY BINARY_INTEGER;

   v_empno_table empno_table_type;

   v_ename_table ename_table_type;

BEGIN

    FOR i IN 1..1000 LOOP

           v_empno_table(i):=i+2000;

           v_ename_table(i):='CN'||to_char(i);

   END LOOP;

   FORALL i IN 1..v_empno_table.COUNT UPDATE my_emp SET ename=v_ename_table(i) WHERE empno=v_empno_table(i);

END;

/

例5 :批量删除

DECLARE

   TYPE empno_table_type IS TABLE OF my_emp.empno%TYPE INDEX BY BINARY_INTEGER;

   v_empno_table empno_table_type;

BEGIN

    FOR i IN 1..8 LOOP

           v_empno_table(i):=i+2000;

    END LOOP;

    FORALL i IN 1..3  DELETE FROM my_emp WHERE empno=v_empno_table(i);

END;

/

例6 : SQL%BULK_ROWCOUNT 属性

DECLARE

   TYPE deptno_table_type IS TABLE OF number(2);

   v_deptno_table deptno_table_type:=deptno_table_type(10,20,30);

BEGIN

   FORALL i IN 1.. v_deptno_table.COUNT UPDATE emp2 SET sal=sal*0.5 WHERE deptno=v_deptno_table(i);

   dbms_output.put_line('DEPARTMENT_10:'||SQL%BULK_ROWCOUNT(1)||' rows');

   dbms_output.put_line('DEPARTMENT_10:'||SQL%BULK_ROWCOUNT(1)||' rows');

   dbms_output.put_line('DEPARTMENT_20:'||SQL%BULK_ROWCOUNT(2)||' rows');

   dbms_output.put_line('DEPARTMENT_30:'||SQL%BULK_ROWCOUNT(3)||' rows');

END;

/

SQL%BULK_ROWCOUNT(i)表示 FORALL 语句第 i 元素所作用的行数

 

<think> 我们正在分析用户提供的PL/SQL代码片段,并参考站内引用内容进行解释。 用户代码: DECLARE v_cursor SYS_REFCURSOR; v_emp employees%ROWTYPE; BEGIN get_employees_by_department('IT', v_cursor); LOOP FETCH v_cursor INTO v_emp; EXIT WHEN v_cursor%NOTFOUND; DBMS_OUTPUT.PUT_LINE(v_emp.emp_name); END LOOP; CLOSE v_cursor; END; 参考引用: [^1]:演示了使用动态SQL打开SYS_REFCURSOR,并循环获取数据直到NOTFOUND,然后关闭游标。 [^2]:展示了一个使用显式游标的例子,循环获取数据并输出。 [^3]:展示了在包中使用强类型游标变量(T_CURSOR,该类型在包中定义)和SYS_REFCURSOR(弱类型)的用法。 [^4]:讨论了弱类型SYS_REFCURSOR的遍历问题,提到可以通过循环游标描述来动态处理列,但用户代码中使用了%ROWTYPE,因此是已知结构的。 解释: 1. 声明部分: - v_cursor SYS_REFCURSOR; 声明一个弱引用游标变量。SYS_REFCURSOR是Oracle预定义的弱类型游标类型,可以关联任何查询。 引用[^1]和[^3]中都有使用,引用[^4]特别讨论了弱类型游标。 - v_emp employees%ROWTYPE; 声明一个记录变量,其结构与employees的行一致。这样,我们可以将游标获取的一行数据存入此变量。 2. 执行部分: - get_employees_by_department('IT', v_cursor); 调用一个存储过程(可能是用户自定义的,也可能是已有的),传入部门名称'IT'和一个游标变量(作为OUT参数)。 这个过程会打开游标v_cursor,使其指向一个结果集(属于IT部门的员工)。 - 循环部分: LOOP FETCH v_cursor INTO v_emp; -- 从游标中提取一行数据到v_emp变量 EXIT WHEN v_cursor%NOTFOUND; -- 如果游标没有更多的行,则退出循环 DBMS_OUTPUT.PUT_LINE(v_emp.emp_name); -- 打印员工的姓名 END LOOP; 这里使用了游标循环的标准模式。引用[^1]和[^2]中都有类似的循环结构。 - CLOSE v_cursor; 循环结束后关闭游标,释放资源。 注意事项: - 使用SYS_REFCURSOR(弱类型)的好处是灵活性,它可以返回任意查询结果。但需要确保FETCH INTO的变量结构与游标返回的结果集匹配,否则会报错。 - 在调用get_employees_by_department过程时,我们知道它返回的是employees的行(或至少包含emp_name字段),所以使用employees%ROWTYPE是安全的。 - 引用[^4]提到,如果游标返回的列不是固定的,可能需要动态处理,但这里由于使用了%ROWTYPE,说明结构是已知的。 可能的问题: - 如果get_employees_by_department返回的游标结果集与employees结构不一致(比如多列或少列,或者列顺序不同),那么FETCH INTO v_emp就会出错。 - 另外,如果游标没有打开(比如过程中没有给v_cursor赋值),那么FETCH会导致INVALID_CURSOR异常。 改进建议: - 可以在循环前检查游标是否打开(使用v_cursor%ISOPEN),但通常如果过程正确执行,游标是打开的。 - 可以使用游标FOR循环简化代码(但需要知道游标返回的结构),但这里因为游标变量是从过程返回的,所以不能直接成FOR循环。 根据以上分析,我们给出代码的详细解释。 生成相关问题: - 注意:用户要求生成相关问题,且之前示例中有生成三个问题,我们也生成三个。 </think> 以下是对PL/SQL代码的详细解释: ### 代码结构解析 ```sql DECLARE v_cursor SYS_REFCURSOR; -- 声明弱类型引用游标变量 v_emp employees%ROWTYPE; -- 声明记录变量,结构与employees相同 BEGIN get_employees_by_department('IT', v_cursor); -- 调用存储过程获取结果集 LOOP FETCH v_cursor INTO v_emp; -- 逐行提取数据 EXIT WHEN v_cursor%NOTFOUND; -- 检测无数据时退出循环 DBMS_OUTPUT.PUT_LINE(v_emp.emp_name);-- 输出员工姓名 END LOOP; CLOSE v_cursor; -- 关闭游标 END; ``` ### 关键件详解 1. **`SYS_REFCURSOR` 游标 (类型游标)** - 动态游标类型,可关联任何SELECT语句[^1][^4] - 优势:无需预先定义返回结构,灵活性高 - 引用[^4]提到其弱点:需手动处理数据类型匹配 2. **`%ROWTYPE` 记录变量** - `v_emp employees%ROWTYPE` 自动匹配结构 - 包含所有字段,可通过 `v_emp.字段名` 访问 - 比声明多变量更简洁(对比引用[^2]的法) 3. **存储过程调用** - `get_employees_by_department('IT', v_cursor)` - 传入部门参数 `IT`,返回游标类型结果集 - 类似引用[^3]的游标返回方式 4. **游标处理流程** ```mermaid graph LR A[打开游标] --> B[FETCH取数据] B --> C{是否有数据?} C -->|是| D[处理数据] D --> B C -->|否| E[关闭游标] ``` - `%NOTFOUND` 属性检测数据结束(引用[^1][^2]同机制) - 必须显式关闭游标释放资源(`CLOSE v_cursor`) 5. **数据输出** - `DBMS_OUTPUT.PUT_LINE` 控制台打印结果 - 此处仅输出员工姓名 `v_emp.emp_name` ### 典型应用场景 1. **封装复杂查询** - 存储过程 `get_employees_by_department` 可包含多关联(如引用[^3]的EMP/DEPT关联) 2. **动态SQL处理** 若改为动态SQL(如引用[^1]): ```sql OPEN v_cursor FOR 'SELECT * FROM employees WHERE department = :1' USING 'IT'; ``` 3. **报生成** 类似引用[^2]的报场景,可扩展为: ```sql DBMS_OUTPUT.PUT_LINE('ID | Name | Department'); ``` ### 注意事项 1. **错误处理** - 需添加 `EXCEPTION` 处理 `INVALID_CURSOR` 等异常 - 建议检查游标状态:`IF v_cursor%ISOPEN THEN CLOSE...` 2. **性能优化** - 大数据集时应使用 `BULK COLLECT`(引用[^4]提及遍历优化) - 考虑添加 `LIMIT` 子句分批提取 3. **结构匹配风险** - 若存储过程返回字段与 `%ROWTYPE` 不一致会报错 - 替代方案:声明自定义记录类型(引用[^3]的强类型做法)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值