ITPub博客

首页 > 数据库 > Oracle > 尚学堂Oracle数据库笔记

尚学堂Oracle数据库笔记

Oracle 作者:shouldbai 时间:2014-01-21 10:35:46 0 删除 编辑
第一课:客户端 
        1. Sql Plus(客户端),命令行直接输入:sqlplus,然后按提示输入用户名,密码。
        2. 从开始程序运行:sqlplus,是图形版的sqlplus.
        3. http://localhost:5560/isqlplus
       
    Oracle Directory Manager:管理目录服务
    OLE:Microsoft自己应用程序开发的时候订立的一套标准,用VC和VB开发时需要了解它
    SQL*plus:Oracle的一个客户端
    Oracle客户端:toad、pl/sql Developer
    Oracle管理时toad用的很多
    isql*plus不需要在客户端装任何东西,只需要一个浏览器就够了
第二课:更改用户
    sql语言(Structured query language,结构化查询语言)是在数据库底下进行操作的专门的语言,是第四代语言,本身是一种标准语言,是一个国际标准;到目前为止有两套标准,第一套标准是1992年订立的,叫做sql1992,第二套标准叫做sql1999,1999年订立的新的标准,目前大多数的数据库系统既支持旧的也支持新的,很多的系统仍然是使用旧的sql语言来写程序的。
    在大多数的数据库系统上,sql语言都通用;只不过每一种不同的数据库,对于这种sql语言都有一点轻微的改变
        1. sqlplus sys/bjsxt as sysdba
            sqlplus 用户名/密码 as 角色
        2. alter user scott account unlock;(解锁)
第三课:table structure
   了解用户下面表的结构是什么样的:    
        1. 描述某一张表:desc 表名
        2. select * from 表名
第四课:select 语句:
       1.计算数据可以用空表:比如:.select 2*3 from dual
               查询当前日期:select sysdate from dual;
       2.select ename,sal*12 annual_sal from emp;与select ename,sal*12 "annual sal" from emp;区别,加双引号保持原大小写。不加全变大写。
                别名里有特殊字符,如空格、中文,需加双引号
                任何含有空值的数学表达式,最后的结果都是空值
       3. select ename || "abcd" 如果连接字符串中含有单引号,用两个单引号代替一个单引号。
第五课:distinct
            消掉重复值
        select deptno from emp;
        select distinct deptno from emp;
        select distinct deptno from emp;
        select distinct deptno ,job from emp
        去掉deptno,job两者组合的重复。更多的项,就是这么多项的组合的不重复组合。
第六课:Where
        select * from emp where deptno =10;
        select * from emp where deptno <>10;不等于10
        select * from emp where ename ='bike';
        select ename,sal from emp where sal between 800 and 1500 (>=800 and <=1500)
        空值处理:
        select ename,sal,comm from emp where comm is (not) null;
        select ename,sal,comm from emp where ename ( not)in ('smith','king','abc');
            变量>'日期',日期格式不知道可以用sysdate查询
        select ename from emp where ename like '_A%';_代表一个字母,%代表0个或多个字母. 如果查询%
        可用转义字符.\%. 还可以用escape '$'比如:select ename from emp where ename like '%$a%' escape '$';
第七课: orderby
        插入数据的顺序查询出来就是什么顺序
         select * from dept;
         select * from dept order by dept desc;(默认:asc)
         select ename,sal,deptno from emp order by deptno asc,ename desc;
