第七章 事务、过程、触发器

1、事务
    (transaction)事务就是将一系列操作作为一个不可分割的整体,要么一起执行失败,要么一起执行成功 。   
    事务是一个不可分割的工作逻辑单元。
    所有数据库中的增删改实现都是通过事务来实现的。

事务具备四个属性ACID:
    原子性:(Atomicity)事务是一个完整的操作。事务的各步操作时不可分的(原子性);要么都执行,要么都不执行。
    一致性:(Consistency)当事务完成时,数据必须处于抑制状态。
    隔离性:(Isolation):对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。
    永久性:事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务的永久性。


管理事务的语句
    commit                 提交事务   
    savepoint              记录回滚点
    rollback                回滚事务

commit提交
    为了保证JAVA程序可以正确访问到数据库的数据内容,必须手动提交:commit。
    insert,update、delete语句都需要通过事务来提交至物理文件,默认情况下所有持久化操作都是在缓存中对数据进行访问。
    在DBMS关闭时数据库将自动对缓存中的数据进行commit提交。(比如在plsqldev这个dbms工具执行插入数据的操作,在物理文件的底层是看不到数据的,需要commit后才可以看到数据,关闭这个dbms工具在开启也可以看到)
    select优先到数据库的缓存中查询数据,如果缓存中不存在数据则至数据文件中继续查询。
    
    savepoint 和 rollback
例子:
   
   
  1. declare
  2. v_money float;
  3. begin
  4. --记录事务的回滚点(还原点)
  5. --savepoint p1;
  6. update bank set balance=balance+1200where cardId=1002;
  7. --一个事务可以定义多个回滚点
  8. --savepoint p2;
  9. update bank set balance=balance-1200where cardId=1001;
  10. --查询转账后的数据是否合法
  11. select balance into v_money from bank where cardId=1001;
  12. if v_money<0then
  13. --非法数据进行回滚
  14. rollback to p1;
  15. else
  16. --提交事务
  17. commit;
  18. endif;
  19. end;

       每个过程也是一个事务,当过程内发生约束错误不会改变数据,因为会自动回滚到begin


2、游标
        映射在结果集中一行数据上位置的实体,通过游标,就能访问结果集中的每一行数据,当游标放置到某一行上即可对该行进行相应的操作。
   
    游标的分类:隐式游标 和显式游标
    游标具有的属性:
                    %found
                    %notfound
                    %rowcount            语句执行影响的行数
                    %isopen
创建游标
            declare    cursor    游标名    is    查询语句

使用游标的步骤
        1、打开游标
                open    游标名;
        2、获取游标中下一行数据
                fetch    游标名    into    变量名;
             一般获取游标的数值,通过死循环来获取。
   
   
  1. loop
  2. fetch 游标名into变量名;
  3. exitwhen游标名%notfound;//当找不到游标值就退出循环
  4. --执行的一系列操作
  5. end loop;
                    如果不使用循环获取游标读取的数据,就只能使用一行数据。
           3、关闭游标
                close    游标名;

