oracle(sql)基础篇系列(五)——PLSQL、游标、存储过程、触发器
PL/SQL
PL/SQL 简介
每一种数据库都有这样的一种语言,PL/SQL 是在Oracle里面的一种编程语言,在Oracle内部使用的编程语言。我们知道SQL语言是没有分支和循环的,而PL语言是为了补充SQL语言的,是带有了分支和循环的语言。
PL/SQL 语法
基本数据类型声明
declare
v_name varchar2(20);
v_temp number(1);
v_count binary_integer := 0;
v_sal number(7,2) := 4000.00;
v_date date := sysdate;
v_pi constant number(3,2) := 3.14;
v_valid boolean := false;
v_name varchar2(20) not null := 'myname';
declare开头声明变量,v_name表示变量名字,通常以v_xxx这种格式命名变量,varchar2(20)表示变量类型, :=为赋值操作符。
在PL/SQL里面boolean类型变量在定义的时候一定要给初始值,Oracle 里面的put_line()不能打印boolean类型的值。
%type属性声明
还有一种声明变量的方法:使用%type属性。
declare
v_empno number(4);
v_empno2 emp.empno%type;
v_empno3 v_empno2%type;
begin
dbms_output.put_line('Test');
end;
使用emp表中empno字段的类型,这种方式的好处是PL/SQL声明的变量类型随着表字段类型的变化而变化。dbms_output.put_line('Test');表示输出Test。
table类型声明
table类型类似java中的数组类型。
declare
type type_table_emp_empno is table of emp.empno%type index by binary_integer;
v_empnos type_table_emp_empno;
begin
v_empnos(0) := 7369;
v_empnos(2) := 7839;
v_empnos(-1) := 9999;
dbms_output.put_line(v_empnos(-1));
end;
--type开头
--类型名type_table_emp_empno
--is table of 表示table数据类型,相当于java里面的数组Array[]
--emp.empno%type表示该类型数据装的是emp表中empno类型的数据
--index by binary_integer 表示下表
--v_empnos type_table_emp_empno;使用type_table_emp_empno类型声明变量v_empnos
record数据类型声明
declare
type type_record_dept is record
(
deptno dept.deptno%type,
dname dept.dname%type,
loc dept.loc%type
);
v_temp type_record_dept;
begin
v_temp.deptno := 50;
v_temp.dname := 'aaaa';
v_temp.loc := 'bj';
dbms_output.put_line(v_temp.deptno || ' ' || v_temp.dname);
end;
/
--type type_record_dept is record声明type_record_dept是record类型。
--该类型中有三个属性deptno ,dname ,loc ,类型分别为dept表中deptno ,dname ,loc 三个字段的类型。record类型类似java中的类,record类型可以存储一条记录。
-- v_temp type_record_dept;使用type_record_dept类型声明变量v_temp。
%rowtype属性声明
另一种声明record变量的方法:
declare
v_temp dept%rowtype;
begin
v_temp.deptno := 50;
v_temp.dname := 'aaaa';
v_temp.loc := 'bj';
dbms_output.put_line(v_temp.deptno || ' ' || v_temp.dname);
end;
/
使用%rowtype声明record类型的变量,v_temp 的属性和dept表的字段保持一致,这种方式的好处是PL/SQL声明的变量类型随着表字段类型的变化而变化。
异常处理
begin
v_num := 2/v_num;
dbms_output.put_line(v_num);
exception
when others then//固定写法
dbms_output.put_line('error');
end;
exception定义异常处理,紧跟“when others then”为固定写法。
SQL> declare
2 v_num number := 0;
3 begin
4 v_num := 2/v_num;
5 dbms_output.put_line(v_num);
6 exception
7 when others then
8 dbms_output.put_line('error');
9 end;
10 /
error
PL/SQL 过程已成功完成。
SQL>
其他类型的异常
--返回记录太多异常
declare
v_temp number(4);
begin
select empno into v_temp from emp where deptno = 10;
exception
when too_many_rows then
dbms_output.put_line('太多记录了');
when others then
dbms_output.put_line('error');
end;
--没有记录异常
declare
v_temp number(4);
begin
select empno into v_temp from emp where empno = 2222;
exception
when no_data_found then
dbms_output.put_line('没数据');
end;
PL/SQL的DML语句
select语句
PL/SQL里面的selec t语句必须和into语句一块用并且有且只有一条记录。
--将编号为7369的员工的员工编号和薪水查询出来并存储到v_empno,v_sal并输出
declare
v_empno emp.empno%type;
v_sal emp.sal%type;
begin
select empno,sal into v_empno,v_sal from emp where empno=7369;
dbms_output.put_line(v_empno || '-' || v_sal);
end;
--将编号为7369的员工的记录查询出来并存储到v_emp,并输出员工编号和薪水
declare
v_emp emp%rowtype;
begin
select * into v_emp from emp where empno=7369;
dbms_output.put_line(v_emp.empno || '-' || v_emp.sal);
end;
insert语句
--向dept表中插入一条数据
declare
v_deptno dept2.deptno%type := 50;
v_dname dept2.dname%type := 'dname';
v_loc dept2.loc%type := ‘ckg’;
begin
insert into dept2 values (v_deptno, v_dname, v_loc);
commit;
end;
和sql语句唯一不同的是采用了pl/sql变量。update与delete语句和sql语句一样。
PL/SQL的DDL语句
create语句
begin
execute immediate 'create table stu2(id number(10),name varchar2(20) default ''zhangsan'')';
end;
PL/SQL编写ddl语句和SQL语句不同的是需要加execute immediate ,单引号中的sql语句使用双单引号指定缺省值,如''zhangsan''。alter语句,drop语句同理。
PL/SQL的分支循环语句
判断语句
declare
v_sal emp.sal%type;
begin
select sal into v_sal from emp where empno = 7369;
if(v_sal < 1200) then
dbms_output.put_line('low');
elsif(v_sal < 2000) then
dbms_output.put_line('middle');
else
dbms_output.put_line('high');
end if;
end;
注意红色语法部分即可。
循环语句
declare
i binary_integer := 1;
begin
loop
dbms_output.put_line(i);
i := i+1;
exit when ( i>=11);
end loop;
end;
注意红色语法部分即可。上面的循环相当于java里的 do-while 循环。
declare
j binary_integer := 1;
begin
while j < 11 loop
dbms_output.put_line(j);
j := j + 1;
end loop;
end;
注意红色语法部分即可。上面的循环相当于java里的while 循环。
begin
for k in 1..10 loop
dbms_output.put_line(k);
end loop;
--逆序
for k in reverse 1..10 loop
dbms_output.put_line(k);
end loop;
end;
注意红色语法部分即可。上面的循环相当于java里的增强 for 循环。
游标
我们知道,select语句的结果集是一张表,如果我们想对结果集逐条记录遍历该如何实现,就像java中的迭代器一样?PL/SQL提供了解决遍历结果集的的功能:游标。游标是指在结果集上的指针,通过游标可以对select语句的结果集逐条记录遍历。
显示游标与隐式游标
oracle中的游标分为显示游标和隐式游标。显示游标是用cursor...is..命令定义的游标,它可以对查询语句(select)返回的多条记录进行处理。显式游标的操作:打开游标、操作游标、关闭游标。
隐式游标由Oracle数据库自动创建,名称是sql ,主要用途是可以返回一个操作是否成功或失败,只能用于DML语句。PL/SQL隐式地打开SQL游标,并在它内部处理SQL语句,然后关闭它。
游标属性
访问游标对象的属性方法:游标对象%游标属性。游标具有的属性如下:
%notfound 没有结果集
%found存在结果集
%rowcount 返回受影响的行数
%isopen询问游标是否已经打开
sql%rowcount可以统计刚执行的sql语句影响了多少条记录。
declare
v_deptno dept2.deptno%type := 50;
v_dname dept2.dname%type := 'dname';
v_loc dept2.loc%type := ‘can’;
begin
update dept2 set loc = 'sha' where deptno = 10;
dbms_output.put_line (sql%rowcount || '条记录被影响');
commit;
end;
--输出
1条记录被影响
循环游标
declare
cursor c is --声明游标指向select的结果集
select * from emp;
v_emp c%rowtype;
begin
open c; --打开游标
loop
fetch c into v_emp; --取出游标当前执向的值存入v_emp,每fetch一次,游标指向下一条记录
exit when (c%notfound); --找不到就退出
dbms_output.put_line(v_emp.ename);
end loop;
close c; --关闭游标
end;
--输出
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER
可见,使用PL/SQL的游标和循环的结合,实现了对select结果集的遍历。
for循环游标
declare
cursor c is
select * from emp;
begin
for v_emp in c loop
dbms_output.put_line(v_emp.ename);
end loop;
end;
for循环中使用游标,不需要声明 v_emp变量,for开始的时候自动声明v_emp;不需要打开关闭游标;不需要每次fetch。因此,通常采用此写法。
带参数的游标
declare
cursor c(v_deptno emp.deptno%type, v_job emp.job%type) is
select ename, sal from emp where deptno = v_deptno and job = v_job;
begin
for v_temp in c (30, 'CLERK') loop
dbms_output.put_line(v_temp.ename);
end loop;
end;
注意红色语法部分即可。从这里我们也可以看出,实际上,真正fetch的时候,PL/SQL才回去数据库查询数据。
可更新的游标
declare
cursor c is
select * from emp2 for update;
begin
for v_temp in c loop
if(v_temp.sal < 2000) then
update emp2 set sal = sal * 2 where current of c;
elsif (v_temp.sal = 5000) then
delete from emp2 where current of c;
end if;
end loop;
注意红色语法部分即可。for update声明游标是更新用的,current of c 更新或者删除时指明是当前游标指向的记录。
存储过程
存储过程的创建
declare
cursor c is
select * from emp2 for update;
begin
for v_temp in c loop
if(v_temp.sal < 2000) then
update emp2 set sal = sal * 2 where current of c;
elsif (v_temp.sal > 2000) then
update emp2 set sal = sal / 2 where current of c;
end if;
end loop;
end;
对于上面这段PL/SQL代码,如果我们需要经常执行,可以将这段代码创建成存储过程,如下:
create or replace procedure p is
cursor c is
select * from emp2 for update ;
begin
for e in c loop
if(e.sal < 2000) then
update emp2 set sal = sal * 2 where current of c;
elsif(e.sal > 2000) then
update emp2 set sal = sal / 2 where current of c;
end if;
end loop;
end;
创建存储过程和普通的PL/SQL代码不同的是将“declare”改为“create or replace procedure p is ”,其他保持不变。
调用存储过程
--命令方式
Procedure created
SQL> exec p;
PL/SQL procedure successfully completed
SQL>
--另一种方式
begin
p;
end;
带参数的存储过程
create or replace procedure p
(v_a in number, v_b number, v_ret out number, v_temp in out number)
is
begin
if(v_a > v_b) then
v_ret := v_a;
else
v_ret := v_b;
end if;
v_temp := v_temp + v_a;
end;
--in 叫做传入参数,调用者负责给v_a赋值
--out 叫做传出参数,存储过程是没有返回值的,它就借助于传出参数
-- v_b 中间什么都没写,默认是in,是接收参数用的
-- v_temp 既可以接收,又可以传出
调用过程
declare
v_a number := 3;
v_b number := 4;
v_ret number;
v_temp number := 5;
begin
p(v_a, v_b, v_ret, v_temp);
dbms_output.put_line(v_ret);
dbms_output.put_line(v_temp);
end;
需要注意的是,执行存储过程并不会直接显示错误,可以使用show error命令显示编译错误。
删除存储过程
drop procedure p;
使用存储过程求emp表的树状结构
求出每个员工的上级(经理)并输出
create or replace procedure p_emp(v_empno emp.empno%type,v_level binary_integer) is
cursor c is
select * from emp where mgr = v_empno;
v_str varchar2(256) := '';
begin
for i in 1..v_level loop
v_str := v_str || ' ';
end loop;
for emp in c loop
dbms_output.put_line(v_str || emp.ename);
p_emp(emp.empno,v_level+1);--递归调用存储过程
end loop;
end;
--求出没有经理的员工
declare
v_emp emp%rowtype;
begin
select * into v_emp from emp where mgr is null;
dbms_output.put_line(v_emp.ename);
p_emp(v_emp.empno, 1);
end;
--或者我们已经知道7839是没有上级的
begin
p_emp(7839, 1);
end;
--输出
KING
JONES
SCOTT
ADAMS
FORD
SMITH
BLAKE
ALLEN
WARD
MARTIN
TURNER
JAMES
CLARK
MILLER
触发器
当对某一张表进行增删改查操作的时候,触发其他操作。
触发器的创建
--创建一张记录操作表
create table emp2_log-- emp2这张表的操作记录
(
uname varchar2(20),--用户
action varchar2(10),--操作
atime date--操作时间
);
--创建一个队emp2表操作的触发器
create or replace trigger trig
after insert or delete or update on emp2 for each row --表示每更新一条记录都会生成一条操作记录
--after可以改为before
begin
if inserting then
insert into emp2_log values(USER, 'insert', sysdate);--USER关键字,代表当前用户是谁
elsif updating then
insert into emp2_log values(USER, 'update', sysdate);
elsif deleting then
insert into emp2_log values(USER, 'delete', sysdate);--记录到log文件中
end if;
end;
触发器的执行
触发器不能直接执行,必须指明在哪张表上面执行哪些操作的时候才能触发触发器。现在对emp2表进行插入操作并查看emp2_log表
SQL> insert into emp2(ename,deptno) values('lisi',20);
SQL> select * from emp2_log;
UNAME ACTION ATIME
-------------------- ---------- -----------
SCOTT insert 2016/10/10
SCOTT insert 2016/10/10
SQL>
可见对emp2表的插入操作触发了日志记录操作。
触发器的删除
drop trigger trig;
基础篇到此。
oracle(sql)基础篇系列(五)——PLSQL、游标、存储过程、触发器的更多相关文章
- 【目录】mysql 基础篇系列
随笔分类 - mysql 基础篇系列 mysql 开发基础系列22 SQL Model(带迁移事项) 摘要: 一.概述 与其它数据库不同,mysql 可以运行不同的sql model 下, sql m ...
- 小猪猪C++笔记基础篇(五)表达式、语句
小猪猪C++笔记基础篇(五) 关键词:表达式.语句 本章的内容比较简单,基本上没有什么理解上的困难,都是知识上的问题.先开始想要不要写呢,本来是不准备写的,但是既然读了书就要做笔记,还是写一写,毕竟还 ...
- 撸基础篇系列,JAVA的NIO部分
前言:撸基础篇系列,避免每次都要从头开始看,写个自己的知识体系树 NIO 核心就是异步, 比如,复制文件,让操作系统去处理,等通知 BIO核心类 一,BIO NIO基本操作类 Bytebuffer 构 ...
- java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别
java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别 目录 java基础解析系列(一)---String.StringBuffer.St ...
- python基础篇(五)
PYTHON基础篇(五) 算法初识 什么是算法 二分查找算法 ♣一:算法初识 A:什么是算法 根据人们长时间接触以来,发现计算机在计算某些一些简单的数据的时候会表现的比较笨拙,而这些数据的计算会消耗大 ...
- oracle(sql)基础篇系列(五)——PLSQL、游标、存储过程、触发器
PL/SQL PL/SQL 简介 每一种数据库都有这样的一种语言,PL/SQL 是在Oracle里面的一种编程语言,在Oracle内部使用的编程语言.我们知道SQL语言是没有分支和循环的,而PL语 ...
- oracle(sql)基础篇系列(一)——基础select语句、常用sql函数、组函数、分组函数
花点时间整理下sql基础,温故而知新.文章的demo来自oracle自带的dept,emp,salgrade三张表.解锁scott用户,使用scott用户登录就可以看到自带的表. #使用ora ...
- oracle(sql)基础篇系列(一)——基础select语句、常用sql函数、组函数、分组函数
花点时间整理下sql基础,温故而知新.文章的demo来自oracle自带的dept,emp,salgrade三张表.解锁scott用户,使用scott用户登录就可以看到自带的表. #使用oracle用 ...
- 《手把手教你》系列基础篇(五)-java+ selenium自动化测试- 创建首个自动化脚本(详细教程)
1.简介 前面几篇宏哥介绍了两种(java和maven)环境搭建和三大浏览器的启动方法,这篇文章宏哥将要介绍第一个自动化测试脚本.前边环境都搭建成功了,浏览器也驱动成功了,那么我们不着急学习其他内容, ...
随机推荐
- 【IDEA&&Eclipse】5、IntelliJ IDEA常见配置
[idea配置jdk] http://blog.csdn.net/tolcf/article/details/50803414 [idea intellij 如何配置tomcat]http://jin ...
- Python编程-从入门到实践 Eric Matthes 著 袁国忠 译 - - 第二章 动手试一试
因为第一章的动手试一试基本都是探索性的,所以直接进入第二章. # 2.2 动手试一试 # 2_1 简单消息: 将一条消息存储到变量中,再将其打印出来. message = 'python 编程从入门到 ...
- python中文编码&json中文输出问题
python2.x版本的字符编码有时让人很头疼,遇到问题,网上方法可以解决错误,但对原理还是一知半解,本文主要介绍 python 中字符串处理的原理,附带解决 json 文件输出时,显示中文而非 un ...
- 【读书笔记】iOS-加速计与陀螺仪
一,数据的“滤波” 直接从加速度计获得的原始数据,往往不能直接使用,而是需要去除一些干扰数据,这个过程称为“滤波”.“滤波”一词来源于无线电技术中对无线电信号的处理过程.事实上从数学角度而言它们是一样 ...
- vue-cli脚手架之webpack.base.conf.js
webpack相关的重要配置文件将在这一节给出.webpack水很深啊^o^,在此先弄清楚原配文件内容的含义,后续可以自己根据实际情况配置. webpack.base.conf.js:配置vue开发环 ...
- 基础篇|一文搞懂RNN(循环神经网络)
基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...
- Salesforce的Developer Console简介
Developer Console是Salesforce提供的一个基于浏览器的集成开发环境.在Developer Console中,开发者可以新建.修改各种Apex.Visualforce.Light ...
- JavaScript与正则表达式
正则表达式的定义 正则表达式与字符串对象相关的方法 相关示例 一.正则表达式(regular expression简称res) 1.定义: 一个正则表达式就是由普通字符以及特殊字符(称为元字符)组成 ...
- 安卓开发_计时器(Chronometer)的简单使用
计时器控件(Chronometer)是一个可以显示从某个起始时间开始一共过去多长时间的本文. 继承自TextView,以文本的形式显示时间内容 该组件有五个方法 1.setBase(): \\用于设置 ...
- 为什么 APM 能提升 IT 团队工作质量?
“有必要吗?”这是很多 IT 专业人员在尝试向团队内部推荐应用程序性能管理价值时所面临的问题.APM(应用程序性能管理)能为公司节约成本,提高内部工作效率,并真实了解用户对公司的系统和产品是否满意.除 ...