ORACLE隐式游标获取记录数

本文介绍了如何在PL/SQL中使用ORACLE的隐式游标来获取删除语句所影响的记录数。通过利用隐式游标的SQL%ROWCOUNT属性,可以轻松地统计出DML操作(如DELETE)影响的行数。

ORACLE隐式游标获取记录数

怎样统计PLSQL语言中删除语句执行之后一共删除了多少条纪录。
Oracle
使用两种光标:显式光标和隐式光标。不管语句返回多少条纪录,PL/SQL为使用的每一条UPDATEDELETEINSERTSQL命令隐式的声明一个光标。(要管理SQL语句的处理,必须隐式的给它定义一个光标。)
PL/SQL中,当执行DML语句的时候,会自动打开一个隐式游标(游标就类似JDBC中的ResultSet),而相关要被操作的数据都是会先被放入隐式游标。隐式游标由ORACLE帮我们自动管理,如果想要手动进行一些操作的话,我们可以使用隐式游标的相关属性。如:
SQL%FOUND
成功获取记录返回true,否则返回false
SQL%NOTFOUND
成功获取记录返回true,否则返回
false
SQL%ROWCOUNT
返回从游标中获取的记录条数

SQL%ISOPEN
总是返回false
利用隐式游标属性SQL%ROWCOUNT可以达到计算删除了多少条纪录。

可以获取的DML sql包括:INSERT /UPDATE/DELETE /MERGER

具体使用看下面的例子把

create or replace procedure test_02 is
CNT INT;
begin
DELETE FROM TEST_01 ;
cnt:=sql%rowcount;

commit;
end;

create or replace procedure test_03 is
CNT INT;
n_1 int;
n_2 int;
n_3 int;
begin

merge into test_04
using (select * from test_09 )
merger_subquery
on(test_04.object_id = merger_subquery.object_id)

when not matched then
insert (object_id,OBJECT_NAME)
values (merger_subquery.object_id,merger_subquery.object_name)
when matched then
update set object_name = merger_subquery.object_name;

cnt:=sql%rowcount;
n_1:=cnt;

commit;
end;

