oracle中的游标 包 集合 的学习总结


游标:

   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;
/




oracle中的游标 包 集合 的学习总结_第1张图片


 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 AS

TYPE 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;








你可能感兴趣的:(oracle,服务器,Integer,dependencies,returning)