第八课: sql function1:
        select ename,sal*12 annual_sal from emp
        where ename not like '_A%' and sal>800
        order by sal desc;
        select lower(ename) from emp;  转换成小写
            upper:全部转换成大写
        select ename from emp
        where lower(ename) like '_a%';等同于
        select ename from emp where ename like '_a%' or ename like '_A%';
        select substr(ename,2,3) from emp;从第二字符截,一共截三个字符.
        select chr(65) from dual 结果为:A
        select ascii('a') from dual 结果为:65
        select round(23.652,1) from dual; 结果为: 23.7
        select round(23.652,-1) from dual; 20
 
        to_char:把数字或者是日期转换成为某种特定的格式
        to_char(纯数字或者纯日期,格式)
        select to_char(sal,'$99,999.9999') from emp;  9代表的是一位数字,这一位上没有数字不显示,小数点后面的强制显示;如果把9改成0,这一位上没有数字则显示0
        select to_char(sal,'$99_999_999') from emp;
        select to_char(sal,'L99_999_999') from emp;人民币符号,L:代表本地符号
        这个需要掌握牢:
        select birthdate from emp;
        显示为:
        BIRTHDATE
        ----------------
        17-12月-80
        ----------------
        改为:
        select to_char(birthdate,'YYYY-MM-DD HH:MI:SS') from emp;
       
        显示:
         
        BIRTHDATE
        -------------------
        1980-12-17 12:00:00
        -------------------
       
        select to_char(sysdate,'YYYY-MM-DD HH24:MI:SS') from dual; //也可以改为:HH12
        TO_CHAR(SYSDATE,'YY
        -------------------
        2007-02-25 14:46:14  改成HH12或者HH直接显示2007-02-25 02:46:01
       

        to_date函数:
        select ename,birthdate from emp where birthdate > to_date('1981-2-20 12:34:56','YYYY-MM-DD HH24:MI:SS');
        如果直接写 birthdate>'1981-2-20 12:34:56'会出现格式不匹配,因为表中的格式为: DD-MM月-YY.
 
       
        select sal from emp where sal>888.88 无错.但
        select sal from emp where sal>$1,250,00;
        会出现无效字符错误.
        改为:
        select sal from emp where sal>to_number('$1.250.00','$9,999.99');
       
        把空值改为0
        select ename,sal*12+nvl(comm,0) from emp;  如果comm里面的值是空值,用0替代它;如果不是空值,就是comm的值
        这样可以防止comm为空时,sal*12相加也为空的情况.
第九课: Group function 组函数
        把好多条记录作为输入,最后产生一个输出
        max,min,avg ,count,sum函数
         count:求表里面有多少条记录
        select to_char(avg(sal),'99999999,99') from emp;
       
       
        select round(avg(sal),2) from emp;
        结果:2073.21
         
        select count(*) from emp where deptno=10;
        select count(ename) from emp where deptno=10; count某个字段,如果这个字段不为空就算一个.
        select count(distinct deptno) from emp;
        select sum(sal) from emp;
第十课: Group by语句
       
        需求:现在想求,求每个部门的平均薪水.
        select avg(sal) from emp group by deptno;
        select deptno avg(sal) from emp group by deptno;
       
        select deptno,job,max(sal) from emp group by deptno,job;
       
       求薪水值最高的人的名字.
       select ename,max(sal) from emp;出错,因为max只有一个值,但等于max值的人可能好几个,不能匹配.
       应如下求:
       select ename from emp where sal=(select max(sal) from emp);
       Group by语句应注意,
       出现在select中的字段,如果没出现在组函数中,必须出现在Group by语句中.
        select deptno,max(sal) from emp group by deptno;
        在使用group by时,出现在select列表中的字段,如果没有出现在组函数里,必须出现在group by子句里
   
  第十一课: Having 对分组结果筛选
       
       Where是对单条纪录进行筛选,Having是对分组结果进行筛选.
     
       select avg(sal),deptno from emp
       group by deptno
       having avg(sal)>2000;
       
       查询工资大于1200雇员,按部门编号进行分组,分组后平均薪水大于1500,按工薪倒充排列.
       select * from emp
       where sal>1200
       group by deptno
       having avg(sal)>1500
       order by avg(sal) desc;
       
   第十二课:字查询
       
       谁挣的钱最多(谁:这个人的名字, 钱最多)
       
       select 语句中嵌套select 语句,可以在where,from后.
       
             
       问那些人工资,在平均工资之上.
       
       select ename,sal from emp where sal>(select avg(sal) from emp);
       查找每个部门挣钱最多的那个人的名字.
       select ename ,deptno from emp where sal in(select max(sal) from ename group by deptno) 查询会多值.
       应该如下:
       
       select max(sal),deptno from emp group by deptno;当成一个表.语句如下:
       select ename, sal from emp join(select max(sal) max_sal,deptno from emp group
       by deptno) t on(emp.sal=t.max_sal and emp.deptno=t.deptno);
       
       每个部门的平均薪水的等级.
       分析:首先求平均薪水(当成表),把平均薪水和另外一张表连接.
       
       
第十四课:self_table_connection
       
       把某个人的名字以及他的经理人的名字求出来(经理人及这个人在表中同处一行)
       
       分析:首先求出这个人的名字,取他的编号,然后从另一张表与其相对应编号,然后找到经理的名字.
       
       select e1.ename ,e2.ename from emp e1,emp e2 where e1.mgr= e2.empno.
       
       empno编号和MGR都是编号.


第十五课: SQL1999_table_connections
         
      select ename,dname,grade from emp e,dept d, sqlgrade s
      where e.deptno = d.deptno and e.sql between s.losal and s.hisal and
      job<>'CLERK';
     
      有没有办法把过滤条件和连接条件分开来? 出于这样考虑,Sql1999标准推出来了.有许多人用的还是
      旧的语法,所以得看懂这种语句.
            
      select ename,dname from emp,dept;(旧标准).
      select ename,dname from emp cross join dept;(1999标准)
       
      select ename,dname from emp,dept where emp.deptno=dept.deptno (旧)
      select ename,dname from emp join dept on(emp.deptno = dept.deptno); 1999标准.没有Where语句.
      select ename,dname from emp join dept using(deptno);等同上句,但不推荐使用.
     
      select ename,grade from emp e join salgrade s on(e.sal between s.losal and s.hisal);
      join 连接语句, on过滤条件。连接,条件一眼分开。如果用Where语句较长时,连接语句和过滤语句混在一起。
     
      三张表连接:
      slect ename,dname, grade from
      emp e join dept d on(e.deptno=d.deptno)
      join salgrade s on(e.sal between s.losal and s.hisal)
      where ename not like '_A%';
      把每张表连接 条件不混在一起,然后数据过滤条件全部区分开来。读起来更清晰,更容易懂一点。
     
      select e1.ename,e2.ename from emp e1 join emp e2 on(e1.mgr = e2.emptno);
      左外连接:会把左边这张表多余数据显示出来。
      select e1.ename,e2,ename from emp e1 left join emp e2 on(e1.mgr =e2.empno);left 后可加outer
      右外连接:
    select ename,dname from emp e right outer join dept d on(e.deptno =d.deptno); outer可以取掉。
       
      即把左边多余数据,也把右边多余数据拿出来,全外连接。
      select ename,dname from emp e full join dept d on(e.deptno =d.deptno);


16-23 课:求部门平均薪水的等级
       A.求部门平均薪水的等级。
       select deptno,avg_sal,grade from
       (select deptno,avg(sal) avg_sal from emp group by deptno)t
       join salgrade s on(t.avg_sal between s.losal and s.hisal)
       
       B.求部门平均的薪水等级
       select deptno,avg(grade) from
       (select deptno,ename, grade from emp
       join salgrade s on(emp.sal between s.losal and s.hisal)) t
       group by deptno
       C.那些人是经理
       select ename from emp where empno in(select mgr from emp);
       select ename from emp where empno in(select distinct mgr from emp);
       
       D.不准用组函数,求薪水的最高值(面试题)
       
       select distinct sal from emp where sal not in(
       select distinct e1.sal from emp e1 join emp e2 on (e1.sal
       
       E.平均薪水最高的部门编号
       
       select deptno,avg_sal from
       (select avg(sal)avg_sal,deptno from emp group by deptno)
       where avg_sal=
       (select max(avg_sal)from
       (select avg(sal) avg_sal,deptno from emp group by deptno)
       )
     
       F.平均薪水最高的部门名称
       select dname from dept where deptno=
      (
        select deptno from
        (select avg(sal)avg_sal,deptno from emp group by deptno)
        where avg_sal=
        (select max(avg_sal)from
        (select avg(sal) avg_sal,deptno from emp group by deptno)
        )
       )
     
      G.求平均薪水的等级最低的部门的部门名称
       
        组函数嵌套
        如:平均薪水最高的部门编号,可以E.更简单的方法如下:
        select deptno,avg_sal from
        (select avg(sal) avg_sal,deptno from emp group by deptno)
        where avg_sal =
        (select max(avg(sal)) from emp group by deptno)
       
        组函数最多嵌套两层
       
        分析:
        首先求
        1.平均薪水: select avg(sal) from group by deptno;
        2.平均薪水等级: 把平均薪水当做一张表,需要和另外一张表连接salgrade
        select deptno,grade avg_sal from
          ( select deptno,avg(sal) avg_sal from emp group by deptno) t
        join salgrade s on(t.avg_sal between s.losal and s.hisal)
       
        上面结果又可当成一张表。
       
        DEPTNO GRADE AVG_SAL
      -------- ------- ----------
        30 3 1566.66667
        20 4 2175
        10 4 2916.66667
        3.求上表平均等级最低值
       
        select min(grade) from
        (
          select deptno,grade,avg_sal from
           (select deptno,avg(sal) avg_sal from emp group by deptno)t
          join salgrade s on(t.avg_sal between s.losal and s.hisa)
         )
        4.把最低值对应的2结果的那张表的对应那张表的deptno, 然后把2对应的表和另外一张表做连接。
         
          select dname ,t1.deptno,grade,avg_sal from
            (
         select deptno,grade,avg_sal from
              (select deptno,avg(sal) avg_sal from emp group by deptno)t
             join salgrade s on(t.avg_sal between s.losal and s.hisal)
             ) t1
            join dept on (t1.deptno = dept.deptno)
            where t1.grade =
            (
              select deptno,grade,avg_sal from
               (select deptno,avg(sal) avg_sal from emp group by deptno) t
                join salgrade s on(t.avg_sal between s.losal and s.hisal)
               )
            )
         结果如下:
         
        DNAME DEPTNO GRADE AVG_SAL
      -------- ------- -------- --------
        SALES 30 3 1566.6667
     
         
       H: 视图(视图就是一张表,一个字查询)
       视图名通常以v$_开头

       G中语句有重复,可以用视图来简化。
       conn sys/bjsxt as sysdba;
       grant create table,create view to scott;
       conn scott/tiger
       创建视图:
       create view v$_dept_avg-sal_info as
       select deptno,grade,avg_sal from
        ( select deptno,avg(sal) avg_sal from emp group by deptno)t
       join salgrade s on 9t.avg_sal between s.losal and s.hisal)
     
       然后
       select * from v$_dept_avg-sal_info
       
       结果如下:
       DEPTNO GRADE AVG_SAL
      -------- ------- ----------
        30 3 1566.66667
        20 4 2175
        10 4 2916.66667
       然后G中查询可以简化成:
       select dname,t1.deptno,grade,avg_sal from
       v$_dept_avg-sal_info t1
       join dept on9t1.deptno =dept.deptno)
       where t1.grade=
       (
        select min(grade) from v$_dept_avg-sal_info t1
       )