<think>在之前的对话中,用户询问了Oracle游标循环进行更新和删除操作的案例。我需要基于提供的引用材料来回答这个问题。 引用[1]提到:游标没有显声明,直接使用SQL名称作为游标名,并可以使用%NOTFOUND、%FOUND、%ROWCOUNT、%ISOPEN属性,这些属性反映的是DML语句影响的行数。 引用[2]展示了游标在FOR循环中的使用案例,它是一个查询的例子(select * from t1),并没有更新或删除操作。 用户需要的是更新和删除操作的案例。因此,我需要构造一个使用游标进行更新和删除的循环案例。 在Oracle中,游标通常与SELECT INTO或DML语句(INSERT、UPDATE、DELETE)一起使用,而在FOR循环中,我们通常使用显游标或使用SELECT语句直接循环。 但是,注意:引用[2]中的例子是使用SELECT语句在FOR循环中,这实际上是一个游标循环(因为它没有声明游标,而是直接将SELECT语句放入循环中)。 然而,在FOR循环中使用SELECT语句进行循环时,我们只能查询,不能直接在该循环内进行更新或删除操作(因为循环变量是只读的)。所以,我们需要另一种方:使用显游标或者使用游标FOR循环(这里显游标更合适)?不对,用户要求游标。 但是,我们通常说的游标循环,也可以指在FOR循环中直接使用SELECT语句(如引用[2]所示),但是这种循环中,我们可以在循环体内根据查询结果执行更新或删除吗? 答案是:可以,但要注意,在循环体内执行更新或删除时,我们通常使用当前行的某些列值作为条件。 因此,我可以提供一个案例:在游标FOR循环(游标)中,根据查询结果更新或删除其他表中的数据。注意:不能在同一个表上更新或删除当前正在查询的行?这取决于锁定机制和操作是否允许,但通常可以,但要小心避免死锁或数据不一致。 所以,我将提供一个案例:使用游标(FOR循环中的SELECT)查询一个表,然后根据查询结果更新另一个表(或同一个表,但避免冲突)。为了简单,我选择更新同一个表的不同行或者另一个表。 案例1:更新操作 假设我们有一个员工表emp,我们想给某些员工(比如部门号为10的员工)加薪10%,我们可以这样做: ```sql BEGIN FOR emp_rec IN (SELECT empno, sal FROM emp WHERE deptno = 10) LOOP -- 在循环体内,使用emp_rec中的值进行更新 UPDATE emp SET sal = emp_rec.sal * 1.1 WHERE empno = emp_rec.empno; END LOOP; END; ``` 案例2:删除操作 假设我们有一个订单表orders,我们想删除所有状态为'CANCELLED'的订单,我们可以这样做: ```sql BEGIN FOR order_rec IN (SELECT order_id FROM orders WHERE status = 'CANCELLED') LOOP DELETE FROM order_details WHERE order_id = order_rec.order_id; -- 先删除明细 DELETE FROM orders WHERE order_id = order_rec.order_id; -- 再删除主表 END LOOP; END; ``` 但是,注意:在循环中逐行删除可能效率不高,实际上我们可以用一条SQL语句完成。这里只是为了演示游标循环的用法。 另外,引用[1]提到游标的属性,我们也可以在DML语句后使用SQL%属性。例如,在循环体内,更新后可以检查SQL%ROWCOUNT。 然而,在FOR循环中,我们遍历的是查询结果,而循环体内的DML语句会开启另一个游标(每次执行DML都会有一个游标),所以我们可以用SQL%来检查最近一次DML的影响行数。 例如: ```sql BEGIN FOR emp_rec IN (SELECT empno, sal FROM emp WHERE deptno = 10) LOOP UPDATE emp SET sal = emp_rec.sal * 1.1 WHERE empno = emp_rec.empno; IF SQL%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE('No rows updated for employee ' || emp_rec.empno); END IF; END LOOP; END; ``` 注意:在循环体内,UPDATE语句执行后,SQL%属性反映的是该UPDATE语句的影响行数(应该是1条,因为按主键更新),如果找不到记录则为0。 但是,用户要求的是游标循环进行更新和删除操作。上面的例子中,循环本身是游标(SELECT语句),循环体内执行了更新或删除操作(也是游标)。 因此,这些例子符合要求。 同时,我需要提醒用户:在可能的情况下,使用一条SQL语句进行批量更新或删除通常更高效。循环操作在数据量大时性能较差。 最后,生成相关问题: 1. 在Oracle中,游标和显游标的主要区别是什么? 2. 如何优化使用游标循环进行批量更新或删除操作? 3. 在游标循环中更新或删除数据时,需要注意哪些事务控制问题? 根据以上思路,组织回答。 </think>### Oracle游标循环更新和删除操作案例 在Oracle中,游标循环可高效实现批量数据操作。以下是完整案例,包含更新和删除操作: #### 1. **更新操作案例** ```sql DECLARE CURSOR cur_emp IS SELECT employee_id, salary FROM employees WHERE department_id = 80; BEGIN FOR emp_rec IN cur_emp -- 游标循环 LOOP UPDATE employees SET salary = emp_rec.salary * 1.1 -- 部门80员工加薪10% WHERE CURRENT OF cur_emp; -- 关键:锁定当前行 DBMS_OUTPUT.PUT_LINE('已更新员工ID:' || emp_rec.employee_id); END LOOP; END; ``` **特性说明**: - `WHERE CURRENT OF` 子句直接定位游标当前行[^1] - 打开/关闭游标,无需显`OPEN/CLOSE` - 使用`%ROWCOUNT`可获取影响行数:`DBMS_OUTPUT.PUT_LINE('更新行数:' || SQL%ROWCOUNT);` #### 2. **删除操作案例** ```sql BEGIN FOR dept_rec IN ( -- 直接嵌入SELECT实现游标 SELECT department_id FROM departments WHERE create_date < ADD_MONTHS(SYSDATE, -60) -- 创建超过5年的部门 ) LOOP DELETE FROM departments WHERE department_id = dept_rec.department_id; DBMS_OUTPUT.PUT_LINE('已删除部门ID:' || dept_rec.department_id); END LOOP; DBMS_OUTPUT.PUT_LINE('计删除:' || SQL%ROWCOUNT || '条记录'); -- 使用游标属性 END; ``` #### 关键注意事项: 1. **事务控制**:循环结束后需显`COMMIT`提交变更 2. **异常处理**:建议添加`EXCEPTION`块捕获`NO_DATA_FOUND`或`TOO_MANY_ROWS` 3. **性能优化**:批量操作时优先考虑`BULK COLLECT`+`FORALL`组合[^2] 4. **游标属性**: - `SQL%FOUND`:检测DML是否影响数据 - `SQL%ISOPEN`:游标始终返回`FALSE`(自动关闭) > **典型应用场景**:工资批量调整、历史数据归档、跨表数据同步等需要逐行处理的场景。注意避免在循环中执行高频`COMMIT`以防锁表。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值