/**
事物
1.1事物是一个不可在分的逻辑操作单元,在整个逻辑单元的操作过程中,n条(insert,update,delete)sql语句
(注:select语句不改变表中内容,因此select不受限制)
要么全部执行成功,要么全部执行失败
1.2事务的实现
1.设置事务保存点:告诉服务器由哪里开始转账(回滚),也就是开启事务的位置
2.事务的结果处理
A.当全部操作成功的时候:commit(事务提交)提交是由于程序员的某种操作对数据有影响了,就要提交
B:当全部操作执行出现异常:roollback(事务回滚)
2.隐式事务:oracle中的insert,update,delete操作都会自动启动一个事务----隐式事务(也就是上面的提交按钮)
(如果不提交,在关闭服务器在打开服务器的时候东西就丢了。因为提交才会永久的保存。
如果没有点击提交按钮,当关闭当前页面的时候就默认自动保存了
)
3.事务的特点
3.1原子性:事务是最小的单元,不可再分
3.2持久性:对数据更改后提交,就永久保存了
3.3隔离性:事务之间互不影响
3.4并发性:同时可以开启多个事务
4.锁的概念:如果前面的事务没有提交,其他人不能操作
行级锁:防止两个事物同时修改相同的记录
表级锁:给表加锁
**/
create table bank(
bid number(3),
bname varchar2(30),
money number(5)
)
alter table bank add constraint ck_money check(money>1)
insert into bank values(111,',王八',100);
insert into bank values(222,'乌龟',200);
insert into bank values(333,',王八',100);
insert into bank values(444,'乌龟',200);
----甲还乙钱过程
update bank set money=money-100 where bid=111;----由于有检查约束,因此没有减去
update bank set money=money+100 where bid=222;----加了一百
----遇到这种情况,需要事物回滚。事物是一个不可在分的逻辑操作单元,因此上面这两个要一起操作
select * from bank;
declare
begin
savepoint bg;--保存点
update bank set money=money-100 where bid=111;
update bank set money=money+100 where bid=222;
dbms_output.put_line('转账成功');
commit;
exception
when others then
dbms_output.put_line('转账失败');
rollback to bg;-----回滚到保存点
end;
/**
第二部分:游标(自我理解:使用游标的目的就是为了遍历这个游标所对应的集合的内容)loop循环最常用
1.游标是指向某个数据集合(集合由select语句产生)的指针,也就是说游标指向某张虚拟表集合,游标保存的集合数据的地址
2.游标的作用:对虚拟表进行遍历
3.游标的使用:看题
4.游标的常用属性
%notfound:表示集合里是M没有数据,没有数据时候返回true
%rowcount:查看游标中有几个记录,前提是先遍历,遍历之后在使用。遍历之前遍历就会输出0
%found:有数据的时候返回true
%isopen:是否打开
5.系统自身还有一个游标---隐式游标,叫sql
隐式游标的isopen永远是false,因为执行完成之后立马关闭
**/
----第一种情况:loop循环遍历游标
---------------------------------------------------
select * from student;
declare
---1.声明一个游标,下面的意思游标是一个虚拟表的集合
cursor v_c is
select sno,sname from student;
-----2.遍历游标,要保存到变量里
v_sno number(6);
v_name varchar2(20);
begin
---3.open打开游标才能遍历 select sno into v_no from student;出错,因为一对多
open v_c;
----4.抓取游标中的数据,顺便遍历
loop
fetch v_c into v_sno ,v_name;---注意游标是按行抓取,所以不用写fecth sno into v_sno,然后把值付给后面的变量(如果想单独抓取某个列就那样写),抓取的是集合
if v_c%notfound then---没有数据就退出
exit;
end if;
dbms_output.put_line(v_sno||' '||v_name);---有数据就打印
dbms_output.put_line(v_c%rowcount); --查看游标中有几个记录,前提是先遍历,遍历之后在使用。遍历之前遍历就会输出0
end loop;
dbms_output.put_line(v_c%rowcount);
----5.关闭游标
close v_c;
end;
----------②.loop循环(自己对上面的题扩展)
declare
cursor v_c is select * from student;
v_r student%rowtype;
begin
open v_c;
loop
if v_c%notfound then
exit;
end if;
fetch v_c into v_r;--v_c就表示集合中的一行。抓取是从游标中抓取
dbms_output.put_line(v_r.sno||' '||v_r.sname);--有了这行数据,在.列名
end loop;
close v_c;
end;
-------③.loop循环(自己对上面的题扩展),游标是可以加参数的
declare
cursor v_c(v_ssex varchar2) is select sno,sname,ssex from student where ssex=v_ssex;
v_sno number(6);
v_sname varchar2(20);
v_ssex student.ssex%type;
begin
open v_c('男'); ----传实参
loop
if v_c%notfound then
exit;
end if;
fetch v_c into v_sno ,v_sname,v_ssex;
dbms_output.put_line(v_sno||' '||v_sname||' '||v_ssex);
end loop;
close v_c;
end;
-------④.loop循环(自己对上面的题扩展),游标可以封装成一个存储过程
create or replace procedure proc_cur
is
cursor v_c(v_ssex varchar2) is select sno,sname,ssex from student where ssex=v_ssex;
v_sno number(6);
v_sname varchar2(20);
v_ssex student.ssex%type;
begin
open v_c('男'); ----传实参
loop
if v_c%notfound then
exit;
end if;
fetch v_c into v_sno ,v_sname,v_ssex;
dbms_output.put_line(v_sno||' '||v_sname||' '||v_ssex);
end loop;
close v_c;
end;
----调用
begin
proc_cur;
end;
------------------------------------
--第二种情况:for循环遍历游标:loop循环必须打开关闭,for循环不用打开关闭
declare
cursor v_c is
select * from student;---声明游标
begin
for v_s in v_c loop ----对游标遍历,v_s表示一行。仿照高级for,高级for中的变量表示集合中的每个元素
---v_c:=v_s;----保存在游标中了,别的函数可以用这里的数据了
dbms_output.put_line(v_s.sno||' '||v_s.sname);---v_s表示表中的一行,因此这里直接用v_s调用这一行里的列(数据)
--insert into a values(v_s.sno,v_s.sname,v_s.sage);---拷贝过程。在student表中取出数据保存到临时表a中,就是把输出的内容写在values后面。a用完删掉。
end loop;
end;
------------②.for循环(自己的扩展)
declare
cursor v_c is
select * from student;---声明游标
v_r student%rowtype;---声明变量
begin
for v_r in v_c loop
dbms_output.put_line(v_r.sno||' '||v_r.sname);
end loop;
end;
------------③.for循环(自己的扩展),传参
declare
cursor v_c(v_ssex varchar2) is
select * from student;---声明游标
v_r student%rowtype;---声明变量
begin
for v_r in v_c('哈') loop ---如果在上面的select语句中不写where条件,在这里传入什么实参都会输出数据库中全部内容
dbms_output.put_line(v_r.sno||' '||v_r.sname||' '||v_r.ssex);
end loop;
end;
-------------------------------------
---隐式游标sql
declare
s number(6);
begin
select sno into s from student where sno=110;
update student set sage=30 where sno=110;
dbms_output.put_line(sql%rowcount);---距离他最近的sql语句的游标
end;
select * from student
-----------------------------------------
--第三部分:触发器(行级触发器是重点)
--1.当我们对电脑做任何事情,都是触发某个事件
---2步骤:
----1.触发源(谁触发)
--dcl(创建用户分配权限)触发器
--ddl(创建表,修改表结构)触发器
--dml(insert,uodate,delect。操作表中数据)触发器
--我们主要掌握dml触发器。他分为两种
--第一:语句级触发器(对整个表来说,执行insert语句,update语句和delect语句时候出发)
--第二:行触发器(对某个行来说)
----2.触发某个事件之后需要完成的功能
--3.触发器的两个属性
-----new:new.列名(获得当前操作的新的值,因此insert和update能用这个)
-----old :old.列名(获得当前操作的原来值。因此update和delete能用这个)
---4.触发器的用处:
--完成数据的自定义完整性(做任何操作,都能查到历史记录)
--------------------------------------------------------
---需求:当对student做出insert操作的时候,就打印“添加了学生信息”
----触发器在什么时候触发。当对这个表做insert操作之前就会自行触发器。 for each row表示行级触发器
-----------------------------------------------------------------------
----第一:操作(增删改)之后触发
create or replace trigger test_tri ---创建触发器test_tri,在堆student表执行insert语句之后执行
after insert on student ---触发器名,时间,操作(增删改),表名
declare
begin
dbms_output.put_line('添加或者删除学生信息成功');
end;
--对上面测试,行级触发器,操作一条数据
insert into student(sno,sname) values(4,'hzzzz');
select * from student
delete from student where ssex is null----行级和语句级触发器的区别是什么?
------------------------------------------
--第二:操作(增删改)之前触发
create or replace trigger bank_tigger
before update or delete on bank
declare
begin
dbms_output.put_line('----喊人救火....');
end;
---测试触发器
update bank set money=money-100 where bid=111;---违反检查约束,由于bank做了事务处理,因此之句话不会被执行 。
--但是由于是在update操作之前就会执行触发器,所以触发器会被执行,显示喊人救活。如果是修改操作之后才执行触发器,如果修改操作出错,触发器将执行不到
------------------------------------------
----例题:创建一个表,用来记录student表:操作的用户,操作时间,操作类型,操作这条数据的学号----这时候用行级触发器。
--- 这个创建的表其实就是日志表
---第一步:创建触发器
create or replace trigger log_trig
after insert or update or delete
on student
for each row ----这个表示行级触发器
declare
begin
if inserting then----如果正在添加
insert into stu_log values('scott',sysdate,'insert',:new.sno);
elsif updating then
--insert into stu_log values(SYS_CONTEXT('USERENV','OS_USER'),sysdate,'udpate',:old.sno); ---获取计算机名
insert into stu_log values(sys.login_user,sysdate,'udpate',:old.sno); ---获取oracle当前登陆用户的用户名
elsif deleting then
insert into stu_log values('scott',sysdate,'delete',:old.sno);
else
insert into stu_log values('1',sysdate,'1',1);
end If;
end;
-----第二步:创建用来保存日志信息的表
create table stu_log
(
uname varchar2(20),
opdate date,
optype varchar2(10),
sno number(6)
)
alter table stu_log modify uname varchar2(30)
-----第三步:在学生表中做dml操作,看看有没有调用触发器
---情况1:对学生表做了操作后,查询日志表是不是记录了日志
update student set sname='张三where sno=2;
---情况2 :如果同时执行update和select语句,就会在日志中添加两条记录,一个是修改记录一个是添加记录
update student set sname='张三where sno=2;
insert into student(sno) values(3)
---情况3:既然是语句级触发器,那么他能同时执行表中多条语句。这时候日志文件中就记录了多条日志
update student set sname='张三'
select * from stu_log;