例子:
        游标读取结果集的数据时单列的,必须要使用变量来接收。
                               fetch c_emp into v_emp
       游标读取结果集的数据是多列的,可以使用属性行变量,也可以使用复合属性来接收。
        

  
  
  1. --创建游标
  2. declare
  3. cursor c_emp isselect*from emp;
  4. --记录游标读取的行
  5. v_emp emp%rowtype;
  6. begin
  7. --开启游标
  8. open c_emp;
  9. --控制游标读取结果集中的下一行数据
  10. /*fetch c_emp into v_emp;
  11. dbms_output.put_line(v_emp.first_name||v_emp.last_name||' '||v_emp.salary);
  12. fetch c_emp into v_emp;
  13. dbms_output.put_line(v_emp.first_name||v_emp.last_name||' '||v_emp.salary);*/
  14. --游标需要循环反复读取,直到读完结果集
  15. loop
  16. fetch c_emp into v_emp;
  17. exitwhen c_emp%notfound;
  18. dbms_output.put_line(v_emp.first_name||v_emp.last_name||' '||v_emp.salary);
  19. end loop;
  20. --关闭游标
  21. close c_emp;
  22. end;

   
   
  1. --将部门的所有员工的薪水增加100
  2. declare
  3. cursor c_empId isselect employee_id from emp;
  4. v_empId int;
  5. begin
  6. open c_empId;
  7. loop
  8. --读取一行数据
  9. fetch c_empId into v_empId;
  10. --判断是否需要退出
  11. exitwhen c_empId%notfound;
  12. --每读到一个人的编号就进行一次加薪
  13. update emp set salary=salary+100where emp.employee_id=v_empId;
  14. end loop;
  15. close c_empId;
  16. end;
        游标对应的结果集可以使用子查询实现。游标对应的select中可以使用变量,但这变量的创建必须在游标的创建前。
   
   
  1. --将sales部门的所有员工的薪水增加100
  2. --游标对应的结果集可以使用子查询实现
  3. --游戏对应的select中可以使用变量
  4. declare
  5. v_depId int;
  6. --cursor c_empId isselect emp.employee_id from emp where department_id=(select department_id from dep where dep.department_name='Sales');
  7. cursor c_empId isselect emp.employee_id from emp where department_id=v_depId;
  8. v_empId int;
  9. begin
  10. --获取Sales的部门编号
  11. select department_id into v_depId from dep where dep.department_name='Sales';
  12. open c_empId;
  13. loop
  14. --读取一行数据
  15. fetch c_empId into v_empId;
  16. --判断是否需要退出
  17. exitwhen c_empId%notfound;
  18. --每读到一个人的编号就进行一次加薪
  19. --update emp set salary=salary+100where emp.employee_id=v_empId;
  20. dbms_output.put_line(v_empId);
  21. end loop;
  22. close c_empId;
  23. end;
            可以定义复合类型接受游标获取的数据
   
   
  1. --打印显示Sales部门的员工的姓名,薪水,入职时间
  2. declare
  3. cursor c_emp isselect first_name||last_name,salary,hire_date from emp where department_id=(select department_id from dep where dep.department_name='Sales');
  4. --定义复合类型接收游标获取的每一行数据
  5. type empInfo is record
  6. (
  7. empName varchar2(50),
  8. salary float,
  9. hireDate emp.hire_date%type
  10. );
  11. --复合类型的变量
  12. v_empRow empInfo;
  13. begin
  14. open c_emp;
  15. loop
  16. fetch c_emp into v_empRow;
  17. exitwhen c_emp%notfound;
  18. dbms_output.put_line(v_empRow.empName||' '||v_empRow.salary||' '||v_empRow.hireDate);
  19. end loop;
  20. close c_emp;
  21. end;

           游标可以嵌套使用
   
   
  1. --打印显示在Seattle工作的所有员工的姓名、薪水,所在部门的名称,岗位名称
  2. declare
  3. v_locationId int;
  4. v_depId int;
  5. --查询Seattle对应部门的游标
  6. cursor c_dep isselect department_id from dep where location_id=v_locationId;
  7. --查询部门对应员工的游标
  8. cursor c_emp isselect first_name||last_name,salary,department_id,job_id from emp where department_id=v_depId;
  9. type empInfo is record
  10. (
  11. empName varchar2(50),
  12. salary float,
  13. depId int,
  14. jobId job.job_id%type
  15. );
  16. --自定义类型的变量
  17. v_emp empInfo;
  18. v_depName dep.department_name%type;
  19. v_jobName job.job_title%type;
  20. begin
  21. --查询对应的地区编号
  22. select location_id into v_locationId from locations where locations.city='Seattle';
  23. --开启游标
  24. open c_dep;
  25. loop
  26. --读取每一条部门编号
  27. fetch c_dep into v_depId;
  28. exitwhen c_dep%notfound;
  29. open c_emp;
  30. loop
  31. --读取每个部门对应的员工信息
  32. fetch c_emp into v_emp;
  33. exitwhen c_emp%notfound;
  34. --根据部门编号查询部门名称
  35. select dep.department_name into v_depName from dep where dep.department_id=v_emp.depId;
  36. --根据岗位编号查询岗位名称
  37. select job.job_title into v_jobName from job where job.job_id=v_emp.jobId;
  38. dbms_output.put_line(v_emp.empName||' '||v_emp.salary||' '||v_depName||' '||v_jobName);
  39. end loop;
  40. close c_emp;
  41. end loop;
  42. close c_dep;
  43. end;