第二十四课
求比普通员工最高薪水还要高的经理人的名称  
    先求普通员工的最高薪水
    select max(sal) from emp where empno not in
     (select distinct mgr from emp where mgr is not null);
   
    select ename from emp where empno in
     (select distinct mgr from emp where mgr is not null)
    and sal >
     (
     select max(sal) from emp where empno not in
       (select distinct mgr from emp where mgr is not null);
     ) 


第二十五课 25_create_new_user_and_insert
    oracle的逻辑结构:
    安装oracle的时候首先安装了一个全局数据库,名称自己起,然后为这个数据库创建了一系列的管理这个数据库的进程,SID名自己起。
    在oracle一个大数据库里面又逻辑上区分为不同的表空间,表放在不同的表空间里面,users是oracle已经建好的表空间。用户的表可以放在不同的表空间里面。
    一个用户可以使用一个或多个表空间,一个表空间也可以供多个用户使用。用户和表空间没有隶属关系,表空是一个用来管理数据存储逻辑概念,表空间只是和数据文件发生关系,数据文件是物理的,一个表空间可以包含多个数据文件,而一个数据文件只能隶属一个表空间。

    SQL>conn sys/bjsxt as sysdba;  用超级管理员登录
    SQL>drop user liuchao cascade;  删除用户
    
    备份与恢复
    backup scott  把scott相关的表等内容备份出来
    DOS环境下:exp导出,imp导入 
    exp命令实际上是执行了 oracle底下的一个export.exe文件
    --drop user yugang cascade;
    --cd
    --cd temp
    =-delete *.*
    --exp
    SQL> create user yugang indentified by yugang default tablespace users quota 10M on users 在users这个表空间上给它分配10M的空间,存数据超过10M就不能再存了
    --grant create session,create table,create view to yugang
        create session:登录权限,用户可以登录到服务器上来了
    --imp the data
    --exp     
    第一次输入的用户名密码为:yugang/yugang
    第二次输入的用户名密码为:导出数据的用户的用户名和密码
    SQL>conn liuchao/liuchao
    SQL>conn scott/tiger
    SQL>insert into dept values (50,'game','bj');
    SQL>rollback
    SQL>create table emp2 as select * from emp; 创建与emp表一样的emp2表
    SQL>create table dept2 as select * from dept; 
   SQL>create table salgrade2 as select * from salgrade; 
    SQL>create table emp3 as select * from emp; 
    SQL>insert into dept2 values (50,'game','bj');
    SQL>insert into dept2(deptno,dname) values (60,'game2');  不插的字段默认为空值
    SQL>insert into dept2 select * from dept;  把dept中的记录都插入到dept2里面,表结构要相同

第二十五课 25_rownum

    没有排序的话默认是先导入的先显示出来,后导入的后显示出来
    rownum是在Oracle中在表的后面加的一个尾字段,并且只能使用诸如rownum<=n这样的 形式,不能与>或>=相连接
    1.求薪水值最高的前5条记录.
    select ename,sal from emp where rownum <= 5 order by sal desc;  先取前五个再排序
    select ename,sal from (select ename,sal from emp order by sal desc) where rownum <= 5;
    2.求薪水值最高的第6~10条记录.
    select ename,sal from
     (
       select ename,sal,rownum r from
         (select ename,sal from emp order by sal desc)
     )
    where r >=6 and r <= 10;
