您的位置:首页 > 数据库 > Oracle

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

2012-04-11 20:18 295 查看
游标: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 thenfetch mycursor into v_empno;elseopen 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)asbeginselect 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操作,可以和循环差不多。。看个例子:DECLARECURSOR 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;BEGINOPEN 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 THENDBMS_OUTPUT.PUT_LINE('--- Fetched 5th record ---');END IF;END LOOP;CLOSE c1;END;/
Referencing %FOUND, %NOTFOUND, or %ROWCOUNT before a cursor is opened orafter 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.游标其实也可以给参数的,这样你就可以在打开游标的时候传递参数值了,如果一个查询经常被用到,你还可以给参数一个默认值,看一个简单的例子declarecursor c1 (job varchar2,max_wage number) is --亮点在这,给c1两个参数。select * fron employees where job_id=job and salary>max_wage;--在这用到这俩参数了。beginfor person in c1('clerk',3000)--调用游标,赋值。loopdbms_output.put_line('name='||person.last_name||',salary='|| person.salary||',jobid='||person,job_id);end loopend;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 子句可以显著提高查询性能的:附上一个小例: 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 VariablesThis section includes several examples of cursor variable usage in PL/SQL. Foradditional cursor variable examples that use the programmatic interfaces, refer to thefollowing 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 GuideFetching DataThe 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 variableusing 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 RecordsThe power of cursor variables comes from their ability to point to different cursors. Inthe following package example, a discriminant is used to open a cursor variable topoint 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 aquery on the Emp_tab table or the Dept_tab table. The following PL/SQL blockshows how to fetch using the cursor variable, and then use the ROWTYPE_MISMATCHpredefined 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;
                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息