3、存储过程
        存储过程类似Java中的方法,可以反复使用,用来执行一些列应用复杂的应用规则。
       
       创建存储过程
  
  
  1. create or replace procedure 过程名 (参数名 in out 类型)
  2. as
  3. begin
  4. --PL/SQL语句
  5. end;
   
   
  1. --创建存储过程
  2. create or replace procedure myPro
  3. as
  4. begin
  5. dbms_output.put_line('我的第一个存储过程');
  6. end;

       存储过程和方法一样可以传入参数,并且可以返回值。
           过程的参数类别
                in           表示输入参数
                out         表示输出参数
                in  out     表示该参数既可以是输入参数,也可以是输出参数
    注意点:使用out输出参数时调用参数前必须声明接受的参数。
        
    执行存储过程
        1) 在过程块外执行存储过程,需要用 call 过程名();
   
   
  1. --在过程块外执行存储过程
  2. call myPro();
        2) 在过程块内调用存储过程,直接用 过程名();
   
   
  1. --在过程内调用存储过程
  2. begin
  3. myPro();
  4. end;

带有输入参数的存储过程
           参数书写:参数名  in  out   数据类型            (in  out必须放在参数名和类型的中间)
           输入参数默认 in
           参数的类型注意 字符类型不需要定义长度(如 直接写 varchar2)
   
   
  1. --编写存储过程用于新增用户信息
  2. --参数默认为in输入参数
  3. create or replace procedure insertUser(v_userId int,v_uname in varchar2,v_birthday in date)
  4. as
  5. begin
  6. insert into users(userId,userName,birthday) values(userId,uname,birthday);
  7. end;
  8. call insertUser(1028,'jerry',to_date('19901010','yyyymmdd'));

   
   
  1. --编写存储过程根据用户编号查询姓名和性别
  2. create or replace procedure findUserById(v_userId int,v_userName out varchar2,v_sex out varchar2)
  3. as
  4. begin
  5. select userName,sex into v_userName,v_sex from users where userId=v_userId;
  6. end;
带有输出参数的存储过程必须要先创建变量用来保存输出参数
   
   
  1. --带有输出参数的存储过程必须要先创建变量用来保存输出参数
  2. declare
  3. v_name varchar2(20);
  4. v_sex users.sex%type;
  5. begin
  6. findUserById(1024,v_name,v_sex);
  7. dbms_output.put_line(v_name||' '||v_sex);
  8. end;
        带有in out类型参数的存储过程
   
   
  1. --带有inout类型参数的存储过程
  2. create or replace procedure testPro(a varchar2,b out varchar2,c inout varchar2)
  3. as
  4. begin
  5. dbms_output.put_line(a||','||b||','||c);
  6. c:='d';
  7. end;
  8. declare
  9. b varchar2(20):='b';
  10. c varchar2(20):='c';
  11. begin
  12. testPro('a',b,c);
  13. dbms_output.put_line(c);
  14. end;
