PL/SQL 知识 (个人笔记)(四)

PL/SQL 知识 (个人笔记)(四)

个人笔记

接上篇

PL/SQL 知识 (个人笔记)(一)

PL/SQL 知识 (个人笔记)(二)

PL/SQL 知识 (个人笔记)(三)

**

之前知识点回顾:

1.变量,常量定义及赋值;
赋值 变量名 :=值;
常量名 constant 数据类型(长度);
变量名 数据类型(长度)
2.注释;
单行注释: -- 
多行注释: /* */
3.类型;
变量名/常量名 表名.列名%type--沿用表中某一列的数据类型 
变量名/常量名 表名%rowtype --沿用整张表的数据类型;

tyepe 类型名 is record--人为定义类型
(); 
变量名 类型名;--变量沿用类型的数据类型

4.动态SQL
动态SQL主要执行DDL
SQL语句用单引号或者q'[]'包裹,用一个变量承接SQL内容,然后 execute immediate 变量;
如果动态SQL里是查询语句,则需要 execute immediate 变量 into 变量;
5.绑定变量
SQL中是 查询 =:1的方式;
execute immediate 变量 into 变量 using 值;
6.流程控制
if-else

if和end if成对出现
elsif的写法
每个if或者elsif后都有一个then, else后是没有的

case when
类似查询时的case when,分等值和范围判断两种;
case和end case成对出现; 
每个 when之后都有一个then,else之后没有

7.循环
重点是for循环,loop和while重要性和普遍性次之

for1:
for i in 1..9 loop
循环体;
end loop;
--不需要定义循环变量,循环变量自增

for2:
for 变量 in(查询语句) loop
循环体;
end loop;
--不需要定义循环变量,不限定循环上下限

8.退出循环
exit:退出循环体,执行循环体以外的代码
continue:退出本次循环,执行下一次循环
return:直接跳到end,结束程序,return到end中间的代码不再执行

代码编写要领:
1.基础得扎实,在写之前想好该用什么,比如 while,loop或者for
2.先写框架,再写代码块;需要什么变量,再回头去定义;
3.写好之后执行验证结果,斟酌代码逻辑;

游标(cursor):

1.定义:

游标是将数据文件中的数据读入内存,有一个指针指向内存中数据的第一行,每fetch一次,指针往后挪一行,以内存换效率,尤其是处理大量数据时;

语法:

declare
cursor 游标名 is select语句;
begin
open 游标名;
fetch 游标名 into 变量;
针对变量执行动作;
close 游标名;
end;

范例:

declare
cursor cur_dept is select * from dept;
v_dept dept%rowtype;
begin
  open cur_dept;
    fetch cur_dept into v_dept;
    dbms_output.put_line('deptno:'||v_dept.deptno||', dname:'||v_dept.dname||', LOC:'||v_dept.loc);
    close cur_dept;
end;

加入循环:

declare
cursor cur_dept is select * from dept;
v_dept dept%rowtype;
begin
  open cur_dept;
  loop
    exit when cur_dept%NOTFOUND;
    fetch cur_dept into v_dept;
    dbms_output.put_line('deptno:'||v_dept.deptno||', dname:'||v_dept.dname||', LOC:'||v_dept.loc);
    end loop;
    close cur_dept;
end;

参数:

游标名%ISOPEN:检查游标是否打开;值返回true和false,若为true,则说明游标打开,false则说明游标未打开;
游标名%FOUND:检查游标内是否还有数据,返回true则说明有数据,false则说明无数据;
游标名%NOTFOUND:检查游标是否有数据;只返回true或者false,返回true就退出,返回false则继续往下执行;
游标名%ROWCOUNT:统计本次执行行数(不是全部,只是本次);
SQLCODE:报错代码,ORA-数字;
SQLERRM: ERRM:error massage缩写,错误信息;

重点:
1.游标语法;
2.每次fetch只有一行;
3.代码开始就open 游标,最后需要close游标;
4.%NOTFOUND,SQLCODE,SQLERRM,%ROWCOUNT

**

游标分类:

**

1.静态和动态

使用动态SQL的叫动态游标,其余的是静态游标;
静态游标范例:

declare
cursor cur_dept is select * from dept;
v_dept dept%rowtype;
begin
  open cur_dept;
  loop
    fetch cur_dept into v_dept;
    exit when cur_dept%NOTFOUND;
    dbms_output.put_line('deptno:'||v_dept.deptno||', dname:'||v_dept.dname||', LOC:'||v_dept.loc);
    end loop;
    close cur_dept;
end;

动态游标范例:

declare
  v_sql   varchar2(300);
  v_emp   emp%rowtype;
  type ref_cur is ref cursor;
  cur_emp ref_cur;
begin
  v_sql := q'[select * from emp]';
  open cur_emp for v_sql;
  loop
  fetch cur_emp into v_emp;
  exit when cur_emp%NOTFOUND;
  dbms_output.put_line(v_emp.empno);
  end loop;
  close cur_emp;
