文章目录
一、PLSQL简介
- PLSQL是一种程序语言,叫做过程化SQL语言(Procedural Language/SQL)
- PLSQL是Oracle数据库对SQL语句的扩展。在普通SQL语句的使用上增加了编程语言的特点。
- PLSQL是Oracle系统的核心语言,现在Oracle的许多部件都是由PLSQL
写成的 - PLSQL具有简单、高效、灵活、实用的特点
- Oracle数据库对SQL 的扩展是PLSQL,SQL Server则是Transac-SQL(T-SQL)
PLSQL的官方文档地址:https://docs.oracle.com/cd/E11882_01/appdev.112/e25519/toc.htm
二、PLSQL语法结构
1.PLSQL块
块(block)是PLSQL的基本程序单位。由声明部分、执行部分、异常处理部分组成
DECLARE
/*声明部分----声明变量、常量、复杂数据类型、游标等*/
BEGIN
/*执行部分----PLSQL语句和SQL语句*/
EXCEPTION
/*异常处理部分----处理运行错误*/
END;----块结束标记
2.PLSQL块的分类
匿名块:动态构造,只能执行一次。
子程序:存储在数据库中的存储过程、函数及包等。当在数据库上建立好后可以在其它程序中调用它们。
触发器:当数据库发生操作时,会触发一些事件,从而自动执行相应的程序。
示例:(全文代码环境全部在navicat 查询界面)
BEGIN
DBMS_OUTPUT.PUT_LINE('hello,word!');--打印语句
END;
三、PLSQL变量
1.命名规则
当编写PLSQL块时,为了临时存储数据,需要定义变量和常量,这些名称的定义需要满足一下规则:
- 标识名不能超过30个字符
- 第一个字符必须为字母
- 不分大小写
- 不能用“-”减号等特殊符号
- 尽量不把变量名声明和表中字段名一样
参考规则(name是表名):
标识符 | 命名规则 |
---|---|
程序变量 | v_name |
程序常量 | c_name |
游标变量 | name_curror |
异常标识 | e_name |
记录类型 | name_record |
2.变量类型
- 数值类型:NUMBER(p,s)以及子类型INT、FLOAT等
- 字符类型:CHAR(n)、VARCHAR2(n)
- 日期类型:DATE
- 布尔类型:BOOLEAN
3.大小写规则
当编写sql语句的PLSQL语句时,既可以采用大写格式,也可以采用小写格式。但是为了程序的可读性,应尽量按照以下规则:
- SQL关键字采用大写格式,如:SELECT ,UPDATE等
- PLSQL关键字采用大写格式,如:DECLARE, BEGIN, END等
- 数据类型采用大写格式,如:INT, DATE等
- 标识符采用小写格式,如:v_sal等
- 数据库对象和列采用小写格式,如:emp,sal等
4.注释
- 单行注释: --注释内容
- 多行注释:/注释内容/
5.变量赋值
variable:=expression
如果在声明部分赋值,也可以使用DEFAULT
关键字
6.示例
DECLARE
v_name VARCHAR2(10);
v_sal NUMBER(7,2);
v_hiredate DATE;
c_tax_rate CONSTANT NUMBER(3,2):= 0.02;--变量常量赋值,这里:=也可以用DEFAULT代替
v_tax_sal NUMBER(7,2);
v_valid BOOLEAN DEFAULT TRUE;--DEFAULT也可以用赋值符号:=代替,布尔类型变量主要勇士逻辑判断
c_empno INT :=7369;
BEGIN
SELECT ename,sal,hiredate INTO v_name,v_sal,v_hiredate FROM emp WHERE EMPNO=c_empno;--将查询字段结果赋值给变量用INTO关键字,前后顺序对应
--计算纳税额
v_tax_sal:=v_sal*c_tax_rate;
--打印信息(使用声明处已被赋值的变量或常量)
DBMS_OUTPUT.PUT_LINE(v_name||'的工资是'||v_sal||',雇佣日期是'||v_hiredate||',纳税额是'||v_tax_sal);
--条件判断
IF v_valid THEN
DBMS_OUTPUT.PUT_LINE('已核实');
END IF;
END;
四、PLSQL引用型变量和记录型变量
当一些变量在声明的时候就要求和表列具有完全一致的数据类型,因此可以直接使用原表列的类型用于定义变量。
1.引用型变量
是指其数据类型与已经定义的某个数据变量的类型相同,或者与数据库表的某个列的数据类型相同。如:v_name emp.ename%TYPE
。
DECLARE
v_name emp.ename%TYPE;--v_name的数据类型和emp的ename字段相同
v_sal emp.sal%TYPE;
BEGIN
SELECT ename,sal INTO v_name,v_sal FROM emp WHERE empno=7788;
DBMS_OUTPUT.PUT_LINE(v_name||'的工资是'||v_sal);
END;
2.记录型变量
变量定义的时候使用%ROWTYPE
操作符,返回一整条记录的类型,其数据类型和数据库表的数据结构一致。如:emp_record emp%ROWTYPE
是将emp表的一整条数据类型赋给变量emp_record,emp_record.ename:='ROSE’是取其一个字段ename形成子变量
DECLARE
emp_record emp%ROWTYPE;--emp_record就是emp表的一条记录类型的集合
c_tax_rate CONSTANT NUMBER(3,2)DEFAULT 0.02;
v_tax_sal NUMBER(7,2);
BEGIN
SELECT * INTO emp_record FROM EMP WHERE EMPNO=7788;
v_tax_sal:=emp_record.sal*c_tax_rate;--emp_record.sal就是取其一个字段的子变量
DBMS_OUTPUT.PUT_LINE(emp_record.ename||'的工资是'||emp_record.sal||',纳税额是'||v_tax_sal);--子变量直接使用emp表的字段名
END;
五、PLSQL运算符
1.关系运算符
运算符 | 含义 |
---|---|
+ | 加号 |
- | 减号 |
* | 乘号 |
** | 乘方 |
DECLARE
v_n1 NUMBER:=3;
v_n2 NUMBER:=5;
BEGIN
DBMS_OUTPUT.PUT_LINE(v_n1+v_n2);
DBMS_OUTPUT.PUT_LINE(v_n1-v_n2);
DBMS_OUTPUT.PUT_LINE(v_n1*v_n2);
DBMS_OUTPUT.PUT_LINE(v_n1/v_n2);
DBMS_OUTPUT.PUT_LINE(v_n1**v_n2);
END;
2.关系运算符
运算符 | 含义 |
---|---|
= | 等于 |
<>,!=,~=,^= | 不等于 |
< | 小于 |
> | 大于 |
< | 小于等于 |
> | 大于等于 |
DECLARE
v_n1 NUMBER(2):=&n1;--&替代变量,系统提示输入,navicat中无法使用
v_n2 NUMBER(2):=&n2;
BEGIN
IF (v_n1 = v_n2) THEN
DBMS_OUTPUT.PUT_LINE('等于');
ELSIF (v_n1 > v_n2) THEN
DBMS_OUTPUT.PUT_LINE('大于');
ELSIF (v_n1 < v_n2) THEN
DBMS_OUTPUT.PUT_LINE('小于');
END IF;
IF (v_n1 <> v_n2) THEN
DBMS_OUTPUT.PUT_LINE('不等于');
END IF;
END;
3.比较运算符
运算符 | 含义 |
---|---|
IS NULL | 是空值 |
BETWEEN… AND | 介于两者之间 |
IN | 等于列表中某个值 |
DECLARE
v_num NUMBER :=3;
v_str VARCHAR2(10):='apple';
BEGIN
IF (v_num BETWEEN 0 AND 10) THEN
DBMS_OUTPUT.PUT_LINE('位于0~10');
ELSE
DBMS_OUTPUT.PUT_LINE('不位于0~10');
END IF;
IF (v_num IN (1,3,5)) THEN
DBMS_OUTPUT.PUT_LINE('在集合内');
ELSE
DBMS_OUTPUT.PUT_LINE('不在集合内');
END IF;
IF (v_num IS NULL) THEN
DBMS_OUTPUT.PUT_LINE('为空');
ELSE
DBMS_OUTPUT.PUT_LINE('不为空');
END IF;
IF (v_str LIKE 'a%') THEN
DBMS_OUTPUT.PUT_LINE('以a开头');
ELSE
DBMS_OUTPUT.PUT_LINE('不以a开头');
END IF;
END;
4.逻辑运算符
运算符 | 含义 |
---|---|
AND | 逻辑与 |
OR | 逻辑或 |
NOT | 取反,如 IS NOT NULL, NOT IN |
DECLARE
v_b1 BOOLEAN :=TRUE;
v_b2 BOOLEAN :=FALSE;
BEGIN
IF (v_b1 AND v_b2) THEN
DBMS_OUTPUT.PUT_LINE('AND--TRUE');
END IF;
IF (v_b1 OR v_b2) THEN
DBMS_OUTPUT.PUT_LINE('OR--TRUE');
END IF;
IF (NOT v_b2) THEN
DBMS_OUTPUT.PUT_LINE('n_b2--NOT--TRUE');
END IF;
END;
5.NULL的运算特点
- 空值加数字仍是空值:NULL+<数字>=NULL,所以如果要避免空值话,可以使用NVL函数
- 空值连接字符,结束是字符:NULL||<字符串>=<字符串>
DECLARE
v_num NUMBER :=3;
v_str VARCHAR2(10):='apple';
BEGIN
DBMS_OUTPUT.PUT_LINE(v_num+NULL);
DBMS_OUTPUT.PUT_LINE(v_str||NULL);
END;
六、条件控制语句
1.IF 语句
- 简单条件判断:IF-THEN
语法格式:
IF condition THEN
statements;
END IF;
示例:
--输入员工号,判断员工工资显示小于3000的员工
DECLARE
v_name EMP.ENAME%TYPE;
v_sal EMP.SAL%TYPE;
BEGIN
SELECT ename,sal INTO v_name,v_sal FROM EMP WHERE EMPNO=&n;
IF v_sal<3000 THEN
DBMS_OUTPUT.PUT_LINE(v_name);
END IF;
END;
- 二重条件分支:IF-THEN-ELSE
语法格式:
IF condition THEN
statements;
ELSE
statements;
END IF;
示例:
--输入员工号,判断员工工资,将工资小于3000的员工工资涨200,并显示涨工资的员工姓名,其它员工显示员工姓名及工资
DECLARE
v_name EMPNEW.ENAME%TYPE;
v_sal EMPNEW.SAL%TYPE;
v_empno EMPNEW.EMPNO%TYPE=7788;
BEGIN
SELECT ename,sal INTO v_name,v_sal FROM EMPNEW WHERE EMPNO=v_empno;
IF v_sal<3000 THEN
UPDATE EMPNEW SET SAL=SAL+200 WHERE EMPNO=&v_empno;
COMMIT;--数据库操作语言DML,执行后需要提交
DBMS_OUTPUT.PUT_LINE(v_name||'涨工资了');
ELSE
DBMS_OUTPUT.PUT_LINE(v_name||'的工资是'||v_sal);
END IF;
END;
- 多重条件分支:IF-THEN-ELSIF
语法格式:
IF condition THEN
statements;
ELSIF condition THEN
statements;
ELSE
statements;
END IF;
示例:
--输入员工号,判断员工工资,工资小于2000,显示低收入,工资小于6000,显示中等收入,其它显示高收入
DECLARE
v_name EMPNEW.ENAME%TYPE;
v_sal EMPNEW.SAL%TYPE;
v_empno EMPNEW.EMPNO%TYPE:=7788;
BEGIN
SELECT ename,sal INTO v_name,v_sal FROM EMPNEW WHERE EMPNO=v_empno;
IF v_sal<2000 THEN
DBMS_OUTPUT.PUT_LINE(v_name||'低收入');
ELSIF v_sal<6000 THEN
DBMS_OUTPUT.PUT_LINE(v_name||'中等收入');
ELSE
DBMS_OUTPUT.PUT_LINE(v_name||'其它');
END IF;
END;
2.CASE
使用CASE语句执行多重条件分支操作,语句更加简洁执行效率更好。
- 单一选择符进行等值比较
语法格式:
CASE selector
WHEN expression1 THEN sequence_of_statements1;
WHEN expression2 THEN sequence_of_statements2;
...
WHEN expressionN THEN sequence_of_statementsN;
[ELSE sequence_of_statements;]
END CASE;
示例:
--输入成绩等级,判断属于哪个层次,并打印输出
DECLARE
v_grade CHAR(1):='&n';
BEGIN
CASE v_grade
WHEN 'A' THEN
DBMS_OUTPUT.PUT_LINE('优秀');
WHEN 'B' THEN
DBMS_OUTPUT.PUT_LINE('中等');
ELSE
DBMS_OUTPUT.PUT_LINE('其它');
END CASE;
END;
- 多种条件进行非等值比较
语法格式:
CASE
WHEN condition1 THEN sequence_of_statements1;
WHEN condition2 THEN sequence_of_statements2;
...
WHEN conditionN THEN sequence_of_statementsN;
[ELSE sequence_of_statements;]
END CASE;
示例:
--输入员工号,获取员工工资,判断工资,如果小于1500,补助加100,如果小于2500,补助加80,如果小于5000,补助加50
DECLARE
v_name EMPNEW.ENAME%TYPE;
v_sal EMPNEW.SAL%TYPE;
v_empno EMPNEW.EMPNO%TYPE:=7788;
BEGIN
SELECT ename,sal INTO v_name,v_sal FROM EMPNEW WHERE EMPNO=v_empno;
CASE
WHEN v_sal<1500 THEN
UPDATE EMPNEW SET COMM=NVL(COMM,0)+100 WHERE EMPNO=v_empno;
WHEN v_sal<2500 THEN
UPDATE EMPNEW SET COMM=NVL(COMM,0)+80 WHERE EMPNO=v_empno;
WHEN v_sal<5000 THEN
UPDATE EMPNEW SET COMM=NVL(COMM,0)+50 WHERE EMPNO=v_empno;
COMMIT;
END CASE;
END;
七、循环语句
1.基本循环
语法格式:
LOOP
statement1;
......
END LOOP;
示例:
--循环打印数字1~10
DECLARE
v_n INT:=1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(v_n);
v_n:=v_n+1;--变量自增
EXIT WHEN v_n>10;--退出条件
END LOOP;
END;
2.WHILE循环
语法格式:
WHILE condition LOOP
statement1;
......
END LOOP;
示例:
DECLARE
v_n INT:=1;
BEGIN
WHILE v_n <=10 LOOP
DBMS_OUTPUT.PUT_LINE(v_n);
v_n:=v_n+1;
END LOOP;
END;
3.FOR循环
语法格式:
FOR counter in [REVERSE] lower_bound ...upper_bound LOOP
statement1;
......
END LOOP;
注意:步长默认为1,且不需要事先定义变量,REVERSE反相打印
示例:
BEGIN
FOR i in 1..10 LOOP
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;
BEGIN
FOR i IN REVERSE 1..10 LOOP
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;
八、嵌套循环和退出语句
1.嵌套循环
嵌套循环是指在一个循环语句中嵌入两一个循环语句,标号用于标记嵌套块或嵌套玄幻,使用<<label_name>>定义标号
DECLARE
v_result INT;
BEGIN
<<outter>>
FOR i IN 1..5 LOOP
<<inter>>
FOR j IN 1..4 LOOP
v_result:=i;
EXIT outter WHEN i=3;
DBMS_OUTPUT.PUT_LINE(i||j);
END LOOP;
DBMS_OUTPUT.PUT_LINE('内:'||v_result);--整个内循环结束一次记录一次i的值
END LOOP;
DBMS_OUTPUT.PUT_LINE('外:'||v_result);--当整个外循环全部结束后才会执行,此时就是结束外循环时j的值
END;
2.退出语句
- EXIT和EXIT WHEN语句
- EXIT语句用于直接退出当前循环
- EXIT WHEN语句用于在满足特定条件时退出当前循环
DECLARE
v_cnt INT:=1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(v_cnt);
EXIT WHEN v_cnt=10;
v_cnt:=v_cnt+1;
END LOOP;
END;
- CONTINUE和CONTINUE WHEN语句
- CONTINUE语句用于直接结束当前循环并继续下一组循环
- CONTINUE WHEN语句用于在满足特定条件时结束当前循环并继续下一组循环
DECLARE
v_cnt INT:=0;
BEGIN
LOOP
v_cnt:=v_cnt+1;
CONTINUE WHEN v_cnt=5;
DBMS_OUTPUT.PUT_LINE(v_cnt);
EXIT WHEN v_cnt=10;
END LOOP;
END;
九、顺序语句
1.GOTO语句
GOTO语句用于跳转到特定标号处执行语句
GOTO label_name;
注意当桑勇GOTO跳转到特定标号时,标号后面至少要包含一条执行语句
比较繁琐,减少使用
DECLARE
v_cnt INT:=1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(v_cnt);
IF v_cnt=10 THEN
--EXIT;
GOTO end_loop;
END IF;
v_cnt:=v_cnt+1;
END LOOP;
<<end_loop>>
DBMS_OUTPUT.PUT_LINE('循环结束');--标号后面至少一条语句
END;
2.NULL语句
NULL语句不会执行任何操作,并且会直接将控制传递到下一个语句,使用该语句的主要目的是提高PLSQL的可读性
--根据输入的员工号,判断员工的工资,如果工资小于3000,将该员工的不止加工资的2%,并打印输出某员工的奖金更新了
DECLARE
v_name EMPNEW.ENAME%TYPE;
v_sal EMPNEW.SAL%TYPE;
v_empno EMPNEW.EMPNO%TYPE:=7369;
BEGIN
SELECT ename,sal INTO v_name,v_sal from EMPNEW WHERE empno=v_empno;
IF v_sal<3000 THEN
UPDATE EMPNEW set COMM=nvl(comm,0)+sal*0.2 WHERE ename=v_name;
COMMIT;
DBMS_OUTPUT.PUT_LINE(v_name||'的奖金更新了');
ELSE
NULL;--没有任何操作
END IF;
END;