第二十六课:homework_dml_transaction
    SQL面试题
    有三张表S、C、SC
    S(SNO、SNAME)代表(学号、姓名)
    C(CNO、CNAME、CTEACHER)代表(课号、课名、老师)
    SC(SNO、CNO、SCGRADE)代表(学号、课号、成绩)
    1.求出没选过郭富城老师的所有学生姓名
    2.列出2门以上(含2门)不及格的学生姓名及平均成绩
    3.既学过1号课程又学过2号课程的所有学生姓名
    1.select sname from s join sc on (s.sno = sc.sno) join c on (c.cno = sc.cno)
    where c.cteacher <> '郭富城';
    2.select sname from s where sno in
     (
       select sno from sc where scgrade < 60 group by sno having count(*) >= 2
     )
    3.select sname from s where sno in
     (
       select sno from sc where cno=1 and sno in
         (select distinct sno from sc where cno=2)
     )
    
    SQL> update emp2 set sal=sal*2, ename=ename||'-' where deptn=10;
    SQL> select ename, sal from emp2 where deptno=10;
    SQL> delete from emp2;
    SQL> delete from dept2 where deptno < 25;
    SQL> rollback;
    SQL> drop table t;  删除表

    事务transaction代表一组不可分割的操作,要么全部执行,要么全部不执行,
    transaction起始于一条DML语句,结束于commit;语句,或者是DCL、DDL语句,在事务未提交前可以通过rollback回滚事务,正常退出事务会自动提交,非正常退出事务会自动回滚。 rollback,回到最原始的状态,也算transaction完成了
    transaction什么时候结束?
    1、用户显式地执行commit语句提交操作或者是rollback语句回退;
    2、执行DDL或者DCL语句时事务自动提交;
    3、当用户正常断开连接的时候,比如exit退出sqlplus;非正常断开包括直接点右上角关闭按钮关闭sqlplus窗口,直接关机等,这时事务自动回滚。

第二十七课:create table
    create table student
    (
    id number(6),
    name varchar2(20),
    sex number(1),
    age number(3),
    sdate date,
    grade number(2) default 1,
    class number(4),
    email varchar2(50)
    ); 


第二十八~三十课 constraint
    五个约束条件:非空、唯一、主键、外键、check
    not null,unique(当某字段有unique约束时,可以插入空值,空值之间不重复)、主键、外键、 check约束
    约束条件本身在数据库里面也是一个对象,可以为约束条件起一个名字,不起名系统默认给起名
    唯一约束unique:在这个字段里面所有的约束不能取重复的值,每个值必须是唯一的
    check约束:本身用得少,写数据库系统和MIS系统的时候对于数据的检验在java中就校验过了
    create table student
    (
    id number(6),
    name varchar2(20) constraint stu_name_nn not null,
    sex number(1),
    age number(3),
    sdate date,
    grade number(2) default 1,
    class number(4),
    email varchar2(50) unique
    )
    /
    字段级/行级约束(放在字段后面)与表级约束(加在表后面):
    字段级约束有一些不能完成的功能,比如要求name和email的组合不能重复
    当一个字段取值是唯一的时候,可以给它往里插空值,而且两个空值不认为是重复的
    create table student
    (
    id number(6),
    name varchar2(20) constraint stu_name_nn not null,
    sex number(1),
    age number(3),
    sdate date,
    grade number(2) default 1,
    class number(4),
    email varchar2(50),
    constraint stu_email_name_uni unique(name,email)
    )
    /
    主键约束
    主键primary key:可以唯一标识整条记录;建一个主键的时候,会随着这个主键建立一个索引;从语法上来说主键相当于非空和唯一的组合,在逻辑意义上通常代表着单独的每一条记录
    create table student
    (
    id number(6) primary key,
    name varchar2(20) constraint stu_name_nn not null,
    sex number(1),
    age number(3),
    sdate date,
    grade number(2) default 1,
    class number(4),
    email varchar2(50),
    constraint stu_email_name_uni unique(name,email)
    )
    /
    create table student
    (
    id number(6),
    name varchar2(20) constraint stu_name_nn not null,
    sex number(1),
    age number(3),
    sdate date,
    grade number(2) default 1,
    class number(4),
    email varchar2(50),
    constraint stu_id_pk primary key(id),
    constraint stu_email_name_uni unique(name,email)
    )
    /
    外键约束
    外键约束被参考的字段必须是主键。
    如果被其它表的字段参考,则该记录不能删除
    create table class
    (
    id number(4) primary key,
    name varchar2(20) not null
    )
    /
    create table student
    (
    id number(6),
    name varchar2(20) constraint stu_name_nn not null,
    sex number(1),
    age number(3),
    sdate date,
    grade number(2) default 1,
    class number(4) references class(id),
    email varchar2(50),
    constraint stu_id_pk primary key(id),
    constraint stu_email_name_uni unique(name,email)
    )
    /
    create table student
    (
    id number(6),
    name varchar2(20) constraint stu_name_nn not null,
    sex number(1),
    age number(3),
    sdate date,
    grade number(2) default 1,
    class number(4),
    email varchar2(50),
    constraint stu_id_pk primary key(id),
    constraint stu_class_fk foreign key (class) references class(id),
    constraint stu_email_name_uni unique(name,email)
    )
    / 


第三十一课:alter table drop table
    修改表结构
    alter table student add(addr varchar2(100));
    alter table student drop(addr);
    alter table student modify(addr varchar2(50));  修改之后的新的精度必须能容纳原有的数据
    若addr字段中有的记录长度大于50,则修改不成功。
    alter table student drop constraint stu_class_fk;
    alter table student add constraint stu_class_fk foreign key (class)
    references class (id);
    drop table class; 
第32课:Oracle dictionaries
    数据字典表:记录了当前用户下有哪些表、约束、视图等等,也是一张表,包括user_tables、user_views、user_constraints等
    desc user_tables;
    select table_name from user_tables;
    select view_name from user_views;
    select constraint_name from user_constraints;
    select constraint_name,table_name from user_constraints;
    desc dictionary;
    dictionary:记录了存储多少张数据字典表,TABLE_NAME:表的名字,COMMENTS:对于这张表的描述
    select table_name from dictionary;
    select table_name from dictionary where table_name like 'USER%';
    select table_name from dictionary where table_name like 'USER%' order by table_name;
第33课:indexes_and_views 
    索引:新的数据库对象
    索引可以提高读数据的效率,但会降低修改、写数据的效率,索引还会占用一定的存储空间 
    create index idx_stu_email on student(email);
    create index idx_stu_email on student(email,class);  为两个字段的组合建一个索引
    drop index idx_stu_email;
    select index_name from user_indexes;
    当给这张表的某个字段加了主键约束、唯一约束,oracle会建立对应的这个字段的索引
    视图就是一个子查询,有时能简化查询
    视图会带来一些维护的代价,比如表结构改了,那么视图也得跟着改变。视图还可以用来保
    护私有数据。
    视图是可以更新数据的,只要不影响其它的表,但很少这么用,
    select view_name from user_views;
   