打印输出结果: d

       注意点:如果需要声明变量,可以在as    begin 间声明变量,不需要declare。
       可读性案例:
   
   
  1. --创建存储过程根据指定员工的编号将该员工所在部门中比其收入高的员工收入增加200,如果该编号的员工的薪资是低于该部门的平均薪资
  2. --则加薪至平均薪资,并返回该部门的平均薪资
  3. create or replace procedure findDepSalaryById(v_empId int,v_avg outfloat)
  4. as
  5. --员工的薪资
  6. v_salary float;
  7. --部门编号
  8. v_depId int;
  9. --游标收入指定高的结果集
  10. cursor c_emp isselect emp.employee_id from emp where emp.department_id=v_depId and salary>v_salary;
  11. --每个员工的编号
  12. v_id int;
  13. --加工资的人数
  14. v_count int:=0;
  15. begin
  16. --查询员工的薪资和部门编号
  17. select salary,emp.department_id into v_salary,v_depId from emp where emp.employee_id=v_empId;
  18. --查询员工对应的平均薪资
  19. select avg(salary)into v_avg from emp where emp.department_id=v_depId;
  20. --开启游标
  21. open c_emp;
  22. --读取游标的每一行数据
  23. loop
  24. fetch c_emp into v_id;
  25. exitwhen c_emp%notfound;
  26. --增加员工的薪资200
  27. update emp set salary=salary+200where emp.employee_id=v_id;
  28. v_count:=v_count+1;
  29. end loop;
  30. dbms_output.put_line('加薪人数是:'||v_count);
  31. --判断员工自身薪资是否低于平均薪资
  32. if v_salary<v_avg then
  33. update emp set salary=v_avg where emp.employee_id=v_empId;
  34. endif;
  35. close c_emp;
  36. end;
  37. declare
  38. v_avg float;
  39. begin
  40. findDepSalaryById(198,v_avg);
  41. dbms_output.put_line(v_avg);
  42. end;

4、函数(函数这部分老师只是粗略的讲了下,未来的工作中基本不会用到)
       函数结构和过程非常相似。
        函数和过程的最大区别:
                   过程只能单独调用或者在语句块中使用
                   函数只能在DQL和DML语句中调用。

        函数可以传入外部的参数,且必须要有返回值。

       创建函数
   
   
  1. create or replace function函数名(参数)
  2. return数据类型
  3. as
  4. begin
  5. --PL/SQL语句
  6. return数据;
  7. end;

例子
   
   
  1. --创建自定义函数
  2. create or replace function showDepName(v_depId int)
  3. return varchar2
  4. as
  5. v_depName dep.department_name%type;
  6. begin
  7. select dep.department_name into v_depName from dep where dep.department_id=v_depId;
  8. return v_depName;
  9. end;

       函数的调用
   
   
  1. select first_name,showDepName(emp.department_id)from emp


5、返回游标的存储过程
    了解一个系统游标     sys_refcursor
        1)创建存储过程的参数里带有声明的系统游标    
        2)在存储过程里开启这个游标
        3)在过程里返回这个游标
--自己感觉 返回游标的存储过程就像java中的一个方法返回一个集合,然后在另一个方法中获取集合的值

例子:
   
   
  1. --返回游标的存储过程
  2. --根据部门编号返回该部门的所有员工姓名
  3. create or replace procedure findEmpByDepId(v_depId int,c_emp out sys_refcursor)
  4. as
  5. begin
  6. --开启系统游标
  7. open c_emp forselect first_name||last_name from emp where emp.department_id=v_depId;
  8. end;
  9. --调用返回游标的存储过程
  10. declare
  11. c_emp sys_refcursor;
  12. v_empName varchar2(20);
  13. begin
  14. findEmpByDepId(50,c_emp);
  15. --系统游标获取时不需要打开
  16. loop
  17. fetch c_emp into v_empName;
  18. exitwhen c_emp%notfound;
  19. dbms_output.put_line(v_empName);
  20. end loop;
  21. close c_emp;
  22. end;

作业题目:使用存储过程查询指定版块的所有主贴标题,返回游标
   
   
  1. --使用存储过程查询指定版块中的所有主贴标题,返回游标
  2. create or replace procedure getTopicById(v_id int,c_tid out sys_refcursor)
  3. as
  4. begin
  5. open c_tid forselect tid from bbstopic where tsid = v_id;
  6. end;
  7. declare
  8. c_tid sys_refcursor;
  9. v_topic bbstopic.ttopic%type;
  10. v_id int;
  11. begin
  12. getTopicById(1,c_tid);
  13. loop
  14. fetch c_tid into v_id;
  15. exitwhen c_tid%notfound;
  16. select ttopic into v_topic from bbstopic where tid = v_id;
  17. dbms_output.put_line(v_id||' '||v_topic);
  18. end loop;
  19. close c_tid;
  20. end;


