`
guiqing85
  • 浏览: 162750 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

马老师的PQ/SQL学习笔记

阅读更多
--sql structured query language

--DML--Data Manipulation Language--数据操作语言

query information (SELECT),
add new rows (INSERT),
modify existing rows (UPDATE),
delete existing rows (DELETE),
perform a conditional update or insert operation (MERGE),
see an execution plan of SQL (EXPLAIN PLAN),
and lock a table to restrict access (LOCK TABLE).

--DDL--Data Definition Language--数据定义语言
create, modify,drop, or rename objects (CREATE,ALTER,DROP,RENAME),
remove all rows from a database object without dropping the structure (TRUNCATE),
manage access privileges (GRANT,REVOKE),
audit database use (AUDIT,NOAUDIT)
and add a description about an object to the dictionary (COMMENT).

--Transaction Control事务控制语句
save the changes(COMMIT)
or discard the changes (ROLLBACK) made by DML statements.
Also included in the transaction-control statements are statements to set a point or marker in the transaction for possible rollback (SAVEPOINT)
and to define the properties for the transaction (SET TRANSACTION).
Used to manage the properties of the database.
There isonly one statement in this category (ALTER SYSTEM).

--DCL--Data Contro Language--与开发关系不是很密切,用于权限的分配与回收
grant,revoke,data control

--Session Control
control the session properties (ALTER SESSION)
and to enable/disable roles (SET ROLE).

--System Control


--------------------------------------------------------
select的用法

--每个员工的所有信息
select * from emp;
--每个人的部门编号,姓名,薪水
select empno, ename, sal from emp;
--每个人的年薪
select ename, sal*12 from emp;
--计算2*3的值
select 2*3 from emp;
--计算2*3的值(dual)
select 2*3 from dual;
--得到当前时间
select sysdate from dual;
--可以给列起别名,比如求每个人的年薪
select ename, sal*12 annual_sal from emp;
--如果别名中有空格,需要用双引号
select ename, sal*12 "annual sal" from emp;
--如果没有内容,则为空
select ename, sal, comm from emp; /*null*/
--当空字段参与计算,则结果是null
--例如:计算每个人的全年的收入包括月薪和年终奖
select ename, sal * 12 + comm from emp;
--可以将多个字符串拼在一起。比如:求每个人的薪水,格式为smith-sal-123
select ename || '-' || sal || '-' || comm from emp;
--如果字符串中有单引号,需要用另外一个单引号转义,比如:这样一个字符串: he's friend
select ename || 'he''s friend' from emp;


--------------------------------------------------------
--distinct 关键词的用法
--求有哪些个部门
select deptno from emp;
select distinct deptno from emp;
--可以用来修饰多个字段
--求有哪些个部门和job的组合
select deptno , job from emp;
select distinct deptno , job from emp;

--------------------------------------------------------
where关键词的用法
--可以是数值类型的等值判断。比如:求10这个部门的所有员工
select * from emp where deptno = 10;
--可以是字符串类型的等值判断。比如:求叫KING的这个人的信息
select * from emp where ename = 'KING';
--也可以是不等值判断。比如:求薪水小于2000的员工信息
select * from emp where sal > 2000;
--字符串也可以做不等值判断,比如:求所有ename大于'CBA'的员工信息。
select ename from emp where ename > 'CBA';
--求部门不是10的部门
select * from emp where deptno <> 10;
--求薪水在800和1500之间的员工信息
select * from emp where sal between 800 and 1500;
--也可以写成
select * from emp where sal >= 800 and sal <= 1500;

/*这样写则不可以
    --select * from emp where 800 <= sal <= 1500;
*/
--where...in..的用法。比如:求薪水是800或者1500或正2000的员工信息
select ename, empno, sal from emp where sal in (800, 1500, 2000);
--相当于写成这样
select ename, empno , sal from emp where sal = 800 or sal = 1500 or sal = 2000;
--再比如求姓名是KING,SMITH,AA的员工信息
select ename, empno , sal from emp where ename in ('KING', 'SMITH', 'AA');
--求入职时间在20-2月-81之后的员工信息
select ename, hiredate from emp where hiredate > '20-2月-81';

--------------------------------------------------------
--and or not的用法
--求薪水大于1000或者部门在10这个部门的员工信息
select * from emp where sal > 1000 or deptno = 10;
--求薪水不是800或者不是1500或者不是3000的员工信息
select * from emp where sal <> 800 and sal <> 1500 and sal <> 3000;
--也可以这样来写
select * from emp where sal not in (800, 1500, 3000);

--------------------------------------------------------
--like的用法
--求名字中包含ALL这三个字符的员工信息
select ename from emp where ename like '%ALL%';
--求名字中的第二个字母是A的员工
select ename from emp where ename like '_A%';
--特殊字符需要转义。比如:求员工中包含特殊字符%的员工信息
select ename from emp where ename like '%\%%' escape '\';

--------------------------------------------------------
--null的用法
--求没有年终奖的员工
select ename from emp where comm is null;
--求有年终奖的员工
select ename from emp where comm is not null;

--------------------------------------------------------
--order by的用法
--员工信息按照姓名正序排列
select ename, sal from emp order by ename asc; --ascent
--员工信息按照倒叙排列
select ename, sal from emp order by ename desc; --descent
--也可以是多个字段组合排列。例如:员工信息按照部门正序排列,并且按照姓名倒叙排列
select ename, sal, deptno from emp order by deptno asc, ename desc;

--------------------------------------------------------
--function的用法
--把所有姓名变成小写
select lower(ename) from emp;
--把所有姓名变成大写
select upper(ename) from emp;
--求所有人名中包含'a'的员工信息不区分大小写
select ename from emp where lower(ename) like '%a%';
--截取子字符串,比如求Hello的一部分
select substr('Hello', 2) from dual;
--求Hello的一部分,并指明长度
select substr('Hello', 2, 3) from dual;
--求ascii码对应的字符
select chr(65) from dual;
--求字符对应的ascii码
select ascii('中') from dual;
--四舍五入
select round(23.652) from dual;
--四舍五入小数点后面多少位
select round(23.652, 1) from dual;
--四舍五入小数点前面多少位
select round(23.652, -1) from dual;

--------------------------------------------------------
--important!日期转换函数
--------------------------------------------------------
--将当前日期转换成1981-03-12 12:00:00这种形式的字符串
select to_char(sysdate, 'YYYY-MM-DD HH24:MI:SS') from dual;
--将1981-03-12 12:00:00字符串转换成日期
select to_date('1981-03-12 12:00:00', 'YYYY-MM-DD HH24:MI:SS') from dual;
--将每个人的薪水转换成固定格式的字符串
select to_char(sal, 'L00,000.9999') from emp;
--将固定格式的字符串转换成数值
select to_number('$1,250.00', '$9,999.99') from dual;

--null当null参与计算时候,需要要nvl这个函数
select ename, sal*12+comm from emp;
select ename, sal*12+ nvl(comm, 0) from emp;

--------------------------------------------------------
--group function组函数
--求所有人的薪水的总和,平均值,最大值,最小值
select sum(sal) , avg(sal), max(sal) , min(sal) from emp;
--求总的行数
select count(*) from emp;
--求总的行树,(可以指定具体的字段)但如果字段有null值的时候需要小心使用
select count(comm) from emp;
--也可以过滤掉重复的行之后统计行数
select count(distinct deptno) from emp;

--可以指明按照哪个字段进行分组.比如;分部门统计最高薪水
select deptno, max(sal) from emp group by deptno;
--也可以按照多个字段来分组统计,比如:分部门和岗位,统计最高薪水和行数
select deptno, job , max(sal), count(*) from emp group by deptno, job;

--------------------------------------------------------
--重要:出现在select列表中的字段,如果没有在组函数中,那么必须出现在group by 子句中。
--------------------------------------------------------
--select ename, deptno, max(sal) from emp group by deptno;
--select ename, max(sal) from emp;

--求薪水最高的员工姓名
select max(sal) from emp;
select ename, sal from emp where sal = 5000;
select ename from emp where sal = (select max(sal) from emp);

--having从句的用法
--求平均薪水是2000以上的部门
select avg(sal), deptno from emp group by deptno having avg(sal) > 2000;

--------------------------------------------------------
--总结一下select语法
select
from
where
group by
having
order by
--------------------------------------------------------
-- 执行顺序very important!
-- 首先执行where语句将原有记录过滤;
-- 第二执行group by 进行分组;
-- 第三执行having过滤分组;
-- 然后将select 中的字段值选出来;
-- 最后执行order by 进行排序;

--------------------------------------------------------
/*
按照部门分组统计,求最高薪水,平均薪水
只有薪水是1200以上的才参与统计
并且分组结果中只包括平均薪水在1500以上的部门
而且按照平均薪水倒叙排列
*/
select max(sal),avg(sal), deptno
from emp
where sal > 1200
group by deptno
having avg(sal) > 1500
order by avg(sal) desc;
--------------------------------------------------------
/*
把雇员按部门分组,
求最高薪水, 部门号,
过滤掉名字中第二个字母是'A'的,
要求分组后的平均薪水>1500,
按照部门编号倒序排列
*/
select deptno, max(sal)
from emp
where ename not like '_A%'
group by deptno
having avg(sal) > 1500
order by deptno desc;







/* very very important! */
select ename, deptno from emp;
select deptno, dname from dept;

----员工姓名以及员工所在部门的名字同时显示出来
select ename, dname from emp , dept;
select ename, dname from emp , dept where emp.deptno = dept.deptno;

--要求每位雇员的薪水等级
select ename, sal, grade from emp, salgrade where emp.sal >= salgrade.losal and emp.sal <= salgrade.hisal;
select ename, sal, grade from emp e, salgrade s where e.sal between s.losal and s.hisal;

--求工作职位是’PRESIDENT’的雇员姓名,部门名称和薪水等级时
select ename, dname, grade
from emp e, dept d, salgrade s
where e.deptno = d.deptno and e.sal between s.losal and s.hisal
and job = 'PRESIDENT';

--求每位员工的姓名,及其上级经理的姓名
select empno, ename, mgr from emp;
select e1.ename, e2.ename from emp e1, emp e2 where e1.mgr = e2.empno;

--新语法
--在SQL1992的语法规则中,语句过滤的条件和表连接的条件都被放在了where子句中,当条件过多时,容易造成混淆,
--SQL1999修正了这个缺点,将连接条件和数据过滤条件区分开来,

--交叉连接
--结果会产生这两张表的笛卡尔乘积
select ename , dname from emp cross join dept; --inner join

--要用deptno作为等值连接条件,我们可以这样写
select ename, dname from emp join dept using(deptno);
--相当于
select ename, dname from emp join dept on emp.deptno = dept.deptno;
--也可以写成这样
select ename, dname from emp join dept on (emp.deptno = dept..deptno);

--也可以用于非等值连接
--求每位雇员的薪水等级
select ename, sal, grade from emp
join salgrade on (emp.sal >= salgrade.losal and emp.sal <= salgrade.hisal);

--多个join,where组合使用
--(求工作职位是’PRESIDENT’的雇员姓名,部门名称和薪水等级时)
select 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 job = 'PRESIDENT';

--外连接--取出表中连接不到一起的多余的数据
--没有全内连接,没有右内连接

--其中outer也可以省略,简写为left join , right join , full join
--left inner join可以缩写成inner join 也可以缩写成join,意思是左内。
--update emp set deptno=null where ename='SMITH';
--commit;
--左内
select dname,ename from emp left inner join dept using(deptno)
--左外连接
select ename,dname from emp left outer join dept using(deptno)
--右外连接
select ename,dname from emp right outer join dept using(deptno)
--全外连接
select ename,dname from emp full outer join dept using (deptno);
--左外,右外的区别
select e1.ename,e2.ename from emp e1 join emp e2 on(e1.mgr = e2.empno);
select e1.ename,e2.ename from emp e1 left outer join emp e2 on(e1.mgr = e2.empno);
select e1.ename,e2.ename from emp e1 right outer join emp e2 on(e1.mgr = e2.empno);
select e1.ename,e2.ename from emp e1 full outer join emp e2 on(e1.mgr = e2.empno);


--什么时候用外连接呢:
----比如领导向你要所有学生的列表,顺便把所属的班级也列出来,就需要外连接

--在Where语句中使用子查询
-----------------------------------------------------------------
--雇员中最高薪水的人员名称
--1,先求出最高薪水
--2,再求雇员中最高薪水的人员名称
select ename from emp where sal = (
select max(sal) from emp
);

--有哪些人的薪水是在整个雇员的平均薪水之上的
select empno, ename from emp where sal > (select avg(sal) from emp);

-----------------------------------------------------------------
--雇员中哪些人是经理人
--1,首先查询mgr中有哪些号码
--2,再看有哪些人员的号码在此出现
select ename from emp where empno in (
select distinct mgr from emp where mgr is not null
);

-----------------------------------------------------------------
--在From子句中使用子查询
------------------------------------------------------------------

--部门平均薪水的等级
--1,首先将每个部门的平均薪水求出来
--2,然后把结果当成一张表,再用这张结果表和salgrade表做连接,以此求得薪水等级
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);

-----------------------------------------------------------------
--每个部门最高薪水的人员名称
--1,首先将每个部门的最高薪水求出来
--2,然后把结果当成一张表,再用emp和这张结果表做连接,以此求得每个部门最高薪水的人员名称
select ename, sal, emp.deptno from emp join
(select deptno, max(sal) max_sal from emp group by deptno) t
on (emp.sal = t.max_sal and emp.deptno = t.deptno);

-----------------------------------------------------------------
--哪些人的薪水在部门的平均薪水之上
--1,首先将每个部门的平均薪水求出来
--2,然后把结果当成一张表,再用emp和这张结果表做连接,以此求得哪些人的薪水在部门的平均薪水之上
select ename, sal from emp join
(select deptno, avg(sal) avg_sal from emp group by deptno) t
on (emp.sal > t.avg_sal and emp.deptno = t.deptno);

-----------------------------------------------------------------
--求部门中(所有人的)平均的薪水等级,形式如:
--  deptno  avg_grade
--  10      3.67
--  20      2.8
--  30      2.5
--1,先求每个人的薪水等级
--2,再按照部门分组,求平均数
select deptno , avg(grade) from
(select ename, deptno, grade from emp e
join salgrade s on (e.sal between s.losal and s.hisal)
)
group by deptno;

------------------------------------------------------------------------------------------
--使用伪字段:rownum,----------------------
------------------------------------------------------------------------------------------
--用来标识每条记录的行号,行号从1开始,每次递增1
select ename from emp where rownum <= 5;
--oracle下rownum只能使用 < <=, 不能使用 = > >= 等比较操作符,
select ename from emp where rownum > 5;
--当rownum和order by 一起使用时,会首先选出符合rownum条件的记录,然后再排序
--(错误的写法)例如,当我们要求薪水最高的前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;

--------------------------------------------------------
--不准用组函数(即MAX()),求薪水的最高值(面试题)
--第一种解决办法:
--1,先把所有薪水按照倒序排列
--2,再取第一行
select sal from (
select sal from emp order by sal desc
)
where rownum <= 1;

--第二种解决办法:
--1,先跨表查询自己,先求出的结果中,e1.sal不可能出现最大数
--2,然后再not in
select distinct sal from emp where sal not in (
select distinct e1.sal from emp e1 join emp e2 on (e1.sal < e2.sal)
);

-----------------------------------------------------------------
--求平均薪水最高的部门的部门编号
--第一种解决办法:
--1,先求出每个部门的平均薪水,
--2,再求每个部门的平均薪水的最高值,
--3,最后再求第一步结果中avg_sal = 最高薪水的记录.
select deptno from
(select deptno, avg(sal) avg_sal from emp group by deptno) t  
where avg_sal =
(
select max(avg_sal) from
(select deptno, avg(sal) avg_sal from emp group by deptno)
);

--第二种解决办法:
--1,将上面的第一步第二步合并,先求最高平均薪水,用max(avg(sal))的办法
--2,求出每个部门的平均薪水
--3,最后再求第二步结果中(即每个部门的平均薪水),avg_sal = (第一步结果)的记录.即avg_sal =最高薪水的记录.
select deptno from
(select deptno, avg(sal) avg_sal from emp group by deptno) t
where avg_sal = (select max(avg(sal)) from emp group by deptno);

--第三种解决办法:
--1,先求出每个部门的平均薪水,
--2,求最高平均薪水,用max(avg(sal))的办法
--3,再使用having语句, avg(sal) = 第二步的结果
注意:为组函数起的别名在having中不能用
select deptno, avg(sal)
  from emp
group by deptno
having avg(sal) = (select max(avg(sal)) from emp group by deptno);

-----------------------------------------------------------------
--求平均薪水最高的部门的部门名称
--1,部门平均最高薪水
--2,得到部门编号列表,注意用group by deptno
--3,再应用having子句, having avg(sal) = (第一步的结果)
--4,得到平均最高薪水的那个部门的编号
--5,再得到部门名称

select dname from dept where deptno in (
       select distinct deptno from emp group by deptno
              having avg(sal) = (
                     select max(avg(sal)) from emp group by deptno
       )
)



-----------------------------------------------------------------
--求平均薪水的等级最低的部门的部门名称
--第一步:部门平均薪水的等级,分成两个小步骤,第一小步是求部门平均薪水
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)
--第二步:最低的等级值
select min(grade) from (第一步的结果)
--第三步:等于最低值的部门编号
------------有错误,应该是grade=
select deptno from (第一步的结果) where grade = (第二步的结果)
--第四步:求名称
select dname from dept where deptno in (第三步的结果)

select dname from dept where deptno in
  (select deptno from
     (
     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)
     )
   where grade =
   (
   select min(grade) from
     (
       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)
     )
   )
  )

--也可以用视图的方式来解决
--conn sys/bjsxt as sysdba
--grant create table, create view, create sequence to scott
--根据第一步的结果,建立一个view
create view v$_dept_info as
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);
--查看一下
desc v$_dept_info;
--查询一下
select * from v$_dept_info;
--带入view
select dname from dept where deptno in (
    select deptno from (
      v$_dept_info
    )
    where grade = (
      select min(grade) from (
         v$_dept_info
      )
    )
)
-------------------------------------------------------------
--为什么in的后面不能order by ?
select dname from dept where deptno
in
(select deptno
from  salgrade s,( select avg(sal)  avg_sal,deptno from emp group by deptno ) t
where t.avg_sal<=s.hisal and t.avg_sal>=s.losal and rownum=1
order by deptno)

---------------------------------------------------------------
--求部门经理人中平均薪水最低的部门名称 (思考题)


----------------------------------------------------------------------------
--求比普通员工的最高薪水还要高的经理人名称
--1,求所有经理的编号
select distinct mgr from emp where mgr is not null
--2,普通员工的最高薪水
select max(sal)
       from emp
       where empno not in (select distinct mgr from emp where mgr is not null);
--3,
select ename, sal
  from emp
where empno in (第一步的结果)
   and sal >(第二步的结果)
--即:
select ename, sal
  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));


------------------------------------------------------------------------------
--求薪水最高的前5名雇员
--1,先观察一下
select rownum , ename , sal from emp;
--2,看看rownum的作用
select rownum ,ename, sal from emp where rownum <= 5;
--3,不是我们想要的结果
select rownum ,ename, sal from emp where rownum <= 5 order by sal desc;
--4,先order by,再rownum
select rownum ,ename, sal from (select ename, sal from emp order by sal desc) where rownum <= 5;

--------------------------------------------------------------------------------
--求薪水最高的第6到第10名雇员(重点掌握)
--这种没法实现,oracle下rownum只能使用 < <=, 不能使用 = > >= 等比较操作符
--注意里面的rownum和外面的rownum的区别,外面要想访问里面的rownum,必须取得一个别名。
select ename, sal, rownum
  from (select ename, sal from emp order by sal desc)
where rownum >= 6 and rownum <= 10;
--所以再套一层select
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;

--还有一种排序方式
minus

--------------------------------------------------------------------
--练习: 求最后入职的5名员工
--1,每个人的入职时间
select ename, hiredate from emp order by hiredate desc
--2,取前5行
select ename, hiredate
  from (select ename, hiredate from emp order by hiredate desc)
where rownum <= 5;


-----------------------------------------------------------------
--求每个部门中薪水最高的前两名雇员

--1,每个员工的工资,按部门和工资排列
select ename, deptno, sal from emp order by deptno, sal desc
--2,套一层,加上个r
select ename, deptno, sal, rownum r
  from (select ename, deptno, sal from emp order by deptno, sal desc);
--3,创建试图
create view v$_t as
select ename, deptno, sal, rownum r from (select ename, deptno, sal from emp order by deptno, sal desc);
--观察一下
desc v$_t;
select * from v$_t
--每个部门中,薪水最高的第一行,并创建试图
create view v$_t2 as
select deptno, min(r) min_r from v$_t group by deptno;
select * from v$_t2
--两个view跨表连接,大于薪水最高的行数,小于最高的行数+1,并且部门编号要匹配
select ename, sal from v$_t join v$_t2
on (v$_t.r >= v$_t2.min_r and v$_t.r <= v$_t2.min_r + 1 and v$_t.deptno = v$_t2.deptno);

-------------------------------------------------------------------------------
--面试题: 比较效率
  select * from emp where deptno = 10 and ename like '%A%';
  select * from emp where ename like '%A%' and deptno = 10;
 
---------------------------------------------------------
--使用union、minus

--使用union、minus可以用来实现结果集的合并和去除(可以理解为加和减),例如:
select ename, empno from emp where deptno = 10
union
select ename, empno from emp where deptno = 20
--相当于
select ename, empno from emp where deptno = 10 or deptno = 20;

--而下面的语句
select ename, empno from emp where deptno in (10,20)
minus
select ename, empno from emp where sal < 1500
--相当于
select ename, empno from emp where deptno in (10,20) and sal >= 1500;

--求分段显示薪水的个数

select ename, sal from emp where deptno = 10
union
select ename, sal from emp where sal > 2000;

select ename, sal from emp where deptno = 10
minus
select ename, sal from emp where sal > 2000;

如:
scale      total
<800 0
801-1000 2
1001-2000 3
2001-5000 6
>5000 8

select '<800' as scale , count(*) as total from emp where sal < 800
union
select '801-1000' as scale, count(*) as total from emp where sal between 801 and 1000;

--或者显示成为
--注意:使用between .. and .. 的时候,包含了最大和最小值。
800-1000 1001-2000 2000-5000
2 3 6
select * from
(select count(*) as "800-1000" from emp where sal between 800 and 1000),
(select count(*) as "1001-2000" from emp where sal between 1001 and 2000),
(select count(*) as "2001-5000" from emp where sal between 2001 and 5000);

--或显示成为
DEPTNO   800-2000  2001-5000
------ ---------- ----------
    30          5          1
    20          2          3
    10          1          2
   
select t1.deptno, "800-2000", "2001-5000" from
(
select deptno , count(*) as "800-2000" from emp where sal between 800 and 2000
group by deptno
) t1
join
(
select deptno , count(*) as "2001-5000" from emp where sal between 2001 and 5000
group by deptno
) t2
on
t1.deptno = t2.deptno
  
-----------------------------------------------------------------------------------
--每个薪水等级有多少名雇员 ?
--1,先求出每个雇员的薪水等级
select ename, sal, grade from emp e join salgrade s on (e.sal between s.losal and s.hisal)
--2,再group一下
select grade, count(*)
  from (select ename, sal, grade
          from emp e
          join salgrade s on (e.sal between s.losal and s.hisal))
group by grade;










-------------------------------------------------------------------------
有3个表S,C,SC
S(SNO,SNAME)代表(学号,姓名)
C(CNO,CNAME,CTEACHER)代表(课号,课名,教师)
SC(SNO,CNO,SCGRADE)代表(学号,课号成绩)
问题:
1,找出没选过“黎明”老师的所有学生姓名。
2,列出2门以上(含2门)不及格学生姓名及平均成绩。
3,即学过1号课程又学过2号课所有学生的姓名。
请用标准SQL语言写出答案,方言也行(请说明是使用什么方言)。
-----------------------------------------------------------------------------
CREATE TABLE SC
(
  SNO      VARCHAR2(200 BYTE),
  CNO      VARCHAR2(200 BYTE),
  SCGRADE  VARCHAR2(200 BYTE)
);

CREATE TABLE S
(
  SNO    VARCHAR2(200 BYTE),
  SNAME  VARCHAR2(200 BYTE)
);

CREATE TABLE C
(
  CNO       VARCHAR2(200 BYTE),
  CNAME     VARCHAR2(200 BYTE),
  CTEACHER  VARCHAR2(200 BYTE)
);

INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '1', '语文', '张');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '2', '政治', '王');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '3', '英语', '李');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '4', '数学', '赵');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '5', '物理', '黎明');
commit;

INSERT INTO S ( SNO, SNAME ) VALUES ( '1', '学生1');
INSERT INTO S ( SNO, SNAME ) VALUES ( '2', '学生2');
INSERT INTO S ( SNO, SNAME ) VALUES ( '3', '学生3');
INSERT INTO S ( SNO, SNAME ) VALUES ( '4', '学生4');
commit;

INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '1', '40');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '2', '30');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '3', '20');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '4', '80');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '5', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '1', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '2', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '3', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '4', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '5', '40');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '3', '1', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '3', '3', '80');
commit;
------------------------------------------------------------------------
答案:
问题1.找出没选过“黎明”老师的所有学生姓名。
第一步:求黎明老师教的所有课的课号
select distinct cno from c where cteacher='黎明'
第二步:选了黎明老师的所有学生的编号
select sno from sc where cno in (
    第一步的结果
)
第三步:没有选黎明老师的所有学生的姓名
select sname from s where sno not in (
    第二步的结果
)
即:
select sname from s where sno not in (
    select sno from sc where cno in (
        select distinct cno from c where cteacher='黎明'
    )
)
----------------------------------------------------------------------------
问题2:列出2门以上(含2门)不及格学生姓名及平均成绩。
第一步:2门以上不及格的学生的学号
select sno from sc where scgrade < 60 group by sno having count(*) >= 2
第二步:每个学生平均分
select sno, avg(scgrade) as avg_grade from sc group by sno
第三步:第一步中得到的学号对应的学生姓名以及平均分
select s.sname ,avg_grade from s
    join
         第一步的结果
         on s.sno = t.sno
    join
        第二步的结果
        on s.sno = t1.sno
即:
select s.sname ,avg_grade from s
    join
         (select sno, count(*) from sc where scgrade < 60 group by sno having count(*) >= 2)t
         on s.sno = t.sno
    join
        (select sno, avg(scgrade) as avg_grade from sc group by sno )t1
        on s.sno = t1.sno

错误的写法:
错误在于:求的是所有不及格的课程的平均分,而不是所有课程(包括及格的)的平均分
执行顺序:
    首先会执行Where语句,将不符合选择条件的记录过滤掉,
    然后再将过滤后的数据按照group by子句中的字段进行分组,
    接着使用having子句过滤掉不符合条件的分组,
    然后再将剩下的数据排序显示。
select sname, avg_scgrade from s join
(select sno, avg(scgrade) avg_scgrade from sc where scgrade < 60 group by sno having count(*) >= 2) t
on (s.sno = t.sno);

----------------------------------------------------------------------------
问题3:即学过1号课程又学过2号课所有学生的姓名。
第一步:学过1号课程的学号
select sno from sc where cno = 1
第二步:学过2号课程的学号
select sno from sc where cno = 2
第三步:即学过1号课程又学过2号课的学号
select sno from sc where cno =1 and sno in (select sno from sc where cno = 2)
第四步:得到姓名
select sname from s where sno in (
       select sno from sc where cno = 1 and sno in (select sno from sc where cno = 2)
)
或者:
select sname from s where
       sno in (select sno from sc where cno = 1)
       and
       sno in (select sno from sc where cno = 2)












--DML语句
--DML语句用于添加、删除和修改表中的数据,
--包含三条最常用的语句,即Insert、Update和Delete。

--Insert语句插入数据
--方式一:指定字段列表
insert into emp (empno, ename, job, deptno)
       values (9998, 'TEST', 'CLERK', 20);
select * from emp;
--显式的插入一个空值,可以用NULL值来表示,
insert into emp (empno, ename, job, sal , deptno)
       values (9997, 'TEST2', 'CLERK', null, 20);
--省略字段列表
insert into emp
       values (9999, 'TEST3', 'CLERK', '7369', NULL, 8000, NULL, 30);
--应用子查询
--可以先查看一下,当前有几个表
select table_name from user_tables
--创建一个表
create table emp7 as select * from emp;
--查询一下
select * from emp7
--再插入一些结果
insert into emp7 (select * from emp5)
--再查询一下,应该28条记录才对
select * from emp5

--------------------------------------------------------------------------------------
--Update语句更新数据
update emp
       set sal = sal * 1.1;
--修改表中部分的值
--将部门号为10的所有员工的薪水都上涨10%
update emp
       set sal = sal * 1.1
       where deptno = 10;
--更新多个字段
update emp5
       set sal=sal*1.1,ename='aaa'
--应用子查询
--给所有经理涨工资
update emp7 set sal=sal*1.1
       where empno in(
             select distinct mgr from emp7 where mgr is not null
       )

----------------------------------------------------------------------------------
--Delete语句删除数据
--不指定where条件的时候,Delete语句将删除表中全部的数据
select * from emp7;
Delete from emp7;
--删除表中部分数据
Delete from emp2
       Where deptno = 20;
--恢复旧有的数据
--(扣钱、吐钱应该作为一组动作同时完成)
--要么同时完成,要么同时不完成的动作就称为一个Transaction
--大多数数据库系统都支持Transaction,在Oracle中,一个Transaction起始于一条DML语句,结束于以下的几种情况:
--1. 用户显式执行Commit语句提交操作或Rollback语句回退。
--2. 当执行DDL语句或DCL语句时事务自动提交。
--3. 用户正常断开连接时,Transaction自动提交。
--4. 系统崩溃或断电时事务自动回退。
delete from emp where empno = 9998;
select * from emp;
commit;
rollback;














NUMBER[(precision [, scale])]
NUMBER(p, s)

其中p,s都是可选的:
p代表精度,默认为38
s代表小数位数,取值范围-84~127,
默认取值要看是否指定了p,
如果指定了p,默认s为0,
如果没有指定p,默认取最大值。

其他的数值类型都是number的衍生,底层都是number,比如integer/int完全映射到number(38)
默认应该是
number(38,127)

范围: 1 <= p <=38, -84 <= s <= 127
保存数据范围:-1.0e-130 <= number value < 1.0e+126   
保存在机器内部的范围: 1 ~ 22 bytes
有效位:从左边第一个不为0的数算起的位数。
s的情况:
s > 0
   精确到小数点右边s位,并四舍五入。然后检验有效位是否 <= p。
s < 0
   精确到小数点左边s位,并四舍五入。然后检验有效位是否 <= p + |s|。
s = 0
   此时NUMBER表示整数。 

eg:
Actual Data   Specified As  Stored As
----------------------------------------
123.89           NUMBER         123.89
123.89           NUMBER(3)     124
123.89           NUMBER(6,2)   123.89
123.89           NUMBER(6,1)   123.9
123.89           NUMBER(4,2)   exceeds precision (有效位为5, 5 > 4)
123.89           NUMBER(6,-2)  100
.01234           NUMBER(4,5)   .01234 (有效位为4)
.00012           NUMBER(4,5)   .00012
.000127       NUMBER(4,5)   .00013
.0000012      NUMBER(2,7)   .0000012
.00000123     NUMBER(2,7)   .0000012
1.2e-4           NUMBER(2,5)   0.00012
1.2e-5           NUMBER(2,5)   0.00001
123.2564      NUMBER        123.2564
1234.9876     NUMBER(6,2)   1234.99
12345.12345   NUMBER(6,2)   Error (有效位为5+2 > 6)
1234.9876     NUMBER(6)     1235 (s没有表示s=0)
12345.345     NUMBER(5,-2)  12300
1234567       NUMBER(5,-2)  1234600
12345678      NUMBER(5,-2)  Error (有效位为8 > 7)
123456789     NUMBER(5,-4)  123460000
1234567890    NUMBER(5,-4)  Error (有效位为10 > 9)
12345.58      NUMBER(*, 1)  12345.6
0.1           NUMBER(4,5)   Error (0.10000, 有效位为5 > 4)
0.01234567    NUMBER(4,5)   0.01235
0.09999       NUMBER(4,5)   0.09999

--delete from test;
--drop table test;
--create table test(
--       n NUMBER
--);
--insert into test (n) values(123.89);

--select * from test









--第三章 数据库常用对象

  字段类型      中文说明                         限制条件   其它说明    
  CHAR          固定长度字符串                   最大长度2000   bytes          
  --VARCHAR2      可变长度的字符串                 最大长度4000   bytes     可做索引的最大长度749    
  NCHAR         根据字符集而定的固定长度字符串   最大长度2000   bytes          
  NVARCHAR2     根据字符集而定的可变长度字符串   最大长度4000   bytes          
  --DATE          日期(日-月-年)                 DD-MM-YY(HH-MI-SS)   经过严格测试,无千虫问题    
  LONG          超长字符串                       最大长度2G(231-1)   足够存储大部头著作    
  RAW           固定长度的二进制数据             最大长度2000   bytes     可存放多媒体图象声音等    
  LONG RAW      可变长度的二进制数据             最大长度2G   同上    
  BLOB          二进制数据                       最大长度4G        
  --CLOB          字符数据                         最大长度4G        
  NCLOB         根据字符集而定的字符数据         最大长度4G        
  BFILE         存放在数据库外的二进制数据       最大长度4G        
  ROWID         数据表中记录的唯一行号           10   bytes   ********.****.****格式,*为0或1    
  NROWID        二进制数据表中记录的唯一行号     最大长度4000   bytes    
  --NUMBER(P,S)   数字类型                         P为整数位,S为小数位    
  DECIMAL(P,S)  数字类型                         P为整数位,S为小数位    
  INTEGER       整数类型                         小的整数    
  FLOAT         浮点数类型                       NUMBER(38),双精度    
  REAL          实数类型                         NUMBER(63),精度更高 

-------------------------------------------------------------------------------------------------------
Category     Datatypes
Character     CHAR, NCHAR, VARCHAR2, NVARCHAR2
Number         NUMBER
Long and raw LONG, LONG RAW, RAW
Date and time DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE,TIMESTAMP WITH LOCAL TIME ZONE, INTERVAL YEAR TO MONTH, INTERVAL DAY TO SECOND
Large object CLOB, NCLOB, BCLOB, BFILE
Row ID         ROWID, UROWID


------------------------------------------------------------------------------------------------------------
--Varchar2(n) 变长字符串,存储空间等于实际空间的数据大小,最大为4K
--Char(n) 定长字符串,存储空间大小固定
--Number(p,s) 整数或小数 
----ps参数的含义
--Date 年、月、日、时、分、秒
--Long 变长字符串,最大字节数达到2GB
--Oracle还提供了二进制和字节大对象,即BLOB、CLOB等类型,常用于二进制数据的保存,例如图片、音频、视频等等,
--BLOB
--CLOB
---------------------------------------------------------------------------------------------------------------
--varchar & varchar2 & nvarchar & nvarchar2区别
1,varchar早就建议不使用了,应该用VARCHAR2。nvarchar估计也不使用的,也应该是使用nvarchar2。
2,VARCHAR2(size),可变长度的字符串,其最大长度为 size 个字节。
size 的最大值是 4000,而最小值是 1。您必须指定一个 VARCHAR2 的 size。
3,NVARCHAR2(size),可变长度的字符串,依据所选的国家字符集,其最大长度为 size 个字符或字节。
size 的最大值取决于存储每个字符所需要的字节数,其上限为 4000 个字节。
您必须为 NVARCHAR2 指定一个 size。
4,VARCHAR2与NVARCHAR2的不同之处在于它们存放信息占用的空间不同。
VARCHAR2存放的英文字符只占一个字节,而nvarchar2依据所选的字符集,大多为两个。
5,当你的数据库字符集不是unicode,比如ZHS16GBK,
但又想在个别字段存储诸如中文冷僻字的unicode字符时,
就可以选择nvarchar2,但必须9i版本以上的.
6,nvachar定义的是字符数,varchar定义的是字节数
所以nvachar(10)放入varchar(10)就会出现此错误,因为varchar(10)只能放10个字节的数据

select lengthb('錒') from dual;
-------------------------------------------------------------------------------------------------------------------
3.1.1创建表
--建立一张用来存储学生信息的表,
drop table stu;
select * from stu;
rollback;
create table stu(
    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)
);
--在创建表的时候,我们还可以使用子查询来参考现有的一张表
create table emp2
    as
    select * from emp;
--在子查询中使用任何查询的语法
create table emp_dept
    as
    select ename, dname from emp join dept using (deptno);

--创建表时指定约束条件
--1. NOT NULL 非空
--2. UNIQUE KEY 唯一
--3. PRIMARY KEY 主键
--4. FOREIGN KEY 外键
--5. CHECK 自定义检查约束

--非空约束
要求姓名不能为空
create table stu(
    id number(6),
    name varchar2(20) not null,
    sex number(1),
    age number(3),
    sdate date,
    grade number(2) default 1,
    class number(4),
    email varchar2(50)
);
--可以给这个约束起一个名字:
create table stu(
    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)
);

--唯一性约束
--email字段不应该有重复值
--unique约束只是代表该字段的值不能重复,但空值是允许的
create table stu(
    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
);
--可以给unique约束起一个名字:
create table stu(
    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_uni unique
);
--还可以将约束加在字段定义完毕后
create table stu(
    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_uni unique(email)
);
--也可以在表级约束中用一个约束条件同时约束多个字段,
--比如我们要求email和名字的组合值不能重复
create table stu(
    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_uni unique(name, email)
);

--主键约束
--主键可以用来唯一的标识一条记录,
--主键相当于非空约束和唯一约束的组合,
--在一张表中只允许有一个主键,
--这个主键可以是一个或者多个子段的组合
create table stu(
    id number(6) primary key,
    name varchar2(20) constraint stu_name_nn not null,
    …
    email varchar2(50),
    constraint stu_email_uni unique(name, email)
);
--主键可以定义在多个子段上,此时用这些字段的组合来标识唯一的一条记录,
--语法上只能够采用表级约束
create table stu(
    id number(6),
    name varchar2(20) constraint stu_name_nn not null,
    …
    email varchar2(50),
    constraint stu_email_uni unique(name, email),
    constraint stu_id_name_pk primary key (id, name)
);


--外键约束
--外键约束建立于一张表的两个字段或两张表的两个字段上,
--用于保证两个字段的关系,即:
----子表外键字段的值必须在参照字段的取值中存在,
----并且,如果字段的值被别的字段参照时,记录不允许被删除。
--外键约束被参考的字段必须是主键!

--违反完整约束条件
insert into emp (empno, ename, job, deptno)
       values (9997, 'TEST', 'CLERK', 80);

--违反完整约束条件
delete from dept where deptno = 10;
select * from dept;


--建立另外一张表class,用来表示班级的信息,
create table class(
    id number(4) primary key,
    name varchar2(20) not null
);
--在创建stu表时加入外键约束:
--drop table stu;
create table stu(
    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_email_uni unique
);
--也可以采用表级约束语法:
create table stu(
    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_uni unique,
    constraint stu_class_fk foreign key (class) references class(id)
);
--违反完整约束条件
insert into stu
       values (1, 'test', 0, 22, to_date('2005-03-02' , 'YYYY-MM-DD'), 1, 1, 'test@test.com')

--想插入stu表中任何的class的值,必须在class表中id字段找到,因此,我们首先执行下面的语句:
insert into class values (1, 'Class1');

--当stu表中已经有记录参照了class表中id为1的记录时,违反完整约束条件
delete from class where id = 1;

--Check约束
--Check约束用于检验字段的值是否符合某个条件表达式,

--要求插入stu表中年龄字段的值不能为负数
create table stu(
    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_uni unique,
    constraint stu_class_fk foreign key (class)references class(id),
    constraint stu_age_min check (age > 0)
);
--在Check约束中,条件表达式可以使用and、or、not等逻辑操作符。

create table stu(
    id number(6) primary key,
    name varchar2(20) not null,
    sex number(1),
    age number(3),
    sdate date,
    grade number(2) default 1,
    class number(4),
    email varchar2(50) unique,
    constraint stu_class_fk foreign key (class)references class(id),
    constraint stu_age_min  check (age > 0)
);


--------------------------------------------------------------------------------------
--修改表结构或其约束条件-->即alter table语句,
--增加字段-----给stu表增加一个家庭住址的字段
alter table stu add (addr varchar2(100));
--删除字段-----例如要讲我们刚才新增的addr字段进行删除,
alter table stu drop (addr);
--修改字段-----例如我们要修改新增的addr字段的精度
--注意如果原来该字段中没有任何数据,那么可以将字段修改为任何类型,否则,所修改的类型必须能够容纳原有的数据。
alter table stu modify (addr varchar2(150));
--删除或增加约束条件
--删除stu表中的外键约束stu_class_fk,可以这样写:
alter table stu
      drop constraint stu_class_fk;
--加入新的约束:
alter table stu
      add constraint stu_class_fk foreign key (class) references class(id);
--注意-----非空约束不能够使用类似的语法,只能够通过修改字段来同时修改约束,例如:
--将name字段上的非空约束去除:
alter table stu modify (name varchar2(20) null);
--在name字段上添加非空约束:
alter table stu modify (name varchar2(20) not null);
--当无法确定一个约束的名字的时候,可以向系统表中寻找,有关系统表的操作,请参考附录《数据库常用DBA操作》,也可以将原来的表删除,然后建立新的表。
--select * from user_objects

--删除表
Drop table stu;

--索引是为了加快对数据的搜索速度而设立的,
--对stu表中的email字段建立索引
create index idx_stu_email on stu(email);
--对两个字段的组合建立索引,
create index idx_stu_email_class on stu(email, class);
--我们想删除刚刚建立的索引,可以这样写:
drop index inx_stu_email_class;

--视图
--不用视图-->非常复杂的查询语句(求平均薪水的等级最低的部门的部门名称):
select dname, grade from
    (select deptno, avg_sal, grade from
      (select deptno, avg(sal) avg_sal from emp group by deptno) t,
      salgrade s
    where
      t.avg_sal between s.losal and s.hisal )
   t1,
   dept
where t1.deptno = dept.deptno
   and
   t1.grade =
       (select min(grade) from
           (select deptno, avg_sal, grade from
                   (select deptno, avg(sal) avg_sal from emp group by deptno) t,
                   salgrade s
               where
                   t.avg_sal between s.losal and s.hisal )
       );
--建立视图
create view v$_temp as
    (select deptno, avg_sal, grade from
         (select deptno, avg(sal) avg_sal from emp group by deptno) t,
         salgrade s
     where
         t.avg_sal between s.losal and s.hisal );
--上面的查询就可以简化为:
select dname from dept, v$_temp where
v$_temp.deptno = dept.deptno
and grade = (select min(grade) from v$_temp);

--适当的利用视图,可以使我们的查询变得简单,
--但是如果视图建立的太多的话,会给我们系统的维护带来麻烦,
----比如如果表的结构了,与这张表有关的任何视图必须跟着修改,

--删除一张视图的时候,
drop view v$_temp;

--序列----->序列是oracle专有的对象,它用来产生一个自动递增的数列,

create table article(
  id number,
  title varchar2(1024),
  content long
);
---创建序列
create sequence seq_article_id start with 1 increment by 1;
---使用序列
----select * from article
insert into article
       values (seq_article_id.nextval, 'TEST', 'test content');
      
      
--删除序列
drop sequence seq_article_id;










--数据库设计
--BBS需求
--1,可以登陆,可以退出
--2,用户可以注册
--3,所有人可以发帖,可以回帖
--4,划分不同的板块
--5,每个板块有一个或多个版主,一个人可以是多个板块的版主
--select * from user;

create table tbl_user(
us_id number(6) primary key,
us_username varchar2(20),
us_password varchar2(20),
us_rdate date
);
create table tbl_board(
bd_id number(6) primary key,
bd_name varchar2(200),
bd_masterid varchar2(200)
);
create table tbl_topic(
tp_id number(6) primary key,
tp_title  varchar2(200),
tp_content varchar2(4000),
tp_us_id number(6) references tbl_user(us_id),
tp_bd_id number(6) references tbl_board(bd_id),
tp_pid  number(6),
tp_pdate date,
tp_rootid number(6)
);


--树状结构的设计
1:id - pid

2:id - pid - isleaf - level - childcount
      递归效率增加
      插入、删除、修改效率降低
      支持平板形式支持的不好

3:id - pid - isleaf - level - childcount - rootId
平板形式好实现

4:id pid rootid 代表字符串(代表数字)
      最多每个节点能有99个小孩
      最多有3层
     
      01 00 00 第一层第一个  01 00 00
      01 01 00 第一个回复      01 01 00
      01 02 00                   01 01 01
      01 02 01                 01 02 00
      01 01 01                   01 02 01

--权限设计

1: 资源Resource
   帖子,用户信息,板块信息
2: 权限privilege
   发帖,删贴,回复帖,查询贴,添加用户,删除用户,查询用户,修改用户密码,修改用户信息,......
2: 角色role,可以赋予多个权限
   一系列权限的组合
3: 用户对应一到多个权限
1:用户登陆
2:验证属于什么角色
3:检查该角色是不是具备操作权限

--简化的权限设计:用户表,角色表
--思考那些人会使用该系统?
--要站在使用者的角度去分析
create table tbl_role(
rl_id number(6) primary key,
rl_name varchar2(20),
rl_code date
);
create table tbl_user(
us_id number(6) primary key,
us_username varchar2(20),
us_password varchar2(20),
us_rdate date,
us_rl_id number(6) references tbl_role(rl_id),
);











declare - 可选
声明各种变量或游标的地方。
begin - 必要
开始执行语句。
--单行注释语句用两个连在一起的'-'表示。
/*多行注释语句,
可以换行*/
exception - 可选
出错后的处理。
end; - 必要(请注意end后面的分号)
结束。

-----------------------------------------------------------------------------
--最简单的语句块
begin
dbms_output.put_line('HelloWorld!');
end;

set serveroutput on;

-------------------------------------------------------------------------------
--简单的PL/SQL语句块

declare
v_name varchar2(20);
begin
v_name := 'myname';
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;


--在变量声明时需要遵守一些基本的规则:
1. 变量名不能够使用保留字,如from 、select等
2. 第一个字符必须是字母
3. 变量名最多包含30个字符
4. 不要与数据库的表或者列同名
5. 每一行只能声明一个变量
--PL/SQL中的变量类型主要有以下几种:
1. binary_integer:整数,主要用来计数而不是用来表示字段类型
2. number:数字类型
3. char:定长字符串
4. varchar2:变长字符串
5. date:日期
6. long:长字符串,最长2GB
7. boolean:布尔类型,可以取值为true、false和null值

--变量声明
declare
v_temp number(1):=5;
v_count binary_integer := 0;
v_sal number(7,2) := 4000.00;
v_date date := sysdate;
v_pi constant number(3,2) := 3.14; --相当于java里面的final
v_valid boolean := false;
v_name varchar2(20) not null := 'MyName';
begin
dbms_output.put_line('v_temp value:' || v_name);
end;

--变量声明,使用%type属性
declare
    v_empno number(4);
v_empno2 emp.empno%type; --好处,表的定义变了,这里跟着变
v_empno3 v_empno2%type;
begin
    dbms_output.put_line('Test');
end;

--简单变量赋值
declare
v_name varchar2(20);
v_sal number(7,2);
v_sal2 number(7,2);
v_valid boolean := false;
v_date date;
begin
v_name := 'MyName';
v_sal := 23.77;
v_sal2 := 23.77;
v_valid := (v_sal = v_sal2);
v_date := to_date('1999-08-12 12:23:38', 'YYYY-MM-DD HH24:MI:SS');
  --dbms_output.put_line(v_sal == v_sal2);
end;

--复合变量
--Table变量类型
--相当于java里面的数组
declare
       --声明了一个数组类型,约定俗成以type_开头
       --下标的类型是binary_integer类型
    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(200) := 9999; --下标可以取负值
dbms_output.put_line(v_empnos(200));
end;

--Record变量类型
--相当于java里面的类
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;

--使用%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;


--SQL语句的运用
--select必须返回一条记录并且只能返回一条记录
--select必须和into一起用
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;
dbms_output.put_line(v_ename || ' ' || v_sal);
end;
--ORA-01403: 未找到数据
declare
v_ename emp.ename%type;
v_sal emp.sal%type;
begin
  select ename,sal into v_ename,v_sal from emp where empno = 9999;
dbms_output.put_line(v_ename || ' ' || v_sal);
end;
--实际返回的行数超出请求的行数
declare
v_ename emp.ename%type;
v_sal emp.sal%type;
begin
  select ename,sal into v_ename,v_sal from emp where deptno = 30;
dbms_output.put_line(v_ename || ' ' || v_sal);
end;

--v_emp可以存贮一条记录
declare
v_emp emp%rowtype;
begin
select * into v_emp from emp where empno = 7369;
dbms_output.put_line(v_emp.ename);
end;




--insert
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;

select * from dept2;

--sql%rowcount
declare
v_deptno emp2.deptno%type := 30;
v_count number;
begin
    --update emp2 set sal = sal/2 where deptno = v_deptno;
    --delete from emp2 where deptno = 10;
--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;

rollback;

--PL/SQL使用DDL语句
--必须写execute immediate......
begin
    execute immediate 'create table T (nnn varchar2(20) default ''aaa'')';
end;

drop table T;
-------------------------------------------------------------------------------------------------------------------

--if语句
--取出7369的薪水,如果<1200,则输出'low',如果<2000则输出'middle',否则'high'
--注意elsif的写法
--注意else后面没有then
--注意end if后面有一个分号
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;

--IF
--FI

--循环
--第一种
declare
   i binary_integer := 1;
begin
   loop
      dbms_output.put_line(i);
  i := i + 1;
  exit when ( i >= 11);
   end loop;
end;

--第二种
declare
   j binary_integer := 1;
begin
   while j < 11 loop
      dbms_output.put_line(j);
  j := j + 1;
   end loop;
end;

--第三种
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;

---------------------------------------------------------------------

--错误处理1
--这条select,返回多条记录,会产生异常
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;
--错误处理2
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;
--------------------------------------------------------------------------------------------------------------------
--游标 (重点)

--open c 的时候,才执行select语句
declare
cursor c is
select * from emp;
v_emp c%rowtype;
begin
open c;
fetch c into v_emp;
dbms_output.put_line(v_emp.ename);
close c;
end;
--最常用的游标属性有以下四个:
--1. %isopen,boolean类型变量,用来代表游标是否打开。
--2. %notfound,boolean类型变量,如果最近的fetch语句没有返回一条记录,取true。
--3. %found,boolean类型变量,如果最近的fetch语句取到了记录,取true。
--4. %rowcount,number类型变量,用来代表目前fetch到的记录的总行数。

declare
   cursor c is
      select * from emp;
   v_emp c%rowtype;
begin
   open c;
   loop
    
      fetch c into v_emp;
      exit when (c%notfound);
    dbms_output.put_line(v_emp.ename);--如果这行挪到上边,最后一行打印两遍
 
   end loop;
   close c;
end;
--while
declare
   cursor c is
      select * from emp;
   v_emp c%rowtype;
begin
   open c;
   fetch c into v_emp;
   while (c%found) loop
  dbms_output.put_line(v_emp.ename);
  fetch c into v_emp;--如果这行挪到上边,最后一行打印两遍
   
   
   end loop;
   close c;
end;
--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;

--带参数的游标
--声明两个行参
--v_temp c%rowtype;--不需要声明
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;
   --v_temp c%rowtype;
begin
   --open c(30, 'CLERK');
   for v_temp in c(30, 'CLERK') loop
      dbms_output.put_line(v_temp.ename);
   end loop;
  
end;

--可更新的游标
--用的不多
--注意:for update
--注意游标的用法:where current of c,
--判断两个值是不是相等,用一个等号
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;
----------------------------------------------------------------------------------------------------------------------
--存储过程
--带名字的plsql程序块
create or replace procedure p
is
cursor c is
select * from emp2 for 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
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics