事务、游标、触发器

/**
   事物
   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;

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值