6、触发器
        触发器是一种特殊的存储过程,在插入,修改或者删除表中的数据时将会被出发执行,触发器在事件发生时是自动执行的,并且不能输入参数。

        触发器的作用
            1)安全性    限制用户的一些特殊操作
            2)审计       将用户执行的操作放到一个表中,跟踪用户的操作
            3)实现非标准的更为复杂的业务规则

        触发器的种类
               1)DML触发器            进行DML语句操作时出发     (主要学习的触发器) 
                2)替换触发器            只能在视图上出发
                3)事件触发器            DDL和数据库事件出发


           DML触发器的创建    (又被称为行级触发器)
  
  
  1. create or replace trigger 触发器名称
  2. 触发时间触发类型 on 数据表for each row
  3. begin
  4. --PL/SQL语句
  5. end;
           删除触发器  
   
   
  1. drop trigger 触发器名称;

            触发时间   before (操作执行之前)  after(操作执行之后)
            触发类型   insert    update    delete

伪记录  
        :new  新纪录  
        :old    原记录
            对于update  :new 表示更新后的数据行    :old表示更新前的数据行
            对于 insert   :new表示新增的数据行        没有:old
            对于 delete   :old表示被删除的数据行      没有:new
     伪记录就相当于一个%rowtype行变量,可以使用 :new.列名获取该列值

  
    触发器内部可以对其他表进行DML操作,但不能对当前表执行DML操作。(想想会不会进入死循环)

例子:
insert操作
   
   
  1. create or replace trigger users_before_insert
  2. before insert on users for each row
  3. begin
  4. --使用伪记录获取数据
  5. --:new表示新增的数据行
  6. dbms_output.put_line(:new.userId||' '||:new.userName);
  7. --insert触发器无法访问old伪记录
  8. dbms_output.put_line(:old.userName);
  9. dbms_output.put_line('在users表新增数据时触发了');
  10. end;
delete操作
   
   
  1. create or replace trigger users_before_delete
  2. before delete on users for each row
  3. begin
  4. --:old表示被删除的数据行
  5. --delete触发器无法访问new伪记录
  6. dbms_output.put_line(:old.userName);
  7. dbms_output.put_line('执行了删除');
  8. end;
update 操作
需要
--自治式事务处理
DECLARE
PRAGMA  AUTONOMOUS_TRANSACTION;
   
   
  1. create or replace trigger users_before_update
  2. before update on users for each row
  3. begin
  4. --update触发器可以同时访问:new和:old伪记录
  5. --:new表示修改后的数据行
  6. --:old表示修改前的数据行
  7. dbms_output.put_line(:new.userName);
  8. dbms_output.put_line(:old.userName);
  9. dbms_output.put_line('执行了修改');
  10. end;
       体现触发器作用二(跟踪用户操作)的案例
   
   
  1. --编写触发器将用户修改数据的情况备份在user_log表中
  2. create or replace trigger users_log
  3. after update on users for each row
  4. begin
  5. --触发器内部可以对其他数据表执行DML操作
  6. --触发器不能对当前表执行DML操作
  7. insert into user_log values(logId.Nextval,'update',sysdate);
  8. end;

