点击(此处)折叠或打开

  1. oracle 游标

  2. 游标(Cursor):用来查询数据库,获取记录集合(结果集)的指针,可以让开发者一次访问一行结果集,在每条结果集上作操作。

  3.  

  4. 游标可分为:

  5. l 静态游标:分为显式(explicit)游标和隐式(implicit)游标。

  6. l REF游标:是一种引用类型,类似于指针。

  7.  

  8. 1、静态游标

  9. 1.1显式游标

  10. 定义格式:

  11. CURSOR 游标名 ( 参数 ) [返回值类型] IS

  12. Select 语句

  13.  

  14. 例子

  15. set serveroutput on

  16. declare

  17. cursor emp_cur ( p_deptid in number) is

  18. select * from employees where department_id = p_deptid;

  19.  

  20. l_emp employees%rowtype;

  21. begin

  22.  dbms_output.put_line('Getting employees from department 30');

  23. open emp_cur(30);

  24.  loop

  25.  fetch emp_cur into l_emp;

  26.  exit when emp_cur%notfound;

  27.  dbms_output.put_line('Employee id '|| l_emp.employee_id || ' is ');

  28.  dbms_output.put_line(l_emp.first_name || ' ' || l_emp.last_name);

  29.  end loop;

  30.  close emp_cur;

  31.  

  32.  dbms_output.put_line('Getting employees from department 90');

  33. open emp_cur(90);

  34.  loop

  35.  fetch emp_cur into l_emp;

  36.  exit when emp_cur%notfound;

  37.  dbms_output.put_line('Employee id '|| l_emp.employee_id || ' is ');

  38.  dbms_output.put_line(l_emp.first_name || ' ' || l_emp.last_name);

  39.  end loop;

  40.  close emp_cur;

  41. end;

  42. /

  43.  

  44. 1.2隐式游标

  45. 不用明确建立游标变量,分两种:

  46. 1.在PL/SQL中使用DML语言,使用ORACLE提供的名为“SQL”的隐示游标。

  47. 举例:

  48. declare

  49. begin

  50.  update departments set department_name=department_name;

  51.  --where 1=2;

  52.  dbms_output.put_line('update '|| sql%rowcount ||' records');

  53. end;

  54. /

  55. 2.CURSOR FOR LOOP,用于for loop 语句

  56. 举例:

  57. declare

  58. begin

  59.  for my_dept_rec in ( select department_name, department_id from departments)

  60.  loop

  61.  dbms_output.put_line(my_dept_rec.department_id || ' : ' || my_dept_rec.department_name);

  62.  end loop;

  63. end;

  64. /

  65.  

  66. 1.3游标常用属性:

  67. %FOUND:变量最后从游标中获取记录的时候,在结果集中找到了记录。

  68. %NOTFOUND:变量最后从游标中获取记录的时候,在结果集中没有找到记录。

  69. %ROWCOUNT:当前时刻已经从游标中获取的记录数量。

  70. %ISOPEN:是否打开。

  71.  

  72. Declare

  73.  /* 定义静态游标 */

  74.  Cursor emps is

  75.  Select * from employees where rownum<6 order by 1;

  76.  

  77.  Emp employees%rowtype;

  78.  Row number :=1;

  79. Begin

  80.  Open emps; /* 打开静态游标 */

  81.  Fetch emps into emp; /* 读取游标当前行 */

  82.  

  83.  Loop

  84.  If emps%found then

  85.    Dbms_output.put_line('Looping over record '||row|| ' of ' || emps%rowcount);

  86.    Fetch emps into emp;

  87.    Row := row + 1;

  88.  Elsif emps%notfound then

  89.    Exit;

  90.  End if;

  91.  End loop;

  92.  

  93.  If emps%isopen then

  94.  Close emps; /* 关闭游标 */

  95.  End if;

  96. End;

  97. /

  98.  

  99.  

  100. 显式和隐式游标的区别:

  101. 尽量使用隐式游标,避免编写附加的游标控制代码(声明,打开,获取,关闭),也不需要声明变量来保存从游标中获取的数据。

  102.  

  103.  

  104. 2、REF CURSOR游标

  105. 动态游标,在运行的时候才能确定游标使用的查询。可以分为:

  106. l 强类型(限制)(Strong REF CURSOR),规定返回类型

  107. l 弱类型(非限制)(Weak REF CURSOR),不规定返回类型,可以获取任何结果集。

  108. 定义格式:

  109. TYPE ref_cursor_name IS REF CURSOR [RETURN return_type]

  110.  

  111. 例如:

  112. Declare

  113.  Type refcur_t is ref cursor;

  114.  Type emp_refcur_t is ref cursor return employee%rowtype;

  115. Begin

  116.  Null;

  117. End;

  118. /

  119.  

  120. 强类型举例:

  121. declare

  122.  --声明记录类型

  123.  type emp_job_rec is record(

  124.  employee_id number,

  125.  employee_name varchar2(50),

  126.  job_title varchar2(30)

  127.  );

  128.  --声明REF CURSOR,返回值为该记录类型

  129.  type emp_job_refcur_type is ref cursor return emp_job_rec;

  130.  --定义REF CURSOR游标的变量

  131.  emp_refcur emp_job_refcur_type;

  132.  

  133.  emp_job emp_job_rec;

  134. begin

  135.  /* 打开动态游标 */

  136.  open emp_refcur for

  137.  select e.employee_id, e.first_name || ' ' ||e.last_name "employee_name",

  138.     j.job_title

  139.  from employees e, jobs j

  140.  where e.job_id = j.job_id and rownum < 11 order by 1;

  141.  /* 取游标当前行 */

  142.  fetch emp_refcur into emp_job;

  143.  while emp_refcur%found loop

  144.  dbms_output.put_line(emp_job.employee_name || '''s job is ');

  145.  dbms_output.put_line(emp_job.job_title);

  146.  fetch emp_refcur into emp_job;

  147.  end loop;

  148. end;

  149. /

  150.  

  151. 指定了retrun 类型,CURSOR变量的类型必须和return 类型一致。

  152. 例子:

  153. CREATE OR REPLACE PACKAGE emp_data AS

  154. TYPE empcurtyp IS REF CURSOR RETURN emp%rowtype;

  155. --定义Strong REF CURSOR

  156. PROCEDURE open_emp_cv(emp_cv IN OUT empcurtyp, choice INT);

  157. --根据不同的choice选择不同的CURSOR

  158. PROCEDURE retrieve_data(choice INT);

  159. --通过调用procedure open_emp_cv,返回指定的结果集。

  160. END emp_data;

  161.  

  162.  

  163. CREATE OR REPLACE PACKAGE BODY emp_data AS

  164.  

  165. PROCEDURE open_emp_cv(emp_cv IN OUT empcurtyp, choice INT) IS

  166. --emp_cv作为传入/传出的CURSOR PARAMETER

  167. BEGIN

  168. IF choice = 1 THEN

  169. OPEN emp_cv FOR SELECT * FROM emp WHERE empno < 7800;

  170. ELSIF choice = 2 THEN

  171. OPEN emp_cv FOR SELECT * FROM emp WHERE SAL < 1000;

  172. ELSIF choice = 3 THEN

  173. OPEN emp_cv FOR SELECT * FROM emp WHERE ename like 'J%';

  174. END IF;

  175. END;

  176.  

  177. PROCEDURE retrieve_data(choice INT) IS

  178. return_cv empcurtyp;

  179. --定义传入open_emp_cv的CURSOR变量

  180. return_row emp%ROWTYPE;

  181. invalid_choice EXCEPTION;

  182. BEGIN

  183. --调用 procedure OPEN_EMP_CV

  184. open_emp_cv(retu rn_cv, choice);

  185.  

  186. IF choice = 1 THEN

  187. DBMS_OUTPUT.PUT_LINE('EMPLOYEES with empno less than 7800');

  188. ELSIF choice = 2 THEN

  189. DBMS_OUTPUT.PUT_LINE('EMPLOYEES with salary less than 1000');

  190. ELSIF choice = 3 THEN

  191. DBMS_OUTPUT.PUT_LINE('EMPLOYEES with name starts with ''J''');

  192. ELSE

  193. RAISE invalid_choice;

  194. END IF;

  195.  

  196. LOOP

  197. FETCH return_cv INTO return_row;

  198. EXIT WHEN return_cv%NOTFOUND;

  199. DBMS_OUTPUT.PUT_LINE(return_row.empno || '--' || return_row.ename || '--' ||

  200. return_row.sal);

  201. END LOOP;

  202.  

  203. EXCEPTION

  204. WHEN invalid_choice THEN

  205. DBMS_OUTPUT.PUT_LINE('The CHOICE should be in one of (1,2,3)!');

  206. WHEN OTHERS THEN

  207. DBMS_OUTPUT.PUT_LINE('Errors in procedure RETRIEVE_DATA!');

  208. END;

  209.  

  210. END emp_data;

  211.  

  212.  

  213. 执行:

  214. SQL> EXEC emp_data.retrieve_data(1);

  215. SQL> EXEC emp_data.retrieve_data(2);

  216. SQL> EXEC emp_data.retrieve_data(3);

  217. SQL> EXEC emp_data.retrieve_data(34);

  218.  

  219. 使用Weak REF CURSOR例子

  220. create or replace procedure open_cv(choice IN INT, return_cv OUT SYS_REFCURSOR) is

  221. --参数return_cv为weak REF CURSOR,利用SYS_CURSOR来定义

  222. /*使用SYS_REFCURSOR可以使变量any_cv跟任何weak REF CURSOR类型匹配。 */

  223. begin

  224. if choice = 1 then

  225. open return_cv for 'select * from emp';

  226. elsif choice = 2 then

  227. open return_cv for 'select * from dept';

  228. end if;

  229. end open_cv;

  230.  

  231.  

  232. CREATE or replace procedure retrieve_data(choice IN INT) is

  233. emp_rec emp%rowtype;

  234. dept_rec dept%rowtype;

  235. return_cv SYS_REFCURSOR;

  236. invalid_choice exception;

  237.  

  238. BEGIN

  239. if choice=1 then

  240. dbms_output.put_line('employee information');

  241. open_cv(1,return_cv); --调用procedure open_cv;

  242. loop

  243. fetch return_cv into emp_rec;

  244. exit when return_cv%notfound;

  245. dbms_output.put_line(emp_rec.empno||'-'||emp_rec.ename||'-'||emp_rec.sal);

  246. end loop;

  247. elsif choice=2 then

  248. dbms_output.put_line('department information');

  249. open_cv(2,return_cv);

  250.  

  251. loop

  252. fetch return_cv into dept_rec;

  253. exit when return_cv%notfound;

  254. dbms_output.put_line(dept_rec.deptno||'-'||dept_rec.dname||'-'||dept_rec.loc);

  255. end loop;

  256. else

  257. raise invalid_choice;

  258. end if;

  259.  

  260. exception

  261. when invalid_choice then

  262. dbms_output.put_line('The CHOICE should be one of 1 and 2!');

  263. when others then

  264. dbms_output.put_line('Errors in procedure retrieve_data');

  265. END retrieve_data;

  266.  

  267.  

  268. 执行:

  269. SQL> exec retrieve_data(1);

  270. SQL> exec retrieve_data(2);

  271.  

  272.  

  273. 用REF CURSOR实现BULK功能

  274. 1. 可以加速INSERT, UPDATE, DELETE语句的执行,也就是用FORALL语句来替代循环语句。

  275. 2. 加速SELECT,用BULK COLLECT INTO 来替代INTO。

  276.  

  277. SQL> create table tab2 as select empno ID, ename NAME, sal SALARY from emp where 1=2;

  278. create or replace procedure REF_BULK is

  279. /* 定义复杂类型 */

  280. type empcurtyp is ref cursor;

  281. type idlist is table of emp.empno%type;

  282. type namelist is table of emp.ename%type;

  283. type sallist is table of emp.sal%type;

  284.   /* 定义变量 */

  285. emp_cv empcurtyp;

  286. ids idlist;

  287. names namelist;

  288. sals sallist;

  289. row_cnt number;

  290. begin

  291. open emp_cv for select empno, ename, sal from emp;

  292. fetch emp_cv BULK COLLECT INTO ids, names, sals;

  293. --将字段成批放入变量中,此时变量是一个集合

  294. close emp_cv;

  295.  

  296. for i in ids.first .. ids.last loop

  297. dbms_output.put_line(' || ids(i) || ' || names(i) ||' salary=' || sals(i));

  298. end loop;

  299.  

  300. FORALL i IN ids.first .. ids.last

  301. insert into tab2 values (ids(i), names(i), sals(i));

  302. commit;

  303. select count(*) into row_cnt from tab2;

  304. dbms_output.put_line('-----------------------------------');

  305. dbms_output.put_line('The row number of tab2 is ' || row_cnt);

  306. end REF_BULK;

  307.  

  308.  

  309.  

  310.  

  311. 3、cursor 和 ref cursor的区别

  312. 从技术底层看,两者是相同的。普通plsql cursor在定义时是“静态”的。而

  313. Ref cursors可以动态打开。

  314. 例如下面例子:

  315. Declare

  316. type rc is ref cursor;

  317. cursor c is select * from dual;

  318.  

  319. l_cursor rc;

  320. begin

  321. if ( to_char(sysdate,'dd') = 30 ) then

  322.        open l_cursor for 'select * from emp';

  323. elsif ( to_char(sysdate,'dd') = 29 ) then

  324.        open l_cursor for select * from dept;

  325. else

  326.        open l_cursor for select * from dual;

  327. end if;

  328. open c;

  329. end;

  330. /

  331. l rc根据逻辑动态打开;而游标c定义好了只有就无法修改了。

  332. l ref cursor可以返回给客户端,cursor则不行。

  333. l cursor可以是全局的global ,ref cursor则必须定义在过程或函数中。

  334. l ref cursor可以在子程序间传递,cursor则不行。

  335. l cursor中定义的静态sql比ref cursor效率高,所以ref cursor通常用在:向客户端返回结果集。

09-20 03:15