第34课:sequences and review
    sequences:oracle里面特有的东西(sql server里用identity,my sql里用auto_increment),产生一个唯一的、不间断的数字序列,一般是用来做主键
    序列可以自动增长,在sql server中有identity,mysql中有auto_increment
    create table article
    (
    id number,
    title varchar2(1024),
    cont long
    )
    /
    如何保证插入数据时id不重复?
    select max(id) from article;这样做也不行,如果有多个线程同时访问数据,则可能会出现
    数据不一致的现象,比方说第一个用户查询出最大id值是100,第二个用户也查询出最大id是
    100,然后第二个用户新插入一条记录:id是101,然后第一个用户也插入了一条记录:id也是
    101
    可以用Oracle的sequence,内部已经做了线程同步
    create sequence seq_article_id;
    多个表之问可以共用一个序列,但是一般情况下为每个字段分别建立一个序列
    sequence有两个属性CurrVal、NextVal
    start with 1:从1开始
    increment by 1:每次递增1
    select seq.nextvalue from dual;
    insert into article values(seq.nextval,'y','yy');
    insert into article values(seq.nextval,'x','xx');
    insert into article values(seq.nextval,'z','zz');
    insert into article values(seq.nextval,'q','qq'); 
    drop sequence seq_article_id;  删除一个序列
    数据库的常用对象:表(table)、约束(constraint):依附在表的上面、索引(index)、视图(view)、序列(sequence):产生一个独一无二的序列,可以用在其它表里面

第35课:三范式
    范式:数据库设计的一些规则,设计数据库时所遵循的一些规则,追求不存在冗余数据这个原则,就是同样的数据不存第二遍
    第一范式:
    1.每个表都要有主键
    2.列不可分,比如:
    create table stu
    (
    id number,
    name varchar2(20),
    age number
    )
    /
    insert into stu values(1,'yu',21);
    create table stu2
    (
    inf long
    )
    /
    insert into stu2 values('1_yu_21');
    虽然表stu2可以字符串解析后可以达到和表stu一样的效果,但是显然第一种方法更好,查询数     
    据更加方便,而表stu2违反了第一范式的列不可分原则。
    列也不能重复  
    第二范式:
    多对多关系这种表设计的时候,当一张表里面有多个字段作为主键的时候,非主键的字段不能够依赖于部分主键;即不能存在部分依赖;不是主键的字段不能部分依赖于主键
    当有两个以上字段作主键时,非主键字段不能部分依赖于主键字段,如有一个需求,一门老师教 多名学生,一名学生可以选多个老师的课。然后设计了一张表,有以下字段(老师编号、学生编 号、老师姓名、学生姓名等),其中以老师编号和学生编号作联合主键,则些表就存在部分依赖 ,老师姓名部分依赖于老师编号,不满足第二范式,有数据冗余。要解决这个问题可以设计三张表。
    第三范式:
    不能存在传递依赖,就是说除了主键以外的任何其它字段必须直接依赖于主键
    不能存在传递依赖,如有一张表有以下字段:(学号、姓名、班级号、班级名称、班级位置), 其中学号为主键,则班级号依赖于学号,每个学生都有相应的班级号,但是班级名称、班级位置是依赖于班级号,即它们通过班级号传递依赖于学号,不满足第三范式。


第三十六~三十七课 BBS

第38课:PL_SQL_1
    PL_SQL:在oracle里面的一种编程语言,oracle内部使用的编程语言,这种编程语言通常在oracle内部写一些存储过程,写一些oracle内部的程序,写一些触发器,写一些函数等等,基本上每一种数据库都有这样一种语言,sql server的叫transact sql(T-SQL),是在sql server内部实现同样功能的语言
    PL(procedure language)语言是为了补充sql语言的,是带有分支和循环的语言,sql语言没有分支、循环的概念,
    PL语言很简单,分四块,
    第一块:declare(可选):做变量的声明,声明各种变量,游标。
    第二块:begin 程序从这开始执行
    第三块:exception :catch到任何exception时执行的
    第四块:end;     结束,要有分号
    set serveroutput on;
    begin
        dbms_output.put_line('Hello World!!');
    end;
    必须先执行 set serveroutput on; 命令(因为serveroutput(内部环境变量)默认为off),再执行上面的程序就能打印出结果


第39课:PL_SQL_2
    declare
        v_name varchar2(20);    变量名一般用v_开头
    begin
        v_name:='xiaoyu';
        dbms_output.put_line(v_name);
    end;
    
    再来认识一下整个语句块的构成:
    declare
        v_num number:=0;
    begin
        v_num:=2/v_num;
        dbms_output.put_line(v_num);
    exception
        when others then
            dbms_output.put_line('error');
    end;


第40课:PL_SQL_3
    --变量声明的规则
    1.变量名不能使用保留字,如from、select等
    2.第一个字符必须是字母
    3.变量名最多包含30个字符
    4.不要与数据库的表或者列同名
    5.每一行只能声明一个变量
   
    --常用变量类型
    1.binary_integer:整数,主要用来计数而不是用来表示字段类型。当一个数用来记数的时候使用binary_interger效率要高一些,除了记数还用来做数组的下标
    2.number:数字类型,既包含整数又包含小数
    3.char:定长字符串
    4.varchar2:变长字符串
    5.date:日期
    6.long:长字符串,最长2GB
    7.boolean:布尔类型,可以取值true、false和null值,默认为null,在PL_SQL中使用boolean类型时,要给它一个初值,另外boolean类型值不可直接打印,会报错;只能是if(变量=true/false) 来使用
    Oracle 里面的put_line()不能打印boolean类型的值
    dbms_output.put_line('v_temp value:' || v_valid); //报错
    变量经常是用来存储某一张表某一个字段的值,如v_sal来存储emp表中sal字段的值
    查看当前用户:show user 
     --变量的声明,使用%type属性
     declare
         v_empno number(4);
         v_empno2 emp.empno%type;//表示类型和emp这张表的empno字段类型相同,当empno类型改动时,不需要再到这里改动v_empno2,会自动变换
         v_empno3 v_empno2%type; //%type属性,可以用于变量身上 ,是变量的一种属性,指的是它的类型
     begin
         dbms_output.put_line('Test');
     end;
     /
    //使用%type属性,可以使变量的声明根据表字段的类型自动变换,省去了维护的麻烦,而且% type属性,可以用于变量身上
    PL/SQL 里面--表示注释掉一行,注释多行
   