end;
declare
  v_sql   varchar2(300);
  v_emp   emp%rowtype;
  cur_emp sys_refcursor;
begin
  v_sql := q'[select * from emp]';
  open cur_emp for v_sql;
  loop
  fetch cur_emp into v_emp;
  exit when cur_emp%NOTFOUND;
  dbms_output.put_line(v_emp.empno);
  end loop;
  close cur_emp;
end;

(1).使用动态游标前须定义游标类型, 沿用系统游标类型:type ref_cur is ref cursor;
(2).游标沿用系统游标属性 的定义类似之前沿用类型的方式,是 游标名 类型名 的形式;
(3).动态游标是 open 游标名 for v_sql的方式,区别于静态游标先定义游标,再open的方式;

**

2.隐式和显式

**
有游标定义的叫显式,执行DML时会自动加隐式游标
显示游标:

declare
cursor cur_dept is select * from dept;
v_dept dept%rowtype;
begin
  open cur_dept;
  loop
    fetch cur_dept into v_dept;
    exit when cur_dept%NOTFOUND;
    dbms_output.put_line('deptno:'||v_dept.deptno||', dname:'||v_dept.dname||', LOC:'||v_dept.loc);
    end loop;
    close cur_dept;
end;

隐式游标:

declare
  v_count number(5);
begin
  select count(1) into v_count from emp;
  if SQL%FOUND then
    dbms_output.put_line('隐式游标存在');
  else
    dbms_output.put_line('不存在');
  end if;
end;

(1):在代码块中执行增删改查时,系统会自动增加隐式游标;
(2):隐式游标统一都叫 SQL,所以可以使用SQL%NOTFOUND, SQL%FOUND,SQL%OPEN等参数确认游标状态;
(3):隐式游标不需要人为打开或者关闭;

绑定变量方式:

declare
  v_sql   varchar2(300);
  v_emp   emp%rowtype;
  type ref_cur is ref cursor;
  cur_emp ref_cur;
  v_deptno number(4):=10;
begin
  v_sql := q'[select * from emp where deptno=:1]';
  open cur_emp for v_sql using v_deptno;
  loop
  fetch cur_emp into v_emp;
  exit when cur_emp%NOTFOUND;
  dbms_output.put_line(v_emp.empno);
  end loop;
  close cur_emp;
end;
(1):动态游标绑定变量方式在处理大量数据时,效率很高;
(2):类似绑定变量,在使用时是 open 游标名 for v_sql using 参数 的方式;
(3):其余的方面跟动态游标保持一致;

1.测试动态游标绑定变量方式处理数据与静态游标的区别;–实际过滤数据;
2.打印全表数据,动态游标采用 where 1=:1 绑定变量方式;

**

游标实例

批量取数据

**
fetch 游标名 bulk collect into 变量名 limit 数字;

范例:

declare
  cursor cur_emp is select * from emp;
  type t_emp is table of emp%ROWTYPE;
  v_emp t_emp;
begin
  open cur_emp;
  loop
    fetch cur_emp bulk collect into v_emp limit 5;
      for i in  v_emp.first..v_emp.last loop
        dbms_output.put_line('EMPNO:'||v_emp(i).EMPNO||', ENAME:'||v_emp(i).ENAME);
        end loop;
        dbms_output.put_line(cur_emp%ROWCOUNT);
      exit when cur_emp%NOTFOUND;
  end loop;
close cur_emp;
end;

(1): bulk collect时,变量类型需要是集合类型,集合类型的定义:type t_emp is table of emp%ROWTYPE;
(2): 外层使用loop循环,内层使用for循环,循环变量的范围是 变量名.first … 变量名.last 的方式;
(3): 打印或者使用数据时,需要带上下标 i, v_emp(i)表示第i行.

declare
  cursor cur_emp is select * from emp;
  type t_emp is table of emp%ROWTYPE;
  v_emp t_emp;
begin
  open cur_emp;
  loop
    fetch cur_emp bulk collect into v_emp limit 5;
    exit when v_emp.count=0;
    --exit when cur_emp%NOTFOUND;
      for i in  v_emp.first..v_emp.last loop
        dbms_output.put_line('EMPNO:'||v_emp(i).EMPNO||', ENAME:'||v_emp(i).ENAME);
        end loop;
        dbms_output.put_line(cur_emp%ROWCOUNT);
      --exit when cur_emp%NOTFOUND;
  end loop;
close cur_emp;
end;

使用 游标名%NOTFOUND的退出条件,如果exit跟在fetch之后,且表中数据量不能被Limit数值整除时,会出现数据不全的现象.原因是当最后fetch的数据量不够limit值时,会执行exit退出,所以不会操作数据.
修改方案:
1.改用 exit when 变量名.count=0 的方式退出;–推荐
2.将exit when 游标名%NOTFOUND下移,移到外层循环结束前使用;

思考:

declare
  cursor cur_dept is select * from dept;
  v_dept dept%ROWTYPE;
