遊標-----內存中的一塊區域,存放的是select的結果
遊標用來處理從數據庫中檢索的多行記錄(使用SELECT語句)。利用遊標,程序能夠逐個地處理和遍歷一次檢索返回的整個記錄集。
爲了處理SQL語句,Oracle將在內存中分配一個區域,這就是上下文區。這個區包含了已經處理完的行數、指向被分析語句的指針,遊標就是指向上下文區句柄或指針。
缺點:遊標使用時會對行加鎖,可能會影響其餘業務的正常進行。
並且,數據量大時其效率也較低。由於遊標實際上是至關於把磁盤數據總體放入了內存中,若是遊標數據量大會形成內存不足,也有可能致使cpu高,因此,在數據量小時才使用遊標。
優勢:至關於程序中的FOR循環處理。一條一條的處理你讀取的記錄內容。
數據存放在內存中,數據量少的時候速度比較快。
1、顯示遊標
顯示遊標被用於處理返回多行數據的SELECT 語句,遊標名經過CURSOR….IS 語句顯示地賦給SELECT 語句。
在PL/SQL中處理顯示遊標所必需的四個步驟:
1)聲明遊標;CURSOR cursor_name IS select_statement
2)爲查詢打開遊標;OPEN cursor_name
3)取得結果放入PL/SQL變量中;
FETCH cursor_name INTO list_of_variables;
FETCH cursor_name INTO PL/SQL_record;
4)關閉遊標。CLOSE cursor_name
注意:在聲明遊標時,select_statement不能包含INTO子句。當使用顯示遊標時,INTO子句是FETCH語句的一部分。
2、隱式遊標
全部的隱式遊標都被假設爲只返回一條記錄。
使用隱式遊標時,用戶無需進行聲明、打開及關閉。PL/SQL隱含地打開、處理,而後關掉遊標。
例如:
SELECT studentNo,studentName INTO curStudentNo,curStudentName
FROM StudentRecord WHERE name=’gg’;
上述遊標自動打開,並把相關值賦給對應變量,而後關閉。執行完後,PL/SQL變量curStudentNo,curStudentName中已經有了值。
單條sql語句所產生的結果集合
用關鍵字SQL表示隱式遊標
4個屬性 %rowcount 影響的記錄的行數 整數
%found 影響到了記錄 true
%notfound 沒有影響到記錄 true
%isopen 是否打開 布爾值 永遠是false
多條sql語句 隱式遊標SQL永遠指的是最後一條sql語句的結果
主要使用在update 和 delete語句上
3、常見遊標實例
(1)for循環遊標
SQL> set timing on
SQL> set serverout on
SQL> set pagesize 1000
SQL> set linesize 400
SQL> declare
cursor c1 is select id,ins_time,random_string from mytest where random_id=50; ---定義遊標
c1cur c1%rowtype; ---定義一個遊標變量
begin
for c1cur in c1 loop ---for循環
dbms_output.put_line(c1cur.id||':'||c1cur.ins_time||'-'||c1cur.random_string);
end loop;
end;
/
113:2015-12-23 16:39:41-1TP6AU8U5JJ1O16FIUSZ
176:2015-12-23 16:40:44-N77Y47YGZQJH2ONG504R
187:2015-12-23 16:40:55-PIQG1HNPM6EVFZXD8N7G
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.02
(2) fetch遊標 --使用的時候 必需要明確的打開和關閉
declare
cursor c1 is select id,ins_time,random_id,random_string from mytest where random_id=50; ---定義顯示遊標
c1cur c1%rowtype; ---定義一個遊標變量
begin
open c1; --打開遊標
loop
fetch c1 into c1cur; ---取一行數據到遊標變量
exit when c1%notfound; ---判斷是否取到了值
dbms_output.put_line(c1cur.id||':'||c1cur.ins_time||'-'||c1cur.random_string);
end loop;
close c1; ---關閉遊標
end;
/
113:2015-12-23 16:39:41-1TP6AU8U5JJ1O16FIUSZ
176:2015-12-23 16:40:44-N77Y47YGZQJH2ONG504R
187:2015-12-23 16:40:55-PIQG1HNPM6EVFZXD8N7G
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.02
(3)參數遊標(如下都利用scott自帶的幾張表測試)
SQL> declare
cursor c1 is select deptno from dept;
cursor c2(pno number,pjob varchar2) is select * from emp where deptno=pno and job=pjob; --只能指定類型,不能指定長度
c1cur c1%rowtype;
c2cur c2%rowtype;
begin
for c1cur in c1 loop
for c2cur in c2(c1cur.deptno,'MANAGER')loop
dbms_output.put_line(c1cur.deptno||':'||c2cur.ename||'---'||c2cur.sal);
end loop;
end loop;
end;
/
10:CLARK---2450
20:JONES---2975
30:BLAKE---2850
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.02
(4)引用遊標/動態遊標
SQL>declare
type c1 is ref cursor; --定義一個類型(ref cursor)弱類型
type c2 is ref cursor return emp%rowtype; --定義一個強類型(返回的結果集有要求)
c0cur c1; ---定義一個弱類型的遊標變量
c1cur emp%rowtype;
c2cur dept%rowtype;
begin
dbms_output.put_line('all employees :');
open c0cur for select * from emp;
loop
fetch c0cur into c1cur;
exit when c0cur%notfound;
dbms_output.put_line(c1cur.ename);
end loop;
dbms_output.put_line('all departments:');
open c0cur for select * from dept;
loop
fetch c0cur into c2cur;
exit when c0cur%notfound;
dbms_output.put_line(c2cur.dname);
end loop;
close c0cur;
end;
/
all employees:
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER
all departments:
ACCOUNTING
RESEARCH
SALES
OPERATIONS
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.02
(5)while循環
SQL> set timing on
SQL> set serverout on
SQL> set pagesize 1000
SQL> set linesize 400
SQL> declare
cursor c1 is select loc from dept;
c1cur c1%rowtype;
begin
open c1;
fetch c1 into c1cur;
while c1%found loop
dbms_output.put_line('location: '||c1cur.loc);
fetch c1 into c1cur; ---與for循環不一樣,while循環須要將下一行的值賦給遊標變量,不然會進入死循環,並報錯
end loop;
close c1;
end;
/
location: NEW YORK
location: DALLAS
location: CHICAGO
location: BOSTON
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.10
死循環,報錯:
SQL> declare
cursor c1 is select loc from dept;
c1cur c1%rowtype;
begin
open c1;
fetch c1 into c1cur;
while c1%found loop
dbms_output.put_line('location: '||c1cur.loc);
end loop;
close c1;
end;
/
location: NEW YORK
location: NEW YORK
location: NEW YORK
location: NEW YORK
location: NEW YORK
declare
*
ERROR at line 1:
ORA-04030: out of process memory when trying to allocate 16328 bytes (koh-kghu sessi,pl/sql vc2)
執行時,單個進程就致使cpu 100%,所以遊標仍是要當心使用的!
(6)利用遊標來更新(update)
6.1 聲明更新顯示遊標:
Cursor 遊標名IS SELECT 語句 For Update [ Of 更新列列名];
6.2 使用顯示遊標當前記錄來更新:
Update 表名 SET 更新語句 Where Current Of 遊標名;
1.全部人普調
SQL> declare
cursor c1 is select * from emp for update of sal;
c1cur c1%rowtype;
saladd emp.sal%type;
salnew emp.sal%type;
begin
for c1cur in c1 loop
saladd:=c1cur.sal*0.2; --全部員工加薪20%
if saladd<300 then
salnew:=c1cur.sal+300; --加薪不足300的,按300加
dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew);
else
salnew:=c1cur.sal+saladd;
dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew);
end if;
update emp set sal=salnew where current of c1;
end loop;
end ;
/
SMITH:new salary 1300 ---原來工資1000
ALLEN:new salary 2304
WARD:new salary 1800
JONES:new salary 4284
MARTIN:new salary 1800
BLAKE:new salary 4104
CLARK:new salary 3528
SCOTT:new salary 4320
KING:new salary 7200
TURNER:new salary 2160
ADAMS:new salary 1620
JAMES:new salary 1450
FORD:new salary 4320
MILLER:new salary 1872
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.04
2.按照部門來調整
declare
cursor c1 is select * from emp for update of sal;
c1cur c1%rowtype;
salnew emp.sal%type;
begin
for c1cur in c1 loop
case ---必須包含全部的分類,不然會報錯ORA-06592: CASE not found while executing CASE statement
when c1cur.deptno=10
then salnew:=c1cur.sal*1.1;
dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew);
when c1cur.deptno=20
then salnew:=c1cur.sal*1.15;
dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew);
when c1cur.deptno=30
then salnew:=c1cur.sal*1.2;
dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew);
when c1cur.deptno=40
then salnew:=c1cur.sal*1.3;
dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew);
end case;
update emp set sal=salnew where current of c1;
end loop;
end;
/
SMITH:new salary 1495
ALLEN:new salary 2764.8
WARD:new salary 2160
JONES:new salary 4926.6
MARTIN:new salary 2160
BLAKE:new salary 4924.8
CLARK:new salary 3880.8
SCOTT:new salary 4968
KING:new salary 7920
TURNER:new salary 2592
ADAMS:new salary 1863
JAMES:new salary 1740
FORD:new salary 4968
MILLER:new salary 2059.2
LITING:new salary 7800
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.07
3.符合if條件的
declare
cursor c1 is select * from emp for update of sal;
c1cur c1%rowtype;
salnew emp.sal%type;
begin
for c1cur in c1 loop
if c1cur.deptno=20 then
salnew:=c1cur.sal*5-8000;
dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew);
else
salnew:=c1cur.sal;
end if;
update emp set sal=salnew where current of c1;
end loop;
end;
/
SMITH:new salary 1525
JONES:new salary 6633
SCOTT:new salary 6840
ADAMS:new salary 1315
FORD:new salary 6840
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.05
4.以平均值爲分界
SQL> select avg(sal) from emp; 平均工資3715
AVG(SAL)
----------
3715
SQL> declare
2 cursor c1 is select empno,ename,sal,deptno,avg(sal) over(partition by deptno) as depavg from emp for update of sal;
3 c1cur c1%rowtype;
4 salnew emp.sal%type;
5 begin
6 for c1cur in c1 loop
7 if c1cur.sal>c1cur.depavg then
8 salnew:=c1cur.sal+50; ---高於平均工資的獎金髮50
9 dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew);
10 else
11 salnew:=c1cur.sal+100; ---低於平均工資的獎金髮100
12 dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew);
13 end if;
14 update emp set sal=salnew where current of c1;
15 end loop;
16 end;
17 /
CLARK:new salary 3980
MILLER:new salary 2160
KING:new salary 7970
JONES:new salary 4976
SMITH:new salary 1595
SCOTT:new salary 5018
FORD:new salary 5071
ADAMS:new salary 1415
WARD:new salary 2260
TURNER:new salary 2692
ALLEN:new salary 2814
JAMES:new salary 1840
MARTIN:new salary 2260
BLAKE:new salary 4974
LITING:new salary 7900
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.47
(7)利用遊標來刪除(delete)
7.1 聲明刪除顯示遊標:
Cursor 遊標名IS SELECT 語句 For Delete;
7.2 使用顯示遊標當前記錄來更新或刪除:
Delete From 表名 Where Current Of 遊標名;
SQL> create table emp1 as select * from emp;
Table created.
Elapsed: 00:00:03.05
SQL> declare
cursor c1 is select ename,job from emp1 for update;
e_job emp1.job%type;
e_name emp1.ename%type;
begin
open c1;
loop
fetch c1 into e_name,e_job; --順序要跟上面的select順序對應
exit when c1%notfound;
if e_job='ANALYST' then
delete from emp1 where current of c1;
dbms_output.put_line(e_name||':'||'deleted');
end if;
end loop;
close c1;
end;
/
SCOTT:deleted
FORD:deleted
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.09
(8)利用遊標來插入
SQL> declare
2 cursor c1 is select * from emp1;
3 c1cur c1%rowtype;
4 begin
5 open c1;
6 loop
7 fetch c1 into c1cur;
8 exit when c1%notfound;
9 insert into emp values c1cur;
10 dbms_output.put_line(c1cur.ename||': inserted');
11 end loop;
12 close c1;
13 end;
14 /
TINA: inserted
BOBO: inserted
LIO: inserted
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.11
SQL> select * from emp; ---查看數據,插入成功
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 1595 20
7499 ALLEN SALESMAN 7698 20-FEB-81 2814 300 30
7521 WARD SALESMAN 7698 22-FEB-81 2260 500 30
7566 JONES MANAGER 7839 02-APR-81 4976 20
7654 MARTIN SALESMAN 7698 28-SEP-81 2260 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 4974 30
7782 CLARK MANAGER 7839 09-JUN-81 3980 10
7788 SCOTT ANALYST 7566 19-APR-87 5018 20
7839 KING PRESIDENT 17-NOV-81 7970 10
7844 TURNER SALESMAN 7698 08-SEP-81 2692 0 30
7876 ADAMS CLERK 7788 23-MAY-87 1415 20
7900 JAMES CLERK 7698 03-DEC-81 1840 30
7902 FORD ANALYST 7566 03-DEC-81 5071 20
7934 MILLER CLERK 7782 23-JAN-82 2160 10
7200 LITING CLERK 7788 03-DEC-81 7900 500 40
6000 TINA CLERK 7788 02-APR-86 4503 300 10
6221 BOBO CLERK 7788 02-APR-86 4000 200 10
6307 LIO CLERK 7788 02-APR-86 4409 0 10sql