41_PL_SQL_4.avi
复杂的变量:
复合变量有两种:
第一种:table 相当于java里面的数组Array[]
在PL/SQL里面要声明数组,也要指定它的类型,PL/SQL里比较复杂,
必须重新定义新的类型,这里先声明类型,再用类型声明变量,下标允许有负值

--Table变量类型
declare
    首先声明一个类型,再用类型声明一个变量
    type type_table_emp_empno is table of emp.empno%type index by binary_integer;
    --声明数组类型,type表示定义了一种新的类型;type_table_emp_empno是类型的名字,起名规则:type说明是一种新定义的类型,table说明是定义的哪种类型,emp表名,empno表中的类型;is table of表示是一张表;emp.empno表示里面装的是emp这张表的empno类型,index by binary_integer表示是由binary_integer来索引,说明下标类型是binary_integer类型
    v_empnos type_table type_table_empno;  用上面定义的类型定义新的变量
begin
    v_empnos(0) := 7369;  下标值为0的位置
    v_empnos(0) := 7839; 
    v_empnos(-1) :=9999;  下标值可以取负值
    dbms_output.put_line(v_empnos(-1)); 
end; 
  /
  第二种:record 相当于java里面的类


42_PL_SQL_3.avi
Record变量类型 相当于java里面的类
PL/SQL程序大多数情况下操作的对象都是表,表里面有字段,有记录,一条记录分了好几个部分,包括姓名、年龄、性别、介绍等等,分了四个部分,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;
/
record的缺陷:当dept这张表结构变化时,增加或删除一个字段时,type里面也需要改动。因此可以使用 %rowtype声明record变量,代表和表的字段同时变动
--使用%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;
/


43_PL_SQL_5.avi
PL/SQL里面--SQL语句的运用
用select语句,必须返回一条记录,并且只能返回一条记录。如果返回太多,变量装不了
PL/SQL里面的select语句如果不用游标的话,必须和into语句一块用并且有且只有一条记录,没有不行,多了也不行。select语句必须能后返回一条记录。
例子1:
--SQL语句的运用
declare
    v_ename emp.ename%type;
    v_sal emp.sal%type;
begin
    select ename, sal into v_ename, v_sal from emp where empno = 7369;
    --pl/sql的select语句里,必须有into,不用游标就必须用into
    dbms_output.put_line(v_ename || ' ' || v_sal);
end;
/
例子2:select语句的应用(record)
declare
    v_emp emp%rowtype;
begin
    select * into v_emp from emp where empno = 7369;
    必须保证能返回记录,否则报错未找到数据;返回多条记录也会报错
    dbms_output.put_line(v_emp.ename);
end;
/
其他语句的运用:
例子3:
declare
    v_deptno dept.deptno%type := 50;
    v_dname dept.dname%type := 'aaaa';
    v_loc dept.loc%type := 'bj';
begin
    insert into dept2 values (v_deptno, v_dname, v_loc);
commit;
end;
例子4:
declare
    v_deptno emp2.deptno%type := 10;
    v_count number;
begin
    --update emp2 set sal = sal/2 where deptno = v_deptno;
    --select deptno into v_deptno from emp2 where empno = 7369;
    select count(*) into v_count from emp2;
    dbms_output.put_line (sql%rowcount || '条记录被影响');
commit;
end;
注:sql%rowcount 统计上一条sql语句更新的记录条数


44_PL_SQL_6.avi
PL/SQL 里面的DDL语句,需要加execute immediate
例子1:
begin
    execute immediate 'create table T (nnn varchar2(20) default ''aaa'')';
    --不能直接写,单引号里面用两个单引号代表一个单引号
end;
命令提示符下:sqlplus 用户名/密码
drop table T;
select ename,sal from emp2;
desc T;

--if语句:
--取出7369的薪水,如果<1200, 则输出'low',如果 <2000 则输出 'middle', 否则'high'
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;
 
 
45_PL_SQL_7.avi
--循环 1
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 循环

--循环 2
declare
    j binary_integer := 1;
begin
    while j < 11 loop
        dbms_output.put_line(j);、
        j := j + 1;
    end loop;
end;
以上循环相当于 java 里的 while 循环
 
--循环 3
begin
    for k in 1..10 loop
        dbms_output.put_line(k);
    end loop;
    for k in reverse 1..10 loop //reverse表示逆序
        dbms_output.put_line(k);
    end loop;
end;
以上循环相当于 java 里的 增强 for 循环

46_PL_SQL_8.avi
--错误处理,在pl/sql里面每一种exception都有一个名字
例子1:
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;

DBA经常使用的一种用来记录错误的做法:
大多数人做记录都是写在日志文件里面,对于数据库里面的记录可以记录在表里面
例子:
创建一张表用来记录出错的信息
create table errorlog
(
    id number primary key,
    errcode number,
    errmsg varchar2(1024),
    errdate date
);

创建序列sequence
create sequence seq_errorlog_id start with 1 increment by 1; 
--使id自动递增
写你的程序
declare
    v_deptno dept.deptno%type := 10;
    v_errcode number;
    v_errmsg varchar2(1024);
begin
    delete from dept where deptno = v_deptno;    //删不了,因为被引用了
commit;
exception
    when others then
    rollback;
    v_errcode := SQLCODE;    //SQLCODE是关键字,代表出错的代码
    v_errmsg := SQLERRM;    //SQLERRM是关键字,代表出错的信息
    insert into errorlog values (seq_errorlog_id.nextval, v_errcode, v_errmsg, sysdate);
    commit;
end;
执行
select * from errorlog;
报错误;
delete from dept where detpno = 10;
报错误
查看具体的出错时间:
select to_char (errdate, 'YYYY-MM-DD HH24:MI:SS') from errorlog;


47_cursor1.avi
PL/SQL里面的重点-->游标
游标是什么意思呢?
请你挨着排的把emp那张表的每个人他的一些相关信息拿出来(注意:每个人都拿出来),拿出来之 
后根据他不同的薪水值,来设定他新的薪水值 <1200 的 *2 , >1200 的 /2 等等
select 语句可以产生一个结果集,游标是指在结果集脑袋顶上的一个指针,游标跟java里面的迭代器(iterator)差不多,所以游标指的就是一个指针,它指在一个结果集上,所以有了这个游标之后,你就可以对这个结果进行遍历,循环,先拿第一条,拿出来之后接下来next一下,再拿第二条,游标是我们的重点,一定要理解。 
 
