(18)PL/SQL

PL/SQL(Procedure Language/SQL)

PL/SQL是Oracle对sql语言的过程化扩展---指在sql命令语言中增加了过程处理语句(如分支、循环等),使sql语言具有过程处理能力

 

在sqlplus中执行一下操作

set serveroutput on; --打开sqlplus的输出功能

一、一个完整的格式

1.只有执行体部分的结构,也就是只有begin ...end 部分

begin
dbms_output.put_line('123');
end; 
/

运行后

 

斜杠  /  就是让服务器执行前面所写的 SQL 脚本。 因为你普通的 select 语句, 一个分号,就可以执行。

但是如果你的是存储过程, 那么遇到分号,就不能马上执行。 这个时候,就需要通过 斜杠 来执行

 

2.包含声明和执行体两部分的结构

 

declare
v_result number(8,2);
begin
v_result :=100/6;
dbms_output.put_line('最后的结果是'||v_result );
end;
/

第一行声明变量关键字

第二行声明变量

 

 

 

二、注释

单行注释用     --

多行注释用    /**/

 

三、字符命名规则

PLSQL能用的字符集包括

1.大小写字母A-Z,a-z

2.数字0-9

3.Tab、空格、回车

4.数学符号:+,-,*,/,<,>

5.(),{},[],?,!,;,:,@,#,%,$,&

其他的字符都是非法字符(除引号里)

 

四、基本数据类型

定义变量时 变量名在前,数据类型在后

1.数值类型

number,pls_integer,binaru_integer

Number类型可以存储整数或浮点数;

例:Num number(9,2) 

 其中9表示精度,从左边不是零数到小数点后一共有9位,2表示小数点最多两位。这个区间内的的数值都是符合的

 

2.字符类型

varchar2,char,long,nchar,nvarchar2 

name varchar2(80)   --可变字符串,最大长度4000.

 

3.日期类型

 例: pdate  date;

4.布尔类型

 boolean

变量的赋值要用:=

例:  b boolean:=True;

 

declare
count number(5,2);
name varchar2(20);
today date;
begin
--赋值
count :=1
name :='hello';
today:=sysdate;
--打印
dbms_output.put_line(count);
dbms_output.put_line(name);
dbms_output.put_line(today);
end;
/

 

5.特殊数据类型

(1)引用型变量

name emp.ename%type;  --把员工表中ename的类型 定义为name的类型

 

declare
name emp.ename%type;
salary emp.sal%type;
begin
--查询出emp表姓名和薪水,并用into赋值给name和salary变量
select ename,sal into name ,salary  from emp where empno=7839;
dbms_output.put_line(name ||'的薪水是'||salary );
end;
/

 

(2)记录型变量

取一行变量的类型

declare
emp_rec emp%rowtype;
begin
--查询后把正行的变量名和类型都赋值给emp_rec
select * into emp_rec from emp where empno=7839;
dbms_output.put_line(emp_rec.ename||'的薪水是'||emp_rec.sal);
end;
/

 

 

五、流程控制语句

选择语句(包括4种)

1、 if then

 只做一种条件判断

/* Formatted on 2017/5/20 9:19:56 (QP5 v5.227.12220.39754) */
set serveroutput on
DECLARE
name1 VARCHAR (50);
name2 VARCHAR (50);
BEGIN
name1 := 'abc';
name2 := 'abcd';

IF LENGTH (name1) < LENGTH (name2)
THEN
DBMS_OUTPUT.put_line ('name1的长度小于name2');
END IF;
END;
/

 

if条件可判断是否为空或 and or not 等逻辑运算

set serveroutput on
DECLARE
name1 VARCHAR (50);
name2 VARCHAR (50);
BEGIN
name1 := '';

IF name1 is not null
THEN
DBMS_OUTPUT.put_line ('name1不为空');
else
DBMS_OUTPUT.put_line ('name1为空');
END IF;
END;
/

 

2. if then else

 两种判断

set serveroutput on
DECLARE
name1 VARCHAR (50);
name2 VARCHAR (50);
BEGIN
name1 := 'abcde';
name2 := 'abcd';

