目录
一,游标
1,游标是什么
2,显式游标与隐式游标的区别
3,游标的四个属性
二,显式游标
1,显示游标的语法
2,游标的FOR循环
1)语法
3,例子
1)获取信息--查询所有TEXT_TABLE的信息。
2)游标属性的用法
3)简化游标,游标的FOR循环(简化下loop循环的游标操作)
三,隐式游标
1,隐式游标简介
2,例子
四,游标修改和删除
1,使用游标进行数据操作
2,NOWAIT -- 不等待锁
3,OF子句 -- 共享锁
4,参数游标
5,例子
1)按逻辑,分别修改参数--LOOP
2)按逻辑,分别修改参数--FOR
3)定义游标时利用FOR UPDATE 子句可以将游标提取出来的数据进行行级锁定
4)使用NOWAIT
5)使用OF字句在特定表上加共享锁(带有参数的游标)
是SQL的一个内存工作区,由系统或用户以变量的方式定义。临时存储从数据库中提取的数据块。是一个结果集。
显式游标:select返回的多行数据
隐式游标
显式游标是用户自定义的显式创建的游标,主要是用于对查询语句的处理。
隐式游标是由系统隐含创建的游标。主要用于对非查询语句,如修改,删除等操作,
则有oracle系统自动地为这些操作设置游标并创建其工作区,对于隐式游标的操作,
如定义,打开,取值及关闭操作,都有oralce系统自动完成,无需用户进行处理。
隐式游标的名字为SQL,这是由oracle系统定义的。
%FOUND :监测游标结果集是否存在数据,存在=true
%NOTFOUND :监测游标结果集是否不存在数据,不存在=true
%ISOPEN :监测游标是否打开,打开=true
%ROWCOUNT :返回已提取的实际行数。
--1定义游标
CURSOR CURSOR_NAME [(paramenter_name datatype)]
IS select_sql;
--2打开游标 游标结果集
OPEN CURSOR_NAME;
--3提取数据 每次只能提取一行
FETCH CURSOR_NAME INTO variable1[,variable2...];
PL/SQL变量
--4关闭游标
CLOSE CURSOR_NAME;
当时用游标FOR时,oracle会隐含的打开游标,提取数据并关闭游标。
当进入循环时,游标FOR循环语句自动打开游标,并提取第一行游标数据;
当程序处理完当前所提取的数据而进入下一次循环时,游标FOR循环语句自动提取下一行数据供程序处理;
当提取完结果集合中的所有数据行后结束循环,并自动关闭游标。
FOR record_name IN cursor_name LOOP
statement;
END LOOP;
效果
ID:1,NAME:1
ID:3,NAME:3
ID:4,NAME:4
ID:5,NAME:5
ID:2,NAME:2
ID:2,NAME:2
DECLARE
--定义游标
CURSOR emp_cursor IS SELECT ID,NAME from TEXT_TABLE ;
V_ID TEXT_TABLE.ID%TYPE;
V_NAME TEXT_TABLE.NAME%TYPE;
V_STR varchar2(50);
BEGIN
IF emp_cursor%ISOPEN THEN
V_STR := V_STR||'ISOPEN=true ';
ELSE
V_STR := V_STR||'ISOPEN=false ';
END IF;
DBMS_OUTPUT.PUT_LINE(V_STR);
--打开游标,执行查询
OPEN emp_cursor;
--提取数据
LOOP
FETCH emp_cursor INTO V_ID,V_NAME;
V_STR := ' ';
IF emp_cursor%FOUND THEN
V_STR := V_STR||'FOUND=true ';
ELSE
V_STR := V_STR||'FOUND=false ';
END IF;
IF emp_cursor%NOTFOUND THEN
V_STR := V_STR||'NOTFOUND=true ';
ELSE
V_STR := V_STR||'NOTFOUND=false ';
END IF;
IF emp_cursor%ISOPEN THEN
V_STR := V_STR||'ISOPEN=true ';
ELSE
V_STR := V_STR||'ISOPEN=false ';
END IF;
DBMS_OUTPUT.PUT_LINE('ID:'||V_ID||',NAME:'||V_NAME ||' ROWCOUNT:'|| emp_cursor%ROWCOUNT||V_STR);
--什么时候能够退出循环?
--%FOUND,%NOTFOUND 有记录返回true,没记录返回true
EXIT WHEN emp_cursor%NOTFOUND;
END LOOP;
--关闭游标
CLOSE emp_cursor;
IF emp_cursor%ISOPEN THEN
DBMS_OUTPUT.PUT_LINE('ISOPEN=true ');
ELSE
DBMS_OUTPUT.PUT_LINE('ISOPEN=false ');
END IF;
END;
效果
ISOPEN=false
ID:1,NAME:1 ROWCOUNT:1 FOUND=true NOTFOUND=false ISOPEN=true
ID:3,NAME:3 ROWCOUNT:2 FOUND=true NOTFOUND=false ISOPEN=true
ID:4,NAME:4 ROWCOUNT:3 FOUND=true NOTFOUND=false ISOPEN=true
ID:5,NAME:5 ROWCOUNT:4 FOUND=true NOTFOUND=false ISOPEN=true
ID:2,NAME:2 ROWCOUNT:5 FOUND=true NOTFOUND=false ISOPEN=true
ID:2,NAME:2 ROWCOUNT:5 FOUND=false NOTFOUND=true ISOPEN=true
ISOPEN=false
DECLARE
--定义游标
CURSOR emp_cursor IS SELECT ID,NAME from TEXT_TABLE ;
BEGIN
FOR emp IN emp_cursor LOOP
DBMS_OUTPUT.PUT_LINE('ID:'||emp.ID||',NAME:'||emp.NAME ||' ROWCOUNT:'|| emp_cursor%ROWCOUNT);
END LOOP;
END;
--定义游标FOR循环引用子查询
BEGIN
FOR emp IN (SELECT ID,NAME from TEXT_TABLE ) LOOP
DBMS_OUTPUT.PUT_LINE('ID:'||emp.ID||',NAME:'||emp.NAME ||' ROWCOUNT:');
END LOOP;
END;
效果
ID:1,NAME:1 ROWCOUNT:1
ID:3,NAME:3 ROWCOUNT:2
ID:4,NAME:4 ROWCOUNT:3
ID:5,NAME:5 ROWCOUNT:4
ID:2,NAME:2 ROWCOUNT:5
当系统使用一个隐式游标时,可以通过隐式游标的属性来了解操作的状态和结果,进而控制程序的流程。通过SQL游标名总是只能访问前一个DML操作或单行SELECT操作的游标属性。
DML操作和单行SELECT语句会使用隐式游标
插入操作:INSERT
更新操作:UODATE
删除操作:DELETE
单行查询操作:SELECT ... INTO
--隐式游标
BEGIN
UPDATE TEXT_TABLE SET NAME='11' WHERE ID = '1';
IF SQL%FOUND THEN
DBMS_OUTPUT.put_line('修改成功');
COMMIT;
ELSE
DBMS_OUTPUT.put_line('修改失败!');
ROLLBACK;
END IF;
END;
SELECT * FROM TEXT_TABLE;
对了对正在处理(查询)的行不被另外的用户改动,oracle提供一个FOR UPDATE 字句来对所选择的行进行锁住。
(针对不同操作所表现出来的效果也不一样)
删除未提交 查询会卡住(死锁)
修改未提交 查询会卡住(死锁)
新增未提交 新插入的数据无法查询出来
用于指定不等待锁,如果发现所操作的数据行已经锁定,将不会等待,立即返回。可以避免死锁。
使用OF子句在特定表上加共享锁,当游标子查询涉及到多张表时,如果在特定表上加行共享锁,那么需要使用OF子句。
是指带有参数的游标,通过使用参数游标,使用不同的参数值可以生成不同的游标结果集。
语法
CURSOR cursor_name(parameter_name datatype) IS
select_sql;
OPEN cursor_name(pararmeter_value);
DECLARE
--定义游标
CURSOR emp_cursor IS SELECT ID,NAME from TEXT_TABLE ;
V_ID TEXT_TABLE.ID%TYPE;
V_NAME TEXT_TABLE.NAME%TYPE;
BEGIN
--打开游标,执行查询
OPEN emp_cursor;
--提取数据
LOOP
FETCH emp_cursor INTO V_ID,V_NAME;
IF V_ID<3 THEN
UPDATE TEXT_TABLE SET NAME = NAME||'1' WHERE ID = V_ID;
ELSE
UPDATE TEXT_TABLE SET NAME = NAME||'3' WHERE ID = V_ID;
END IF;
EXIT WHEN emp_cursor%NOTFOUND;
END LOOP;
COMMIT;
--关闭游标
CLOSE emp_cursor;
END;
-- 用游标FOR循环的方式实现
DECLARE
CURSOR emp_cursor IS SELECT ID,NAME from TEXT_TABLE ;
BEGIN
FOR EMP IN emp_cursor LOOP
IF EMP.ID<3 THEN
UPDATE TEXT_TABLE SET NAME = NAME||'1' WHERE ID = EMP.ID;
ELSE
UPDATE TEXT_TABLE SET NAME = NAME||'3' WHERE ID = EMP.ID;
END IF;
END LOOP;
COMMIT;
END;
DECLARE
CURSOR emp_cursor IS SELECT ID,NAME from TEXT_TABLE FOR UPDATE;
BEGIN
FOR EMP IN emp_cursor LOOP
IF EMP.ID<3 THEN
UPDATE TEXT_TABLE SET NAME = NAME||'1' WHERE ID = EMP.ID;
ELSE
UPDATE TEXT_TABLE SET NAME = NAME||'3' WHERE ID = EMP.ID;
END IF;
END LOOP;
COMMIT;
END;
在jin用户下执行修改操作
update TEXT_TABLE set name='1' where id ='1';
在sys用户执行NOWAIT操作
--FOR UPDATE NOWAIT 不等待锁,如发现所操作的数据行已经锁定,将不会等待,立即返回
DECLARE
--定义游标
CURSOR emp_cursor IS SELECT ID,NAME FROM TEXT_TABLE FOR UPDATE NOWAIT;
BEGIN
FOR emp IN emp_cursor LOOP
DBMS_OUTPUT.put_line(emp.ID||'----'||emp.NAME);
IF EMP.ID<3 THEN
UPDATE TEXT_TABLE SET NAME = NAME||'1' WHERE ID = EMP.ID;
ELSE
UPDATE TEXT_TABLE SET NAME = NAME||'3' WHERE ID = EMP.ID;
END IF;
END LOOP;
COMMIT;
END;
效果
--使用OF子句在特定表上加行共享锁
DECLARE
CURSOR emp_cursor2 IS
SELECT TT.ID,TT.NAME,TTL.OPERATE_TYPE,TTL.OPERATE_DATE
FROM TEXT_TABLE TT join TEXT_TABLE_LOG TTL on TT.ID = TTL.OPERATE_ID
WHERE TT.ID = &ID
FOR UPDATE OF TT.ID;
BEGIN
FOR emp2 IN emp_cursor2 LOOP
DBMS_OUTPUT.PUT_LINE('ID:'||emp2.ID||' 操作:'||emp2.OPERATE_TYPE||' 操作时间:'||emp2.OPERATE_DATE);
DELETE FROM TEXT_TABLE WHERE CURRENT OF emp_cursor2;
END LOOP;
COMMIT;
END;
效果
在sys用户下执行修改操作
update JIN.TEXT_TABLE set name='1' where id ='1';
在jin用户下执行游标操作
在sys用户下执行提交操作,jin用户下锁定的操作随之执行