例子1:
declare
    cursor c is    --声明游标
    --cursor 是游标的意思,相当于一个指针,cursor的本意就是我们鼠标上的小指针 
        select * from emp;    //取出了一个结果集
    v_emp c%rowtype; --v_emp用来存储我们这个游标所指向的结果集的记录
begin
    open c; --打开游标 注意:cursor c 叫做声明游标,声明游标的时 候PL/SQL不会真正的取数据,只有打开游标的时候PL/SQL才会真真正正的取数据执行select语句 
    fetch c into v_temp; --把当前游标指向的内容拿出来放到v_emp变量里面去
    --fetch拿出来之后游标自动的往下移一格
    dbms_output.put_line(v_emp.ename);
    close c; --用完游标之后要close c,把内存清掉
end;
没有游标的话,甚至我说让你把emp那张表里面,每一个记录都取出来,恐怕你都不知道怎么样取? 
如果取好多条怎么办?
应该和循环一起用,用一个东西取探测这个游标什么时候到底了。
下面来看看用do-while循环,也就是简单循环来遍历游标
例子2:
declare
    cursor c is
    select * from emp;
    v_emp c%rowtype;
begin
    open c;
    loop
        fetch c into v_emp; --这个时候就把第一条记录放进去了
        exit when (c%notfound); --用notfound这个属性和简单循环配合来遍历这个游标指向最后一条的时候能fetch到记录 
    dbms_output.put_line(v_emp.ename);
    end loop;
close c;
end;
cursor 有四个属性:
isopen,布尔型,游标是否打开
notfound,布尔型,最近的fetch语句没有返回一条记录,找不到记录就返回ture ,指向最后一条的时候能fetch到记录
found,布尔型,找着记录了就返回ture
rowcount,数字型,当前已经fetch到了多少条记录,总数是多少,返回已从游标中读取的记录数
多问一句:
我把dbms_output.put_line(v_emp.ename);放到exit when (c%notfound); 上面,最后输出的 结果会是什么样子? 
当你到最后一条记录的时候fetch 它,没有找到,那你还打印它的值,就把上一条记录的值再 打印一遍,所以这三条语句顺序是不能颠倒的。 
 
48_cursor2.avi
另外两种循环,一种是while循环,一种是for循环
例子1:
declare
    cursor c is
    select * from emp;
    v_emp emp%rowtype;
begin
    open c;
    fetch c into v_emp; --首先fetch一下
    while(c%found) loop --如果找到了 loop循环
        dbms_output.put_line(v_emp.ename);
        fetch c into v_emp; --接下来再fetch
    end loop;
    close c;
end;
while循环的写法:如果找到了,循环,打印一行,然后再fetch,这是while循环的写法
问大家一句:如果把fetch c into v_emp;放到 dbms_output.put_line(v_emp.ename)前面什么效果? 
---->第一句没打,最后一句打了两遍
为什么打印两遍,最后一条找到了,找到了之后你还找,最下面的没了,找就再找一 次上面的。所以,最后一条记录打了两遍。
for循环:
declare
    cursor c is
    select * from emp;
begin
    for v_emp in c loop --上面不用声明变量v_emp,for开始的时候自动声明v_emp
        -- c不需要打开,for开始的时候自动打开,结束的时候自动关闭
        -- 不需要每次都fetch,它自动帮你fetch完了进入下一条记录
        -- 它挨着排的循环每条记录,然后挨着排的放到v_emp里面
        dbms_output.put_line(v_emp.ename);
    end loop;
end;
--看这三种循环,for循环恐怕是最简单,并且它很不容易出错,前后语句不容易颠倒,你也不
需要根据cursor的属性来判断cursor是不是结束,所以for循环是我们平常用的最多的循环

--带参数的游标
例子:
declare
    cursor c(v_deptno emp.deptno%type, v_job emp.job%type)
    is --这个游标的语句是什么 --> is
        select ename, sal from emp where deptno = v_deptno and job = v_job;
    --v_temp c%rowtype; --v_temp 不需要声明,用的for循环
begin
    for v_temp in c (30, 'CLERK') loop
        dbms_output.put_line(v_temp.ename);
    end loop;
end;
--可更新的游标 -->不是重点,大多数游标是用来做遍历的 ,用来读的,只读的
declare
    cursor c
    is
    select * from emp2 for update; --为了更新才使用的游标
    --v_temp c%type;
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;
    commit;
end;


49_produre_1.avi
这个时候我们可以随便写PL/SQL程序了
例子:
declare
    cursor c
    is
        select * from emp2 for update;
    --v_temp c%rowtype;
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;
    commit;
end;
/
 
如果你下次登录进来还想执行这个程序你怎么办?有没有一个办法,比如给这个程序起一个名 字,以后调用的时候直接调用这个名字就行了? 
-> 有,这个办法就是创建存储过程(Stored Procedure),每种数据库都会提供编写数据库存储语言,实际上它就是 带有名字的PL/SQL程序块
最简单的存储过程的例子:
create or replace procedure p --如果有就把它替换,没有就创建
is
    --除了以上语句替换了declare以外,剩下的语句跟PL/SQL语句一模一样
    cursor c is
        select * from emp2 from update;
begin
    for v_emp in c loop
        if(v_emp.deptno = 10) then
            update emp2 set sal = sal + 10 where current of c;
        elsif(v_emp.deptno = 20) then
            update emp2 set sal = sal + 20 where current of c;
        else
            update emp2 set sal = sal + 50 where current of c;
        end if;
    end loop;
    commit;
end;
/ --执行后,说这个存储过程已经创建,不等于这个存储过程已经执行了
你要执行这个存储过程有两个办法,
第一个:exec p;
select sal from emp2; --执行这个语句会发现,sal已经改变
第二个:
begin
p;
end;
/ --执行这个块,它说PL/SQL过程成功完成。
select sal from emp2; --测试执行后的结果
总结:存储过程就是一个块,只是这个块带有名字,执行的时候只要执行名字就可以了