IF LENGTH (name1) < LENGTH (name2)
THEN
DBMS_OUTPUT.put_line ('name1的长度小于name2');
else
DBMS_OUTPUT.put_line ('name1的长度大于name2');
END IF;
END;
/

 

3. if then elsif

 多分支判断

DECLARE
   month   INT := 10;
BEGIN
   IF month >= 1 AND month <= 3
   THEN
      DBMS_OUTPUT.put_line ('春天');
   ELSIF month >= 4 AND month <= 6
   THEN
      DBMS_OUTPUT.put_line ('夏天');
   ELSIF month >= 7 AND month <= 9
   THEN
      DBMS_OUTPUT.put_line ('秋天');
   ELSIF month >= 10 AND month <= 12
   THEN
      DBMS_OUTPUT.put_line ('冬天');
   ELSE
      DBMS_OUTPUT.put_line ('月份不合法');
   END IF;
END;
/

 

4. case

 

SET SERVEROUTPUT ON

DECLARE
   score   VARCHAR (20) := '';
BEGIN
   CASE score
      WHEN ''
      THEN
         DBMS_OUTPUT.put_line ('学霸你好');
      WHEN ''
      THEN
         DBMS_OUTPUT.put_line ('命中注定你是个普通人');
      WHEN ''
      THEN
         DBMS_OUTPUT.put_line ('未来的老板');
      ELSE
         DBMS_OUTPUT.put_line ('不要乱赋值');
   END CASE;
END;
/

 

循环语句(3种)

1.loop

 先执行一次循环体,然后再判断 exit when 关键字后面的条件表达式的值是ture还是false,如果是true退出循环,如果是flase则继续执行

SET SERVEROUTPUT ON

DECLARE
   num   INT := 5;
BEGIN
   LOOP
      DBMS_OUTPUT.put_line ('num=' || num);
      num := num - 1;
      EXIT WHEN num = 2;
   END LOOP;

   DBMS_OUTPUT.put_line ('执行完成');
END;
/

 

2.while

 先判断表达式,在循环。有可能执行0次

SET SERVEROUTPUT ON

DECLARE
   num   INT := 5;
BEGIN
   WHILE num >= 1
   LOOP
      DBMS_OUTPUT.put_line ('num=' || num);
      num := num - 1;
   END LOOP;

   DBMS_OUTPUT.put_line ('执行完成');
END;
/

 

3.for

 语法:

for a in [reverse] b..c 
loop ...
end loop;

a: 整数变量,用来作为计数器,默认计数器循环递增,如果加上了[reverse]  表示循环递减

b:计数器下限值,当计数器的值小于下限值时,程序循环终止

c:计数器上限值,当计数器的值大于上限值时,程序循环终止

SET SERVEROUTPUT ON

DECLARE
   num   INT := 6;
BEGIN
   FOR i IN 3 .. 5    --i初始值是3,共执行三遍
   LOOP
      DBMS_OUTPUT.put_line ('num=' || num);
   END LOOP;

   DBMS_OUTPUT.put_line ('执行完成');
END;
/

 

六、PL/SQL游标

 在PL/SQL里使用游标代表集合

1.显示游标

声明游标

声明游标要在pl\sql的declare里

cursor 游标名[(变量名 in 类型:='')]
[return 返回值类型]
is sql语句

DECLARE
CURSOR cur_n IS 
SELECT id,name FROM student WHERE name= 'tom';

 

DECLARE
CURSOR cur_n(name1 in varchar2:='tom') IS--不要指定varchar2的长度,否则报错
SELECT id,name  FROM student  WHERE name = name1;

 

打开游标

在打开游标的过程中,程序首先将符合条件的记录送人内存中,然后再将指‘针指’向第一条记录

打开游标要写在begin里

格式:

open 游标名[参数]

open cur_n;

 

open cur_n('tom');

 

读取游标

打开游标后,开始读取游标中的数据,读取方式是逐行将结果数据保存到变量中

格式:

