游标:
1、cursor_name%ROWCOUNT 表示游标影响了多少行,如:sql%rowcount中的sql是oracle的内部游标,rowcount的意思是之前的dml sql语句影响的多少行数据。如果是delete操作rowcount就是delete的数据,如果是insert,就是插入的数量......
2、游标的目标变量(v_empno, v_sal)必须与游标select 表中得列的数据类型(就是emp表中的数据类型咯,可以用%emp.empno或者 %emp.sal 这样永远错不了哈哈)一致。
3、如果试图打开一个已经打开的,或者关闭一个已经关闭的游标都会出现错误,如果不清楚目前游标的状态,那就用一个%isopen 进行检查。
如下: if mycursor%isopen then
fetch mycursor into v_empno;
else
open mycursor;
end if;
隐式游标:
每个隐式游标都有一个 into
数据类型一致
一次只能返回一行否则会报 NO_DATA_FOUND 和 TOO_MANY_ROWS 异常。。可以用 ROWNUM=1来约束一下。
来个小例子吧。不过真正应用的时候还是显示游标用得多:
create or replace procedure cx_xm(in_xh in char ,out_xm out varcharw)
as
begin
select xm into out_xm from sxb where xh=in_xh and rownum=1;
dbms_outputl.put_line(out_xm);
end cx_xm;
任何变量只有当游标打开的时候才能被赋值,下面例子中每一行检索都会乘以2,这个factor的增长不会影响fetch的读取
: DECLAREmy_sal employees.salary%TYPE;my_job employees.job_id%TYPE;factor INTEGER := 2;CURSOR c1 ISSELECT factor*salary FROM employees WHERE job_id = my_job; --我一直不明白为什么会等于一个变量。。。BEGINOPEN c1; -- factor initially equals 2LOOPFETCH c1 INTO my_sal;EXIT WHEN c1%NOTFOUND;factor := factor + 1; -- does not affect FETCH ;这里不影响一个读取,只有factor自己在加。END LOOP;CLOSe c1;END;
因为fetch读取的时候是自动向下移动一行的,所以我们可以用不同的fetch...into 句子分别对同一个cursor操作,可以和循环差不多。。看个例子:
DECLARE
CURSOR c1 IS SELECT last_name FROM employees ORDER BY last_name;
name1 employees.last_name%TYPE;
name2 employees.last_name%TYPE;
name3 employees.last_name%TYPE;
BEGIN
OPEN c1;
FETCH c1 INTO name1; -- this fetches first row注意这里取了第一行
FETCH c1 INTO name2; -- this fetches second row 取了第二行
FETCH c1 INTO name3; -- this fetches third row取了第三行
CLOSE c1;
END;
/
注意这个fetch操作取到最后一行后也不会提醒,不报错,所以要我们用cursor的属性%found 和 %notfound
关于%rowcount的属性不必在写了,看个例子一目了然了:
DECLARECURSOR c1 IS SELECT last_name FROM employees WHERE ROWNUM < 11;name employees.last_name%TYPE;BEGINOPEN c1;LOOPFETCH c1 INTO name;EXIT WHEN c1%NOTFOUND OR c1%NOTFOUND IS NULL;
DBMS_OUTPUT.PUT_LINE(c1%ROWCOUNT || '. ' || name);
IF c1%ROWCOUNT = 5 THEN
DBMS_OUTPUT.PUT_LINE('--- Fetched 5th record ---');
END IF;
END LOOP;
CLOSE c1;
END;
/
Referencing %FOUND, %NOTFOUND, or %ROWCOUNT before a cursor is opened or
after it is closed raises INVALID_CURSOR.
After the first FETCH, if the result set was empty, %FOUND yields FALSE,
%NOTFOUND yields TRUE, and %ROWCOUNT yields 0.
游标其实也可以给参数的,这样你就可以在打开游标的时候传递参数值了,如果一个查询经常被用到,你还可以给参数一个默认值,看一个简单的例子:
declare
cursor c1 (job varchar2,max_wage number) is --亮点在这,给c1两个参数。
select * fron employees where job_id=job and salary>max_wage;--在这用到这俩参数了。
begin
for person in c1('clerk',3000)--调用游标,赋值。
loop
dbms_output.put_line('name='||person.last_name||',salary='|| person.salary||',jobid='||person,job_id);
end loop
end;
Example 6–23 Passing Parameters to Explicit CursorsDECLAREemp_job employees.job_id%TYPE := 'ST_CLERK';emp_salary employees.salary%TYPE := 3000;my_record employees%ROWTYPE;CURSOR c1 (job VARCHAR2, max_wage NUMBER) ISSELECT * FROM employees WHERE job_id = job and salary > max_wage;BEGIN-- Any of the following statements opens the cursor:-- OPEN c1('ST_CLERK', 3000); OPEN c1('ST_CLERK', emp_salary);-- OPEN c1(emp_job, 3000); OPEN c1(emp_job, emp_salary);OPEN c1(emp_job, emp_salary);LOOPFETCH c1 INTO my_record;EXIT WHEN c1%NOTFOUND;-- process data recordDBMS_OUTPUT.PUT_LINE('Name = ' || my_record.last_name || ', salary = ' ||my_record.salary || ', Job Id = ' || my_record.job_id );END LOOP;END
游标变量:
注意几个游标变量的约束条件:
1、不能在包或者包体中声明游标变量,但是定义游标类型是可以的:
CREATE PACKAGE emp_data ASTYPE EmpCurTyp IS REF CURSOR RETURN employees%ROWTYPE; --定义游标类型是可以的。-- emp_cv EmpCurTyp; -- not allowed --这个是不允许的,
2、如果你在一个oci客户端绑定了一个游标变量到一个plsql块中,你不能从服务器端读取数据,除非你也在服务器端打开了它。
3、你不能用比较操作测试游标变量是否相等,是否无效。
4、数据库的列不能存储游标变量的值,在创建表的句法中没有等效的类型来用。
5、不能把游标变量存储在组合数组,内嵌表,可变数组中。
6、游标和游标变量是不能彼此协作的,你不能在一个游标的循环中参照一个游标变量。
包:
串行可再用的包: oracle实例需要一个可用的实例区间,他是在SGA的LRU(least-recently used)池中已经为这个包分配了,也配置好了的,在向服务器调用的结尾,这个区间会归还给LRU池,把LRU池放在SGA中的原因就是这个工作区可以被不同的用户同时使用,可以串行化调用同一个包。
由于不可串行化包存在于一个会话的整个生命过程中,这就在真个session过程中锁定了UGA内存。
句法:pragma serially_reusable;
下面看一个例子:
包变量 下面的例子用到了串行复用包声明;connect scott/liu1990create or replace package Sr_pkg ispragma serially_reusable;n number:=5;end Sr_pkg;再次连接connect scott/liu1990--第一次访问服务器beginSr_pkg.n:=10;end;--第二次访问服务器begindbms_output.put_line(Sr_pkg.n);end;输出结果 5为什么不是10呢?哈哈,这就是serially_reusable的作用了,如果没有这句,那结果才是10哦。
存储程序模块(存储过程,函数,包)
默认参数值:create or replace procedure get_emp_names(dept_num in numberdefault 20) is ....
或者:create or replace procedure get_emp_names(dept_num in number:=20) is ....
当你调用这个存储过程的时候,如果为其赋值,那默认值将被重写。在这里你不能用declare关键字来定义变量、游标、异常,一用这个就会出错的。
创建包 包体:
可以包体可以包含如下内容: 过程、函数、游标的声明,本机的过程或者函数,本机的变量。
例:
应用批量绑定:
declare
type numlis is varry(100) of number;
id numlist := numlist(7902,7698,7839);
begin
forall i in id.first .. id.last ---批量绑定可变数组的值,关键字 forall
update emp set sal = 1.2 * sal where mgr=id(i);
再看一个没有使用批量绑定的块:
for i in id.first .. id.last
loop
update emp set sal = 1.2*sal where mgr= id(i);
end loop;
end;
查询,用BULK COLLECT INTO 子句可以显著提高查询性能的:附上一个小例:
declaretype var_tab is table of varchar2(20) index by binary_integer;--创建一个varchar2类型的表,加上一个binnary_integer类型的索引empno var_tab;ename var_tab;counter number;cursor c is select empno , ename from emp where mgr=7698;beginselect empno,ename BULK COLLECT INTO empno, ename from emp where mgr=7698;--下面是利用游标实现的counter:=1;for rec in cloopempno(counter) :=rec.empno;ename(counter) :=rec.ename;counter :=counter+1;end loop;end;
再来一个 用于更新各个员工bonus的例子:
declaretype emplist is varray(100) of number;empids emplist :=emplist(7369,7499,7521,7654,7698);--与下面的赋值方法比较一下type bonliset is table of emp.sal%type;bonlist_inst bonlist;beginbonlist_inst :=bonlist(1,2,3,4,5);--对应上面的赋值方法,注意下。forall i in empids.first .. empids.lastupdate emp set bonus = 0.1 * sal where empno = empids(i)returning sal BULK COLLECT INTO bonlist;--下面这种传统的更新,没有用批量绑定,效率低下。for i in empids.first .. empids.last loopuodate emp set bonus = 0.1 * sal where empno = empids(i)returning sal INTO bonlist(i);end loop;end;
如果没有批量绑定,pl/sql把每个employee的更新发送 sql命令到sql 引擎,很多的上下文切换(context switches)会大大降低了更新效率。
signnatures 和 timestamps:两种远程依赖模式
remote_dependencies_mode参数有两个值
TIMESTAMP
与 SIGNATURE
当设置为TIMESTAMP时用于指定数据库为远程 PL/SQL 存储的过程处理被依赖对象的方式。如果设置为 TIMESTAMP, 只有在服务器与本地时间戳相匹配的情况下, 才能执行该过程。如果设置为 SIGNATURE,
在签名安全的情况下即可执行该过程。
游标变量的使用实例:
Examples of Cursor Variables
This section includes several examples of cursor variable usage in PL/SQL. For
additional cursor variable examples that use the programmatic interfaces, refer to the
following manuals:
■ Pro*C/C++ Programmer's Guide
■ Pro*COBOL Programmer's Guide
■ Oracle Call Interface Programmer's Guide
■ Oracle SQL*Module for Ada Programmer's Guide
Fetching Data
The following package defines a PL/SQL cursor variable type Emp_val_cv_type,
and two procedures. The first procedure, Open_emp_cv, opens the cursor variable
using a bind variable in the WHERE clause. The second procedure, Fetch_emp_data,
fetches rows from the Emp_tab table using the cursor variable.
CREATE OR REPLACE PACKAGE Emp_data ASTYPE Emp_val_cv_type IS REF CURSOR RETURN Emp_tab%ROWTYPE;PROCEDURE Open_emp_cv (Emp_cv IN OUT Emp_val_cv_type,Dept_number IN INTEGER);PROCEDURE Fetch_emp_data (emp_cv IN Emp_val_cv_type,emp_row OUT Emp_tab%ROWTYPE);END Emp_data;CREATE OR REPLACE PACKAGE BODY Emp_data ASPROCEDURE Open_emp_cv (Emp_cv IN OUT Emp_val_cv_type,Dept_number IN INTEGER) ISBEGINOPEN emp_cv FOR SELECT * FROM Emp_tab WHERE deptno = dept_number;END open_emp_cv;PROCEDURE Fetch_emp_data (Emp_cv IN Emp_val_cv_type,Emp_row OUT Emp_tab%ROWTYPE) ISBEGINFETCH Emp_cv INTO Emp_row;END Fetch_emp_data;END Emp_data;
The following example shows how to call the Emp_data package procedures from a PL/SQL block:
DECLARE-- declare a cursor variableEmp_curs Emp_data.Emp_val_cv_type;Dept_number Dept_tab.Deptno%TYPE;Emp_row Emp_tab%ROWTYPE;BEGINDept_number := 20;-- open the cursor using a variableEmp_data.Open_emp_cv(Emp_curs, Dept_number);-- fetch the data and display itLOOPEmp_data.Fetch_emp_data(Emp_curs, Emp_row);EXIT WHEN Emp_curs%NOTFOUND;DBMS_OUTPUT.PUT(Emp_row.Ename || ' ');DBMS_OUTPUT.PUT_LINE(Emp_row.Sal);END LOOP;
END;
Implementing Variant Records
The power of cursor variables comes from their ability to point to different cursors. In
the following package example, a discriminant is used to open a cursor variable to
point to one of two different cursors:
CREATE OR REPLACE PACKAGE Emp_dept_data ASTYPE Cv_type IS REF CURSOR;PROCEDURE Open_cv (Cv IN OUT cv_type,Discrim IN POSITIVE);END Emp_dept_data;CREATE OR REPLACE PACKAGE BODY Emp_dept_data ASPROCEDURE Open_cv (Cv IN OUT cv_type,Discrim IN POSITIVE) ISBEGINIF Discrim = 1 THENOPEN Cv FOR SELECT * FROM Emp_tab WHERE Sal > 2000;ELSIF Discrim = 2 THENOPEN Cv FOR SELECT * FROM Dept_tab;END IF;END Open_cv;END Emp_dept_data;
You can call the Open_cv procedure to open the cursor variable and point it to either a
query on the Emp_tab table or the Dept_tab table. The following PL/SQL block
shows how to fetch using the cursor variable, and then use the ROWTYPE_MISMATCH
predefined exception to handle either fetch:
DECLAREEmp_rec Emp_tab%ROWTYPE;Dept_rec Dept_tab%ROWTYPE;Cv Emp_dept_data.CV_TYPE;BEGINEmp_dept_data.open_cv(Cv, 1); -- Open Cv For Emp_tab FetchFetch cv INTO Dept_rec; -- but fetch into Dept_tab record-- which raises ROWTYPE_MISMATCHDBMS_OUTPUT.PUT(Dept_rec.Deptno);DBMS_OUTPUT.PUT_LINE(' ' || Dept_rec.Loc);EXCEPTIONWHEN ROWTYPE_MISMATCH THENBEGINDBMS_OUTPUT.PUT_LINE('Row type mismatch, fetching Emp_tab data...');FETCH Cv INTO Emp_rec;DBMS_OUTPUT.PUT(Emp_rec.Deptno);DBMS_OUTPUT.PUT_LINE(' ' || Emp_rec.Ename);END;