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.显示游标
声明游标
声明游标要在plsql的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; /