fetch 游标名 into 变量 

fetch cur_n into 

 

2.游标属性

无论是显示游标还是隐式游标,都具有 %found %notfound %isopen %rowcount 四个属性

通过这四个属性可以获知sql语句的执行结果以及该游标的状态信息。

%found: 布尔型属性,如果SQL语句至少影响到一行数据,则改属性为true,否则为false

%notfound:与%found功能相反

%rowcount:数字型属性,返回受sql语句影响的行数

%isopen:布尔型属性,当游标已经打开时返回true,关闭时则false

 

declare
--定义一个游标集合,指向该结果集的第一行
cursor cemp is select ename,sal from emp;
--定义变量
pename emp.ename%type;
psal emp.sal%type;
begin
--打开游标
open cemp;
loop 
--取游标集合的一条记录
fetch cemp into pename,psal;
--游标行无数据时退出
exit when cemp%notfound;
dbms_output.put_line(pename||'的薪水是'||psal);
end loop;
--关闭游标
close cemp;
end;
/

 

3.隐示游标

 

4.带参数光标

带参数的游标就定义的时候和open的时候不带参数的游标有区别

declare
--定义一个参数
cursor cemp(dno number) is select ename from emp where deptno=dno;
pename emp.ename%type;
begin
open cemp(10);--传参
loop
fetch cemp into pename;
exit when cemp%notfound;
dbms_output.put_line(pename);
end loop;
close cemp;
end;
/

 

 

5.光标数的限制

oracle数据库只允许在同一个会话中打开300个光标。

修改光标数的限制:

alter system set open_cursors=400 scope=both;--允许400光标

scope取值有三种:memory(只更改当前实例),spfile(只更改参数文件--数据库需要重启),both(前两者同时更改)

 

 

七、异常

 异常格式:

exception
when 异常类型 then
...
when 异常类型 then
...
end;

 

预定义异常

zero_divide:除数为零时引发的异常

access_into_null:企图为某个未初始化对象的属性赋值

collection_is_null:企图使用未初始化的集合元素

cursor_already_open:企图再次打开一个已经打开过的游标,但重新打开之前,游标未关闭

invalid_number:企图将一个字符串转换成一个无效的数字而失效

login_denied:企图使用无效的用户名或密码连接数据库

no_data_found:select into 语句没有返回数据

rowtype_mismatch:主游标变量与PL/SQL游标变量的返回类型不兼容

self_is_null:使用对象类型时,使用空对象调用其方法

subscript_beyond_count:元素下标超过嵌套表或varray的最大值

subscript_outside_limit: 企图使用非法索引号引用嵌套表或varray中的元素

sys_invalid_rowid:字符串向rowid转换时的错误,因为该字符串不是一个有效的rowid值

timeout_on_resource oracle在等待资源时超时

too_many_rows 执行select into 语句时,结果集超过一行引发的异常

 

 例:

no_data_found

declare
pename emp.ename%type;
begin
select ename into pename from emp where empno=1234;
exception
when no_data_found then dbms_output.put_line('未找到该员工');
--除了上面的异常以外的所有异常
when others then dbms_output.put_line('其他例外');
end;
/

too_many_rows

declare
pename emp.ename%type;
begin
--如果返回多行数据会报异常
select ename into pename from emp where deptno=10;
exception
when too_many_rows then dbms_output.put_line('打印了多行');
--除了上面的异常以外的所有异常
when others then dbms_output.put_line('其他例外');
end;
/

 

自定义异常

 

declare
cursor cemp is select ename from emp where deptno=99;
pename emp.ename%type;
--自定义异常
no_emp_found exception;
begin
open cemp;
fetch cemp into pename;
if cemp%notfound then
--抛出异常
raise no_emp_found;
-- 当发生异常后,oracle调用close自动关闭
close cemp;
exception
whenno_emp_found then dbms_output.put_line('没有找到员工');
when others then dbms_output.put_line('其他例外');
end;
/

 

posted @ 2017-03-09 22:50  富坚老贼  阅读(213)  评论(0编辑  收藏  举报