游标:
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的读取
: DECLARE
my_sal employees.salary%TYPE;
my_job employees.job_id%TYPE;
factor INTEGER := 2;
CURSOR c1 IS
SELECT factor*salary FROM employees WHERE job_id = my_job; --我一直不明白为什么会等于一个变量。。。
BEGIN
OPEN c1; -- factor initially equals 2
LOOP
FETCH 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的属性不必在写了,看个例子一目了然了:
DECLARE
CURSOR c1 IS SELECT last_name FROM employees WHERE ROWNUM < 11;
name employees.last_name%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH 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 Cursors
DECLARE
emp_job employees.job_id%TYPE := 'ST_CLERK';
emp_salary employees.salary%TYPE := 3000;
my_record employees%ROWTYPE;
CURSOR c1 (job VARCHAR2, max_wage NUMBER) IS
SELECT * 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);
LOOP
FETCH c1 INTO my_record;
EXIT WHEN c1%NOTFOUND;
-- process data record
DBMS_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/liu1990
create or replace package Sr_pkg is
pragma serially_reusable;
n number:=5;
end Sr_pkg;
再次连接
connect scott/liu1990
--第一次访问服务器
begin
Sr_pkg.n:=10;
end;
--第二次访问服务器
begin
dbms_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 子句可以显著提高查询性能的:附上一个小例:
declare
type 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;
begin
select empno,ename BULK COLLECT INTO empno, ename from emp where mgr=7698;
--下面是利用游标实现的
counter:=1;
for rec in c
loop
empno(counter) :=rec.empno;
ename(counter) :=rec.ename;
counter :=counter+1;
end loop;
end;
再来一个 用于更新各个员工bonus的例子:
declare
type 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;
begin
bonlist_inst :=bonlist(1,2,3,4,5);
--对应上面的赋值方法,注意下。
forall i in empids.first .. empids.last
update emp set bonus = 0.1 * sal where empno = empids(i)
returning sal BULK COLLECT INTO bonlist;
--下面这种传统的更新,没有用批量绑定,效率低下。
for i in empids.first .. empids.last loop
uodate 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 AS
TYPE 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 AS
PROCEDURE Open_emp_cv (Emp_cv IN OUT Emp_val_cv_type,
Dept_number IN INTEGER) IS
BEGIN
OPEN 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) IS
BEGIN
FETCH 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 variable
Emp_curs Emp_data.Emp_val_cv_type;
Dept_number Dept_tab.Deptno%TYPE;
Emp_row Emp_tab%ROWTYPE;
BEGIN
Dept_number := 20;
-- open the cursor using a variable
Emp_data.Open_emp_cv(Emp_curs, Dept_number);
-- fetch the data and display it
LOOP
Emp_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 AS
TYPE 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 AS
PROCEDURE Open_cv (Cv IN OUT cv_type,
Discrim IN POSITIVE) IS
BEGIN
IF Discrim = 1 THEN
OPEN Cv FOR SELECT * FROM Emp_tab WHERE Sal > 2000;
ELSIF Discrim = 2 THEN
OPEN 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:
DECLARE
Emp_rec Emp_tab%ROWTYPE;
Dept_rec Dept_tab%ROWTYPE;
Cv Emp_dept_data.CV_TYPE;
BEGIN
Emp_dept_data.open_cv(Cv, 1); -- Open Cv For Emp_tab Fetch
Fetch cv INTO Dept_rec; -- but fetch into Dept_tab record
-- which raises ROWTYPE_MISMATCH
DBMS_OUTPUT.PUT(Dept_rec.Deptno);
DBMS_OUTPUT.PUT_LINE(' ' || Dept_rec.Loc);
EXCEPTION
WHEN ROWTYPE_MISMATCH THEN
BEGIN
DBMS_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;