在触发器中自定义异常
        declare   
        异常名  exception;

         抛出异常(不同java的抛出异常)
        raise  异常名;

        处理异常(所有的异常处理都是在过程的末端执行,否则会报错)
        exception when 异常名 then
                raise_application_error(参数一,参数二);
            --第一个参数表示错误的编号,所有自定义的编号都从-20001开始取,只能比-20000小
            --第二个参数表示错误提示信息
   
   
  1. --异常处理
  2. --所有异常的处理应在过程的末端执行
  3. begin
  4. insert into users values(1025,'tom',sysdate,null,null,20,null);
  5. --处理系统异常
  6. exception when others then
  7. dbms_output.put_line('数据新增出现了异常');
  8. end;

       异常的处理在Oracle只是提示错误信息,但是在java中可以获取这些错误信息,进而进行相应的操作。

   
   
  1. --新增用户数据时,用户的年龄不能超过用户表中所有用户的平均年龄
  2. create or replace trigger users_before_insert
  3. before insert on users for each row
  4. declare
  5. v_avg int;
  6. --自定义异常
  7. myError exception;
  8. begin
  9. --查询平均年龄
  10. select round(avg(age))into v_avg from users;
  11. dbms_output.put_line('平均年龄是'||v_avg);
  12. --判断新增的年龄是否超过平均年龄
  13. if:new.age>v_avg then
  14. dbms_output.put_line('新增的年龄大于平均年龄');
  15. --可以改变伪记录的数据
  16. --:new.age:=v_avg;
  17. --抛出异常
  18. raise myError;
  19. endif;
  20. --处理异常
  21. exception when myError then
  22. --第一个参数是错误编号,所有自定义异常编号从-20000开始
  23. --第二个参数是错误的信息内容
  24. raise_application_error(-20001,'age must less than avg age!');
  25. end;

   
   
  1. --编写触发器,要求当用户修改emp表的数据时,salary的值必须在其岗位的薪资范围内,同时用户的薪水不超过该部门经理的薪水
  2. create or replace trigger emp_update
  3. before update on emp for each row
  4. declare
  5. PRAGMA AUTONOMOUS_TRANSACTION;
  6. v_jobid job.job_id%type;
  7. v_min float;
  8. v_max float;
  9. --部门经理的薪水
  10. v_salary float;
  11. --自定义异常
  12. myError exception;
  13. begin
  14. --查询修改数据行对应的岗位编号
  15. select job_id into v_jobid from emp where emp.employee_id=:new.employee_id;
  16. --根据岗位编号查询薪资范围
  17. select min_salary,max_salary into v_min,v_max from job where job_id=v_jobid;
  18. --查询部门经理的薪水
  19. select salary into v_salary from emp where emp.employee_id=(select emp.manager_id from emp where emp.employee_id=:new.employee_id);
  20. --判断修改后的薪资是否合法
  21. if:new.salary<v_min or:new.salary>v_max or:new.salary>v_salary then
  22. raise myError;
  23. endif;
  24. --处理异常
  25. exception when myError then
  26. raise_application_error(-20001,'薪水范围错误!');
  27. end;
  28. update emp set salary=5600where emp.employee_id=198;
        
使用触发器实现成绩表的数据添加不能新增学生中学号以外的非法学号
实现思路:  用:new获取添加的学号判断在学生表是否存在
   
   
  1. --使用触发器实现成绩表的数据添加不能新增学生中学号以外的非法学号
  2. create or replace trigger score_insert
  3. before insert on score for each row
  4. --创建游标获取所有学号
  5. declare
  6. cursor c_stuid isselect stuId from student;
  7. v_stuId int;
  8. --标识新增的学号是否存在
  9. v_hasId boolean:=false;
  10. --异常
  11. myError exception;
  12. begin
  13. --判断要新增的成绩信息中的学号是否出现在学生的学号范围内
  14. open c_stuid;
  15. loop
  16. fetch c_stuid into v_stuid;
  17. exitwhen c_stuid%notfound;
  18. if v_stuid=:new.stuid then
  19. v_hasId:=true;
  20. --退出循环
  21. exit;
  22. endif;
  23. end loop;
  24. if v_hasId=falsethen
  25. --抛出异常阻止数据的写入
  26. raise myError;
  27. endif;
  28. close c_stuid;
  29. exception when myError then
  30. raise_application_error(-20001,'违背了外键约束!');
  31. end;
  32. insert into score values(1000,1,9999);