begin
  open cur_dept;
  loop
    fetch cur_dept into v_dept;
    dbms_output.put_line(v_dept.deptno);
    exit when cur_dept%NOTFOUND;
  end loop;
  close cur_dept;
end;
declare
  cursor cur_dept is select * from dept;
  v_dept dept%ROWTYPE;
begin
  open cur_dept;
  loop
    fetch cur_dept into v_dept;
    exit when cur_dept%NOTFOUND;
    dbms_output.put_line(v_dept.deptno);
  end loop;
  close cur_dept;
end;

游标练习:

打印emp表的全部信息;
1、定义游标:列出每个员工的姓名、部门名称并编程显示第5个到第10个记录.

declare
  type t_1 is record(
    ename varchar2(50),
    dname varchar2(50));
  v_1 t_1;
  cursor cur_1 is
   select ename,dname from( select rownum rn, a.ename, b.dname from emp a, dept b where a.deptno = b.deptno) where rn>=5 and rn<=10;

begin
  open cur_1;
  loop
    fetch cur_1 into v_1;
    exit when cur_1%NOTFOUND;
    dbms_output.put_line('ENAME:' || v_1.ename || ', DNAME:' || v_1.dname);
  end loop;
  close cur_1;
end;

declare
  type t_1 is record(
    ename varchar2(50),
    dname varchar2(50));
  v_1 t_1;
  cursor cur_1 is
    select a.ename, b.dname from emp a, dept b where a.deptno = b.deptno;
begin
  open cur_1;
  loop
    fetch cur_1
      into v_1;
    exit when cur_1%NOTFOUND;
    if cur_1%ROWCOUNT<5 then continue;
      elsif cur_1%ROWCOUNT>10 then exit;
      else
        dbms_output.put_line('ENAME:' || v_1.ename || ', DNAME:' || v_1.dname);
        end if;
  end loop;
  close cur_1;
end;

2、定义游标:从雇员表中显示工资大于3000的记录,只要姓名、部门编号和工资。编程显示其中的奇数记录。

declare
  v_emp emp%ROWTYPE;
  cursor cur_emp is
    select * from emp where sal > 1000;
begin
  open cur_emp;
  loop
    fetch cur_emp into v_emp;
    exit when cur_emp%NOTFOUND;
    if mod(cur_emp%ROWCOUNT, 2) = 1 then
      dbms_output.put_line('ENAME:' || v_emp.ename || ', DEPTNO' ||v_emp.deptno || ', SAL:' || v_emp.sal);
    end if;
  end loop;
  close cur_emp;
end;

3、用游标显示所有部门编号与名称,以及其所拥有的员工人数。

declare
cursor cur_check is select b.deptno,b.dname,count(1) cnt from emp a, dept b where a.deptno=b.deptno group by b.deptno,b.dname;
type t_dept is record
(
   deptno  dept.deptno%TYPE,
   dname  dept.dname%TYPE,
     cnt number(3)
);
v_dept t_dept;
begin
  open cur_check;
  loop
    fetch cur_check into v_dept;
    exit when cur_check%NOTFOUND;
    dbms_output.put_line('Deptno:'||v_dept.deptno||', Dname: '||v_dept.dname||', Cnt'||v_dept.cnt);
    end loop;
    close cur_check;
end;

4、用游标属性%rowcount实现输出前十个员工的信息

declare
  cursor cur_emp is select * from emp;
  v_emp emp%ROWTYPE;
begin
  open cur_emp;
  loop
    fetch cur_emp into v_emp;
    exit when cur_emp%NOTFOUND;
    if (cur_emp%ROWCOUNT > 10) then
      exit;
    end if;
    dbms_output.put_line(v_emp.ename);
  end loop;
  close cur_emp;
end;

5、通过使用游标来显示dept表中的部门名称,及其相应的员工列表(提示:可以使用双重循环)。

declare
  cursor cur1 is
    select * from dept;
  v_dept  dept%ROWTYPE;
  v_ename emp.ename%TYPE;
  type t_emp is table of emp%ROWTYPE;
  v_emp t_emp;
begin
  open cur1;
  loop
    fetch cur1
      into v_dept;
    exit when cur1%NOTFOUND;
    dbms_output.put_line('');
    dbms_output.put_line(v_dept.dname || ':');
    for i in (select ename from emp where deptno = v_dept.DEPTNO) loop
      dbms_output.put_line(i.ename);
    end loop;
  end loop;
  close cur1;
end;

6、接受一个部门号,使用For循环,从emp表中显示该部门的所有雇员的姓名,工作和薪水。

declare
v_deptno number(4):=&Deptno;
begin
  for v in (select * from emp where mgr in(select mgr from emp where deptno=v_deptno)) loop
    dbms_output.put_line('Ename:'||v.ename||', Job:'||v.job||', Sal:'||v.sal);
    end loop;
end;

猜你喜欢

转载自blog.csdn.net/yang_z_1/article/details/111880054