目录儿
- 常用指令
- 1. 什么是PLSQL语言
- 2. PLSQL程序结构
- 2.1 第一个程序 HelloWord:
- 2.2 执行程序
- 2.2.1 在工具中执行
- 2.2.2 在sqlplus客户端中执行(命令行)
- 3. 变量
- 3.1 普通变量
- 3.2 引用型变量
- 3.3 记录型变量
- 4. 流程控制
- 4.1 条件分支
- 4.2 循环
- 5. 游标
- 5.1 定义
- 5.2 语法
- 5.3 游标的属性
- 5.4 创建和使用
- 5.5 带参游标
- 6. 存储过程⭐
- 6.1 概念
- 6.2 语法
- 6.3 无参存储
- 6.4 带输入参数的存储过程
- 6.5 带输出参数的存储过程
- 6.6 异常处理
Oracle
数据库封装了
PLSQL
语言。
常用指令
# cmd打开 sqlplus 客户端
sqlplus /nolog
# 以 DBA 角色连接
conn /as sysdba;
1. 什么是PLSQL语言
PLSQL
全称Procedure Language SQL
,是Oracle
对SQL
的一种过程化拓展,在SQL
命令语言中增加了过程处理语句(如分支,循环等),是SQL
语言具有过程处理的能力。
把复杂的业务逻辑放到数据库中处理,能够减少数据库和服务之间的网络交互,提高执行效率。
PLSQL
是一种不区分大小写的语言(大小写不敏感)
2. PLSQL程序结构
一个程序分为三个部分:
DECLARE
--声明(变量,游标)
BEGIN
-- 执行体(业务逻辑)
-- [异常处理]
END;
其中
DECLARE
部分在不用声明变量、游标的情况下可以省略。
2.1 第一个程序 HelloWord:
DECLARE
--Local variables here
I INTEGER:
BEGIN
--在`DBMS Output`窗口打印 "hello world"
DBMS_OUTPUT.PUT_LINE ('hello world');
END;
DBMS_OUTPUT
是Oracle
内置的程序包,.PUT_LINE()
是包内函数
2.2 执行程序
2.2.1 在工具中执行
在工具中,直接点击编辑窗口的执行按钮即可。
2.2.2 在sqlplus客户端中执行(命令行)
在命令行中,执行程序需要在程序最后输入/
表示程序结束,这样回车才会执行程序,否则会认为程序还没结束而换行。
连接到:
Oracle Database 10g Express Edition Release 10.2.0.1.0-Production
SQL> begin
2 DBMS_OUTPUT.PUT_LINE('HELLO WORLD');
3 END;
4 /
HELLO WORLD
PL/SQL过程已成功完成。
默认情况下,sqlplus
客户端默认关闭打印输出,因此没有显示打印结果,可以通过命令set serveroutput on
开启:
SQL> set serveroutput on
SQL> begin
2 DBMS_OUTPUT.PUT_LINE('HELLO WORLD');
3 END;
4 /
HELLO WORLD
PL/SQL 过程已成功完成。
3. 变量
- 普通数据类型:char、varchar2、date、number、boolean、long
- 特殊变量类型:引用型变量、记录型变量
变量声明格式:
变量名 变量类型(变量长度)
例如:v_name varchar2(20);
变量赋值格式:
- 直接赋值:
v_name := 'zhangsan'
- 语句赋值:
select 值 into 变量
3.1 普通变量
【示例】打印人员个人信甚,包括:姓名、薪水、地址
DECLARE
-- 姓名
v_name VARCHAR2(20) := 'Haydon';
-- 薪水
v_sal NUMBER;
-- 地址
v_addr VARCHAR2(200);
BEGIN
--直接赋值
v_sal := 8888;
--语句赋俏
SELECT 'xxx' INTO v_addr FROM DUAL;
--打印输出
DBMS_OUTPUT.PUT_LINE('姓名:'|| v_name ||'薪水:'|| v_sal ||'地址:' || v_addr);
END;
执行结果:
3.2 引用型变量
变量的类型和长度取决于表中字段的类型和长度
通过表名.列名%TYPE
指定变量的类型和长度,例如:v_name emp.ename%TYPE;
【示例】查询emp表中7839号员工的个人信息,打印姓名和薪水
DECLARE
v_name emp.ename&TYPE;
v_sal emp.sal&TYPE;
BEGIN
SELECT ENAME,SAL INTO v_name,v_sal FROM EMP WHERE EMPNO = 7839;
--打印输出
DBMS_OUTPUT.PUT_LINE('姓名:'|| v_name ||'薪水:'|| v_sal ||'地址:' || v_addr);
END;
引用型变量的好处:
使用普通变量定义方式,需要知道表中列的类型,而使用弱引用类型,不需要考虑列的类型,使用%TYPE
是非常好的编程风格,因为它使得PL/SQL
更加灵活,更加适应于对数据库定义的更新。
3.3 记录型变量
接受表中的一整行记录,相当于Java中的一个对象
语法:变量名称 表名%ROWTYPE
,例如:v_emp emp%ROWTYPE;
【示例】查询并特打印7839号员工的姓名和薪水
DECLARE
-- 记录型变量
v_emp emp%ROWTYPE;
BEGIN
SELECT * INTO v_name,v_sal FROM EMP WHERE EMPNO = 7839;
--打印输出
DBMS_OUTPUT.PUT_LINE('姓名:'|| v_emp.ename ||'薪水:'|| v_emp.sal ||'地址:' || v_emp.addr);
END;
- 如果有一个表,有100个字段,那么你程序如果要使用这100字段话,如果你使用弱引用型变量一个个声明,会特别麻烦,记录型变量可以方便的解决这个问题
- 因为记录型变量是存储一行数据的变量,因此不能指定查询字段,要用
*
,否则报错- 因为记录型变量是存储一行数据的变量,因为查询的结果不能
>1
,否则报错
4. 流程控制
4.1 条件分支
BEGIN
IF 条件1 THEN 执行1
ELSIF 条件2 THEN 执行2
ELSE 执行3
END IF;
END;
注意
ELSIF
是少一个e
的。
【示例】
4.2 循环
loop循环:
【示例】打印1~10
DECLARE
--声明循环变量
V_NUM NUMBER :1;
BEGIN
LOOP
EXIT WHEN V_NUM > 10;
DBMS_OUTPUT.PUT_LINE (V_NUM);
--循环变量的自增
V_NUM := V_NUM + 1;
END LOOP;
END:
5. 游标
5.1 定义
用于临时存储一个查询返回的多行数据(结果集,类似于Java的Jdbc连接返回的ResultSet集合),通过遍历游标,可以逐行访问处理该结果集的数据。
游标的使用方式:声明 一> 打开 一> 读取 一> 关闭
5.2 语法
声明游标:CURSOR 游标名[(参数列表)] IS 查询语句;
开启游标:OPEN 游标名;
游标取值:FETCH 游标名 NTO 变量列表;
关闭游标:CLOSE 游标名;
5.3 游标的属性
属性 | 返回值类型 | 说明 |
---|---|---|
%ROWCOUNT | 整形 | 获得 FETCH 语句返回的数据行数 |
%FOUND | 布尔型 | 最近的 FETCH 语句能拿到数据则为真,否则为假 |
%NOTFOUND | 布尔型 | 与 %FOUND 属性返回值相反 |
%ISOPEN | 布尔型 | 游标已经打开时值为真,否则为假 |
其中
%NOTFOUND
是在游标中找不到元素的时候返回TRUE
,通常用来判断退出循环,他的默认值是false
,因此用%NOTFOUND
作判断之前,应该先执行 FETCH 语句使%NOTFOUND
带上真实的值。
5.4 创建和使用
【示例】使用游标查询 emp 表中所有员工的姓名和工资,并将其依次打印出来。
-- 使用游标查诡m表中所有员工的名和工资,并将其依次打印出来。
DECLARE
-- 声明游标
CURSOR c_emp IS SELECT ename,sal FROM emp;
-- 声明变量接受游标中的数据
v_ename emp.enames%TYPE;
v_sal emp.sal%TYPE;
BEGIN
-- 打开游标
OPEN c_emp;
-- 遍历游标
LOOP
--获取游标中的数据
FETCH c_emp INTO v_ename,v_sal;
--退出循环条件
EXIT WHEN c_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_ename || '-' || v_sal);
END LOOP;
--关闭游标
CLOSE c_emp;
END;
5.5 带参游标
【示例】使用游标查询并打印某部门的员工的姓和薪资,部门编号为运行时手动输入。
-- 使用游标查诡m表中所有员工的名和工资,并将其依次打印出来。
DECLARE
-- 声明含参游标
CURSOR c_emp(v_deptno emp.deptno$TYPE) IS SELECT ename,sal FROM emp WHERE deptno = v_deptno;
-- 声明变量接受游标中的数据
v_ename emp.enames%TYPE;
v_sal emp.sal%TYPE;
BEGIN
-- 打开游标,同时传入参数
OPEN c_emp(10);
-- 遍历游标
LOOP
--获取游标中的数据
FETCH c_emp INTO v_ename,v_sal;
--退出循环条件
EXIT WHEN c_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_ename || '-' || v_sal);
END LOOP;
--关闭游标
CLOSE c_emp;
END;
6. 存储过程⭐
6.1 概念
之前我们编写的PLSQL
程序可以进行表的操作,判断,循环逻辑处理的工作,但无法重复调用。
可以理解之前的代码全都编写在了main
方法中,是匿名程序。JAVA可以通过封装对像和方法来解决程序的复用问题。
PLSQL
是将一个个PLSQL
的业务处理过程存储起来进行复用,这些被存储起来的PLSQL程序
称之为 存储过程
存储过程作用:
- 在开发程序中,为了一个特定的业务功能,会向数据库进行多次连接关闭连接和关闭是很耗费资源),需要对数据库进行多次
I/O
读写,性能比较低。如果把这些业务放到PLSQL
中,在应用程序中只需要调用PLSQL
就可以做到连接关闭一次数据库就可以实现我们的业务,可以大大提高效率。 ORACLE
官方给的建议:能够让数据库操作的不要放在程序中 。在数据库中实现基本上不会出现错误,在程序中操作可能会存在错误.(如果在数据库中操作数据,可以有一定的日志恢复等功能.)
很奇怪,因为之前没有用过
Oracle
数据库,通常都是使用Mysql
配合Mybatis
/Mybatis puls
做持久层操作,连接数据库就是做一些比较基础的增删改查,遍历封装什么的存储过程都放到应用程序的service
里面做,说是减少数据库的负担云云。
·
而Oracle
官方却提倡把存储过程复杂业务放到数据库中做,减少连接次数,提升效率,保障安全。
·
在使用Oracle
的时候,Oracle
是推荐尽可能将所有的操作都放在Plsql
中的,但是在使用Mysql
的时候是不让使用存储过程的,为什么?
·
下面这个回答感觉是这种分歧的主因:
·
因为在Mysql
中,存储过程不是一次编译的,而是对每个会话在执行的时候编译一次,在会话结束的时候抛弃,而且在执行alter
/procedure
的时候是会将所有会话对这个存储过程持有的存储过程的版本抛弃掉。同时没有包的概念,没有类库,没有面向对象的概念,所以相对Oracle
来说,Mysql
额存储过程不是很好的选择。
6.2 语法
CREATE OR REPLACE PROCEDURE 过程名称[(参数列表)] IS
BEGIN
-- 业务
END [过程名称];
根据参数的类型,我们将其分为3类:
- 不带参数的
- 带输入参数的
- 带输入输出参数(返回值)的
6.3 无参存储
创建存储过程(这里用的Oracle Sql Developer)
在过程文件夹右键 → 新建过程
选择方案,输入名称
编辑过程代码
CREATE OR REPLACE PROCEDURE PRO_HELLO IS
--声明变量
BEGIN
dbms_output.put_line ('hello world');
END PRO_HELLO;
注意
第一个问题:is
和as
是可以互用的,用哪个都没关系
第二个问题:过程中可以不用declare
关键字
保存即可
直接测试:
如果是在sqlplus
上用 exec
指令调用:
C:\Users\82009816.GZCASCADE>sqlplus
SQL*Plus: Release 19.0.0.0.0 - Production on 星期三 5月 10 10:48:34 2023
Version 19.3.0.0.0
Copyright (c) 1982, 2019, Oracle. All rights reserved.
请输入用户名: DMG
输入口令:
上次成功登录时间: 星期三 5月 10 2023 10:48:16 +08:00
连接到:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
SQL> set serveroutput on;
SQL> exec pro_hello
hello world
PL/SQL 过程已成功完成。
6.4 带输入参数的存储过程
【示例】查询并用打印某个员工(如7839号员工)的姓名和薪水。要求,调用的时候传入员工编号,自动控制台打印,
CREATE OR REPLACE PROCEDURE P_QUERYNAMEANDSAL(I_EMPNO IN EMP.EMPNO%TYPE) IS
--声明变量接授查询结果
V_ENAME EMP.ENAME%TYPE;
V_SAL EMP.SAL%TYPE;
BEGIN
--根据用户传递的员工号查询姓名和蕲水
SELECT ENAME,SAL INTO V_ENAME,V_SAL FROM EMP WHERE EMPNO = I_EMPNO;
--打印结果
DBMS_OUTPUT.PUT_LINE('姓名:' || V_ENAME || ',薪水:'|| V_SAL);
END P_QUERYNAMEANDSAL;
参数包括
IN
OUT
INOUT
三种,对应单纯输入
、单纯输出
、输入输出
作用。
6.5 带输出参数的存储过程
【示例】输入员工号查询某个员工(7839号员工)信息,要求,将薪水作为返回值输出,给调用的程序使用
--输入员工号查询某个员工(7839号员工)信息,要求,将薪水作为返回值输出,给调用的程序使用
CREATE OR REPLACE PROCEDURE P_QUERYSAL_OUT(
I_EMPNO IN EMP.EMPNO%TYPE,
O_SAL OUT EMP.SAL%TYPE) IS
BEGIN
SELECT SAL INTO O_SAL FROM EMP WHERE EMPNO = I_EMPNO;
END P_QUERYSAL_OUT;
PLSQL工具调用:
--Created on 2018/3/22 by ADMINISTRATOR
declare
--明变量接受存储过军中的榆出参数
v_sal emp.sal&TYPE;
BEGIN
p_querysal_out(7839,v_sal)
dbms_output.put_line(v_sal):
END;
6.6 异常处理
在一个procedure
中,通常都包含着异常处理部分
我们可以声明自己的异常,并在处理过程中手动抛出,然后在异常捕捉中做相应的处理。
【示例】
PROCEDURE pro_myprocedure_test (v_status OUT VARCHAR2) AS
-- 声明自定义异常
MYEXCEPTION_ONE EXCEPTION;
MYEXCEPTION_TWO EXCEPTION;
MYEXCEPTION_THREE EXCEPTION;
BEGIN
IF 条件1
THEN RAISE EXC_REQUIRED_PARAM_EMPTY; -- 抛出自定义异常
END IF;
IF 条件2 THEN RAISE EXC_DELIVERY_COMPLETED; -- 抛出自定义异常
ELSIF 条件3 THEN RAISE EXC_DELIVERY_CANCELED; -- 抛出自定义异常
END IF;
v_status := 0;
-- 异常捕捉处理
EXCEPTION
WHEN MYEXCEPTION_ONE THEN -- 处理 MYEXCEPTION_ONE 异常
ROLLBACK;
v_status := 1;
WHEN MYEXCEPTION_TWO THEN -- 处理 MYEXCEPTION_TWO 异常
ROLLBACK;
v_status := 2;
WHEN MYEXCEPTION_THREE THEN -- 处理 MYEXCEPTION_THREE 异常
ROLLBACK;
v_status := 3;
WHEN OTHERS THEN -- 其他异常
ROLLBACK;
v_status := 4;
END pro_myprocedure_test;
参考资料:
哔哩哔哩 丫丫5358 基于oracle数据库的PLSQL编程以及存储过程的创建和使用视频
https://www.bilibili.com/video/BV1Bb411E74d/?p=3&share_source=copy_web&vd_source=e22a75a6781804ee25ce46a2afc235ce