创建一个自动插入序列的触发器
   
   
  1. --自动插入序列
  2. create or replace trigger auto_logId
  3. before insert on user_log for each row
  4. begin
  5. --将序列的值写入新纪录
  6. select logId.nextval into:new.logid from dual;
  7. end;
触发器还可以级联操作(比如删除主贴级联删除回帖)
     

7、索引
           在数据库中,所有数据的存储也是按页进行存储的,通过索引能够快速定位到指定页中,再根据索引所指定的列进行快速查询。
            
         创建索引
      create index  索引名称  on 表名(列名) [tablespace 表空间名]
       索引是针对某个列创建的。

使用索引合
       1)数据量应至少达到10w行级别
       2)频繁搜索的数据列适合建立索引
       3)频繁进行排序的列适合建立索引

使用索引的注意点:
                    数据量过少的表格不适合建立

                     数据表的列被频繁修改不合适建立

 

    

8、视图   (主要用于查询)
      视图就是一张通过查询获取的虚拟表,视图中本身不存在数据,数据来自于视图对源表的映射作用,一张数据表可以根据不同的需要创建多个不同的视图。
      
        使用视图的优点:
                过滤表中的行数据
                组织未经授权的用户访问敏感数据
                降低数据操作的复杂度
                可以将多个表空间的数据抽象为一个逻辑数据

        创建视图
        create    view    视图名称    as    查询语句
   
   
  1. --使用视图存储允许可见的数据
  2. create or replace view cashView asselect cardid,userName from card;
         使用视图
         select  * from    视图名称
   
   
  1. --使用视图查看数据
  2. select*from cashview;

视图是对源表的映射,因此修改视图即修改源表。
           比如修改视图里的用户名即对源表的数据进行修改。
  
  
  1. --视图是对源表数据的映射,因此修改视图即修改源表
  2. update cashView set userName='tony'where cardId='100000001';
               如果视图存在多表数据,同一时间只能更新一张表。

               视图可以将表中的敏感数据进行屏蔽,同时也可以将多表的数据以更直观的方式将其作为整体呈现        
                  如下为:选择非敏感数据呈现的视图 
  
  
  1. create or replace view cashView asselect recordId,card.cardId,userName,cash,case cashtype
  2. when1then'收入'
  3. when0then'支出'endcase,cashtime from card inner join cashRecord on card.cardId=cashRecord.Cardid;
两张表银行卡号表,消费记录表
   
   
  1. --银行卡号表
  2. create table card
  3. (
  4. cardid varchar2(20) primary key,
  5. userName varchar2(50)notnull,
  6. pwd varchar(6)notnull
  7. );
  8. create table cashRecord
  9. (
  10. recordId int primary key,
  11. cardid int,
  12. cash float,
  13. cashType int,--0表示支出1表示收入
  14. cashTime date
  15. );
这两张表以cardid关联,两张表的主键cardId、recordId

       键值保留表,表示保留主键的数据表,子表必须引用主表的主键或者是唯一键,如果修改的视图中的数据列是来自于A表,此时即表示为A表依赖于B表,B表中必须要有对应的主键或者是唯一键。

         视图
   
   
  1. create or replace view cashView asselect recordId,card.cardId,userName,cash,case cashtype
  2. when1then'收入'
  3. when0then'支出'endcase,cashtime from card inner join cashRecord on card.cardId=cashRecord.Cardid;
        对多表的视图进行修改
        以下就不能修改
        修改的是银行卡号表的数据,此时银行卡号表  依赖  消费记录表,但在消费记录表里,银行卡号并不是主键或唯一键,
   
   
  1. update cashView set userName='tony'where cardId='100000001';
       例如A B两表各有字段ID列 如果视图已A.ID=B.ID进行表联接,当修改A表数据的时候则A于B关联,A表的数据应依赖于B表,因此此时的A表相当于子表,B表为主表,若B表不存在主键则无法修改数据;
对应的,如果修改B表数据的时候,B表依赖于A表,因此此时的B表相当于子表,A表为主表,因此要进行数据修改A表中必须存在主键
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值