--带参数的存储过程(这个稍微复杂一些)
create or replace procedure p
(v_a in number, v_b number, v_ret out number, v_temp in out number)
--在存储过程里面,参数可以分为不同的类型
-- in 叫做传入参数->谁调用这个存储过程谁负责给v_a赋值,从调用这个环境往我们这个存储
过程里头传的参数
-- out 叫做传出参数:存储过程是没有返回值的,它就借助于传出参数,由存储过程把这个值传
出到调用过程(调用的环境)里面去
-- out是负责往外传的,in是负责接收的
-- v_b 中间什么都没写,默认是in,是接收参数用的
-- v_temp 你还可以既可以接收,又可以传出
is
begin
    if(v_a > v_b) then
        v_ret := v_a;
    else
        v_ret := v_b; --给v_ret赋了值
    end if;
    v_temp := v_temp + 1; --既用了原来的值,又给它了新的值
end;
/ --创建好这个过程怎么用这个过程?
调用过程:
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
接下来会告诉你哪里错误,即便是语法错误,过程依然会创建


50_produre_2.avi 
删除procedure
--函数
create or replace function sal_tax    //计算个人所得税
  (v_sal number)
  return number
is
begin
   if (v_sal < 2000) then
      return 0.10;
   elsif (v_sal < 2750) then
      return 0.15;
   else
      return 0.20;
   end if;
end;

select lower(ename),sal_tax(sal) from emp;

--触发器
create table emp2_log
(
    uname varchar2(20),
    action varchar(10),
    atime date
)
-----------
create or replace trigger trig
    after insert or update or delete on emp2 (for each row)
    --after是插入完成之后触发触发器,before是在插入之前触发触发器
    --USER是关键字,代表的是当前用户是谁
    --for each row表示每更新一行就会触发一次操作
begin
    if inserting then
        insert into emp2_log values (USER, 'insert', sysdate);
    elsif updating then
        insert into emp2_log values (USER, 'update', sysdate);
    elsif deleting then
        insert into emp2_log values (USER, 'delete', sysdate);
    end if;
end;
ORACLE提供的如下条件谓词。
1)INSERTING:当触发事件是INSERT时,取值为TRUE,否则为FALSE。
2)UPDATING [(column_1,column_2,…,column_x)]:当触发事件是UPDATE 时,如果修改了column_x列,则取值为TRUE,否则为FALSE。其中column_x是可选的。
3)DELETING:当触发事件是DELETE时,则取值为TRUE,否则为FALSE。

----------
update emp2 set sal = sal * 2 where deptno = 30;
select * from emp2_log;
update dept set deptno=99 where deptno=10;
drop trigger trig;
--------
create or replace trigger trig
    after insert or update or delete on emp2 for each row
begin
    if inserting then
        insert into emp2_log values (USER, 'insert', sysdate);
    elsif updating then
        insert into emp2_log values (USER, 'update', sysdate);
    elsif deleting then
        insert into emp2_log values (USER, 'delete', sysdate);
    end if;
end;
-------------


第五十一课51_trigger
SQL>update dept set deptno=99 where deptno=10;
违反约束条件。由于deptno被人参考了,所以不能这么写;除非在这个值改成99的同时也要把那些参考了10的那些参考的值改成99,两边的值同步的变
--不提倡使用,很少这么用
create or replace trigger trig
    after update on dept for each row
begin
    update emp2 set deptno = :NEW.deptno where deptno = :OLD.deptno;
end;
----------------------
update一句话是先检查约束条件,同时也会触发触发器,是先触发触发器还是先检查约束条件?
先触发触发器。


第五十二课52_recursion
--树状结构的存储与展现
BBS帖子本身就是一个树状结构;这个树状结构的存储:帖子有一个id号,后面跟着一个pid,pid就是它父id是谁,这两个字段有了树状结构就存储下来了。
如何把这个树状结构展现出来:存一个id存一个pid,但只有这两个字段在用程序做展现的时候非常的麻烦;用递归方式存储树状结构,往往会增加一些冗余字段
drop table article;
create table article
(
    id number primary key,  --id号不一定是连续的
    cont varchar2(4000),
    pid number,
    isleaf number(1), --0代表非叶子节点,1代表叶子节点
    alevel number(2) --alevel表示级别是哪个级别
)
-------------
insert into article values (1, '蚂蚁大战大象', 0, 0, 0);
insert into article values (2, '大象被打趴下了', 1, 0, 1);
insert into article values (3, '蚂蚁也不好过', 2, 1, 2);
insert into article values (4, '瞎说', 2, 0, 2);
insert into article values (5, '没有瞎说', 4, 1, 3);
insert into article values (6, '怎么可能', 1, 0, 1);
insert into article values (7, '怎么没可能', 6, 1, 2);
insert into article values (8, '可能性是很大的', 6, 1, 2);
insert into article values (9, '大象进医院了', 2, 0, 2);
insert into article values (10, '护士是蚂蚁', 9, 1, 3);
commit;
---------
蚂蚁大战大象
    大象被打趴下了
        蚂蚁也不好过
        瞎说
            没有瞎说
        大象进医院了
            护士是蚂蚁
    怎么可能
        怎么没有可能
        可能性是很大的

第五十三课53_recursion2
使用一个存储过程,把这些数据插到表里面,然后使用一个存储过程展现成上面的形式
create or replace procedure p(v_pid article.pid%type) is
    cursor c is select * from article where pid=v_pid;
begin
    for v_article in c loop
        dbms_output.put_line(v_article.cont);
        if(v_article.isleaf=0) then
            p(v_article.id);
        end if;
    end loop;
end;

SQL>set serveroutput on;
SQL>exec p(0);

怎么产生缩进?
create or replace procedure p(v_pid article.pid%type,v_level binary_integer) is
    cursor c is select * from article where pid=v_pid;
    v_preStr varchar2(1024):='';  --前导字符串
begin
    for i in 1..v_level loop
        v_preStr:=v_preStr||'    ';
    end loop;
    for v_article in c loop
        dbms_output.put_line(v_preStr||v_article.cont);
        if(v_article.isleaf=0) then
            p(v_article.id,v_level+1);
        end if;
    end loop;
end;
SQL>exec p(0,0);    //会把空格自动去掉,改成*就显示

考虑一下怎么样才能把emp那张表的树状结构展现出来?
<!-- 正文结束 -->

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/21231747/viewspace-1115209/ ,如需转载,请注明出处,否则将追究法律责任。

上一篇: 没有了~
下一篇: 没有了~
请登录后发表评论 登录
全部评论

注册时间:2009-03-28