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

Oracle学习 笔记

    博客分类:
  • java
阅读更多
1、  Day01; 10
a)  知识了解; 10
i.  Oracle数据库的安装与配置; 10
b)  登录数据库的方式; 20
1.  sqlplus或者sqlplusw 21
2.  cmd(sqlplus scott/zgl) 21
3.  sqlplus dev 21
c)  登录数据库的常用的用户; 21
4.  sys/zgl (网络管理员) 22
5.  system/zgl (本地管理员) 22
6.  scott/zgl (用户) 22
7.  sh/zgl (大数据用户) 22
d)  登录验证的方式; 22
ii.   系统验证; 22
8.    sys用户使用系统验证.在操作系统的用户组里有一个ora_dba的组,里面有一个用户; 22
9.    如果你用ora_dba组里的用户登录操作系统,并且用sysdba身份登录数据库,可以直接登录成功; 22
iii.   密码文件验证; 22
10.   解锁用户 23
11.   如果有需要,重置/修改密码; 23
12.   切换用户; 23
13.   查看当前用户名; 23
e)  表结构; 23
f)   表关系; 23
g)  了解范式; 24
h)  学习Oralce需要学习的语言分类; 24
iv.   SQL语言;后面会一一讲解 24
v.   sql*plus命令 ; 24
14.    缓存命令; 24
15.    文件命令; 25
16.    其他命令; 25
vi.   pl/sql语言; 25
vii.  SQL语句书写需要注意的地方; 26
viii.  用户, schema, 数据库对象; 26
ix.   查看当前用户有哪些数据库表; 26
i)  select语句; 26
x.  语法; 26
j)  select子句; 27
xi.  去除重复的行;(distinct) 27
xii.  全名; 27
17.  表全名; 27
18.  字段全名; 27
xiii.  别名; 28
19.  字段别名; 28
20.  表别名; 28
k)  from子句; 28
l)  where子句; 28
xiv.  非; ! 29
21.  !=; 29
xv.  模糊查询; like 29
22.  _; 匹配一个字符 29
23.  %;匹配多个字符 29
xvi.  范围; in,is null,not 29
xvii.  区间查询; 29
24.  between..and..; 29
25.  not between..and..; 30
2、  Day02; 30
xviii.  连接查询; 30
26.  内连接; 30
27.  自连接; 30
28.  自然连接; 31
29.  外连接; 31
30.  全连接; 31
xix.  子查询(嵌套查询); 31
31.  子查询的位置; 32
32.  子查询的返回结果; 32
xx.  分页查询; 32
33.  MySQL : limit; 33
xxi.  select 的分组 group by; 33
34.  group by 的使用限制; 34
xxii.  分组函数,聚合函数,多行函数.  函数的概念:类似于高级语言中有返回值的方法; 34
35.  count() , 统计行(非空)数; 34
36.    sum() , 求分组后的总和; 34
37.    avg() , 求分组后的平均值; 34
38.    max() , 求分组后的最大值; 34
39.    min() , 求分组后的最小值; 34
xxiii.  having子句,分组后再做筛选; 34
xxiv.  ORDER BY 子句, 排序,可以排数字,也可以排字符串; 35
40.   可以对过个字段排序; 35
41.  排字符,按照字母顺序升序或者降序排列; 35
xxv.  例题; 35
3、  Day03; 36
m)  函数; 36
xxvi.  语法;Function_name[arg1,([arg2,…argn])] 36
xxvii.  dual表; 36
n)  内置的单行函数; 37
xxviii.  字符函数; 37
42.  upper(str) 将字符串转换成大写; 37
43.  lower(str)  将字符串转换成小写; 37
44.  initcap(str);  将各个单词的首字母换成大写 37
45.  concat(str1,str2);  将字符串连接起来。等同于 || 37
46.  length(str);  球字符串的长度 38
47.  substr(str,index[,length]); 38
48.  instr (str1,str2);  返回str2在str1中的位置 38
49.  trim(str);  去除前后空格 38
50.  trim(char from str);  去除字符串最前或者最后的字符 39
51.  replace(str,old_str,new_str);  替换字符串 39
52.  lapd(str1,len,str2)\lapd(str1,len,str2);  左补全和右补全字符; 39
xxix.  数字函数; 40
53.  round(num[,p]);  根据小数点精度(p)四舍五入数字 40
54.  trunc(num[,p]);  40
55.  mod(num1,num2);  40
xxx.  日期函数; 41
56.  MONTHS_BETWEEN(date1,date2); 41
57.  ADD_MOUNTHS(date,num); 41
58.  NEXT_DAY(date,day); 41
59.  LAST_DAY(date); 42
60.  ROUND(date,参数) 42
61.  TRUNC(date,参数); 43
62.  对日期的补充; 44
63.  日期的算术运算; 45
xxxi.  转换函数; 45
64.  to_char(value,fmt); 46
65.  to_char(num,fmr) 数字到字符串的转换; 48
66.  to_number(); 48
xxxii.  通用函数; 49
67.  nvl 空值转换函数; 49
xxxiii.  DML : 数据操作语言; 50
68.  insert: 插入数据; 50
69.  delete:删除数据,0~多条数据; 50
70.  update:更新数据,0~多条数据; 51
xxxiv.  事务: 由一些列DML语言组成; 51
71.   commit; 52
72.   rollback; 52
73.   savepoint; 52
74.   事务自动提交; 52
75.  事务自动回滚; 52
76.  事务隔离带来的现象; 52
77.  事务隔离级别; 53
4、  Day04; 53
o)  DDL; 53
xxxv.  数据库对象的命名规则; 53
xxxvi.  常用的数据类型; 54
xxxvii.  创建表; 54
78.  如何使用默认值; 54
79.  约束Constructs; 54
80.  约束的定义; 55
81.  查看指定表的约束信息; 55
82.  查看指定列的约束信息; 55
83.  约束详解; 56
84.  表关系; 57
85.  主键外键关联中的表现; 58
xxxviii.  从表中删除约束; 59
xxxix.  启用表中的约束; 59
xl.  禁用表中的约束;  59
xli.  用子查询向表中插入数据; 59
p)  修改表; 60
xlii.  改表名; 60
q)  drop表; 60
r)  截断; 60
xliii.  同:两者都是把表中的数据删除; 61
xliv.  异:前者,删除当前表中数据所占用数据文件的空间.后这只删数据,空间不删; 61
s)  垃圾表的处理; 61
86.  查看回收站; 61
87.  清空回收站; 61
88.  清除回收站中指定的表; 61
89.  恢复指定的表(闪回); 61
90.  闪回和真删除表时的表现; 62
t)  视图; 62
xlv.  视图的作用; 62
xlvi.  视图的分类; 62
91.  简单视图; 62
92.  复杂视图; 62
xlvii.  视图创建的方法; 62
93.  说明一下:在视图中必须的部分 63
xlviii.  对视图的DML操作 insert update  delete; 64
94.   emp_sal_view -- 是简单视图; 64
95.   emp_dept_view -- 复杂视图; 64
xlix.  只读视图; 65
l.  检查视图;   65
li.  临时视图; 65
lii.  用管理者赋予创建视图的权限给scott 65
u)  序列; 65
liii.  序列的概念; 65
liv.  如何创建序列; 66
96.  语法; 66
97.  创建序列发生器的以下几种方法; 66
98.  在student表中使用序列发生器; 67
lv.  使用序列; 67
99.  使用序列给表中的主键插入; 67
lvi.  查看序列的属性; 68
lvii.  删除序列; 68
lviii.  修改序列; 68
lix.  序列使用上的注意事项; 69
v)  索引(index); 69
lx.  概念; 69
lxi.  索引的创建方式; 69
lxii.  查看索引的字典; 69
lxiii.  给表中某个字段添加索引; 70
lxiv.  使用索引; 70
lxv.  何时是否要创建索引; 70
100.  创建; 70
101.  不创建; 70
lxvi.  索引删除; 71
5、  Day05; 71
w)  PLSQL的结构; 71
x)  课前准备; 72
lxvii.  标识符; 72
102.  标识符的规则; 72
lxviii.  字符集; 72
lxix.  数据类型; 72
103.  数据类型分为;               73
104.  变量的声明; 73
105.  变量的使用; 75
106.  %type; 77
lxx.  组合数据类型 1. record; 77
107.  %rowtype; 79
108.  record 于rowtype的异同; 80
lxxi.  组合类型2: table; 80
109.  创建语法; 80
lxxii.   plsql中的控制语句; 81
110.  条件控制; 82
111.  循环控制; 83
112.  循环补充; 87
lxxiii.  游标cursor; 88
113.    声明; 89
114.  打开 open 与关闭 close; 89
115.  使用游标,从游标中取值 fetch; 89
116.  游标属性的使用; 91
lxxiv.  异常处理; 94
117.  ZERO_DIVIDE--除数为0异常; 95
118.  NO_DATA_FOUND --无数据异常; 95
119.  TOO_MANY_ROWS--返回太多行; 96
120.  VALUE_ERROR--值错误异常; 96
i.  异常的捕获; 96
ii.  自定义异常; 97
iii.  练习; 98
6、  Day06; 100
y)  PlSQL子程序; 100
iv.  那么我们先看看那过程的创建于法: 100
121.  参数定义的方式; 100
122.  过程体的说明; 101
123.  过程体创建的例子; 101
v.  调用过程; 102
124.  在plsql中调用; 102
125.  命令行调用; 102
vi.  删除过程; 102
vii.  查看当前用户下的所有过程; 102
viii.  有名块和无名块; 102
z)      过程或函数中参数的输入输出模式; 103
ix.  in; 103
x.  out; 103
xi.  in out; 103
aa)  查看过程,函数的错误命令; 105
xii.  练习; 105
bb)  调用过程或函数的传参方式; 106
cc)  函数 function; 107
xiii.  函数和过程的区别是; 107
xiv.  函数于法的创建; 107
126.  例子:创建一个无参的函数; 107
127.  例子:创建一个有参的函数; 108
128.  例子:有参数的函数. 接受员工编号,姓名然后插入到emp表中; 108
129.  例子:有参数的函数. 接受员工编号,姓名然后插入到emp表中; 109
dd)  函数调用的注意事项; 110
ee)  包PACKAGE; 110
xv.  包体的结构; 110
xvi.  调用包里的函数、过程、类型、变量; 111
130.  创建包头; 111
131.  使用包中的内容; 111
xvii.  创建包体的语法; 112
ff)  包内部的函数,过程的重载 113
gg)  触发器; 115
xviii.  触发器的分类; 115
xix.  触发器的创建语法; 115
xx.  *级联更新触发*(记住); 116
7、  详细介绍触发器(触发器的使用); 118
hh)  触发器; 118
xxi.  数据库领域名词; 118
xxii.  创建触发器的SQL语法; 119
xxiii.  触发器的优点; 119
xxiv.  比较触发器与约束; 119
xxv.  慎用触发器; 121
xxvi.  触发器命名规则; 121
ii)  触发器应用; 121
xxvii.  功能; 121
xxviii.  触发器的组成部分; 121
132.  触发器名称; 122
133.  触发语句; 122
xxix.  触发器类型; 123
134.  语句触发器; 123
135.  行触发器; 125
136.  INSTEAD OF 触发器更新视图 128
137.  模式触发器; 129
138.  数据库触发器; 131
xxx.  禁用和启用触发器 133






















1、 Day01;
a) 知识了解;
DBMS:数据库管理系统
DB:数据库
i. Oracle数据库的安装与配置;
如下:
















b) 登录数据库的方式;
有如下几种方式:
1. sqlplus或者sqlplusw
2. cmd(sqlplus scott/zgl)
3. sqlplus dev
c) 登录数据库的常用的用户;
有如下几种用户:
4. sys/zgl (网络管理员)
5. system/zgl (本地管理员)
6. scott/zgl (用户)
7. sh/zgl (大数据用户)
d) 登录验证的方式;
ii. 系统验证;
8. sys用户使用系统验证.在操作系统的用户组里有一个ora_dba的组,里面有一个用户;
9. 如果你用ora_dba组里的用户登录操作系统,并且用sysdba身份登录数据库,可以直接登录成功;
iii. 密码文件验证;
解锁用户的步骤:
用管理员身份登录到DBMS
10. 解锁用户
alter user <userName> account unlock;
alter user <userName> account lock;锁定用户
11. 如果有需要,重置/修改密码;
alter user <userName> identified by <password>;
alter user <userName> password exprie; 使密码失效
12. 切换用户;
conn <userName>/<password>;
13. 查看当前用户名;
show user;
e) 表结构;
可以使用desc tablename来显示表的详细信息
f)  表关系;
有如下几种关系:
1:1
1:m
m:n
g) 了解范式;

h) 学习Oralce需要学习的语言分类;
iv. SQL语言;后面会一一讲解
DML:数据操作语言 insert,update,delete
DDL:数据定义语言 create,drop,alter
DCL:数据控制语言 grant,revoke
TL:事务语言 commit,rollback,savepoint
DQL:数据检索语言 select
v. sql*plus命令 ;
14. 缓存命令;
l[ist] 调出最近一次执行过的SQL语句. (有行号:让我们可以选择某一行进行修改;行号后面的*表示选中行;输入行号进行选择)
a[ppend] newStr 在选中行尾追加新字符串. append ,dept
i[nput] newStr 在选中行下追加新字符串. input ,salgrade
c[hange] /oldStr/newStr
del 删除选中行
/  或者  r 执行缓存内的SQL语句.
clear buffer 清空缓存.
15. 文件命令;
save 文件路径\文件名 把缓存中的sql保存到指定的文件中
start 文件路径\文件名 执行指定文件里的sql
@文件路径\文件名 同上
get 文件路径\文件名 把指定文件中的内容加载到缓存中.
ed 把缓存中的内容用记事本形式打开,可以通过记事本的形式编辑缓存的SQL,保存后可以执行新的SQL
ed 文件路径\文件名 用记事本打开指定的文件.
spool 文件路径\文件名 开始录制屏幕内容
spool off 结束屏幕录制
16. 其他命令;
conn userName/password
set pagesize 25 设置一页显示高度. 默认是14
set linesize 设置行宽
clear screen 清屏
set server ouput on/off 打开终端输出
desc tableName 展示表结构
exit 退出终端
SQL> show user --显示当前连接用户
   SQL> set pause on --默认为OFF,设置暂停,会使屏幕显示停止,等待按下ENTER键,再显示下一页
   SQL> set autocommit ON --设置是否自动提交,默认为OFF  
SQL> show all --查看所有68个系统变量值    
SQL> show error --显示错误
SQL> set heading off --禁止输出列标题,默认值为ON
SQL> set feedback off --禁止显示最后一行的计数反馈信息,默认值为"对6个或更多的记录,回送ON"
SQL> set timing on --默认为OFF,设置查询耗时,可用来估计SQL语句的执行时间,测试性能
SQL> set sqlprompt "SQL> " --设置默认提示符,默认值就是"SQL> "
SQL> set arraysize 1 --默认为15
SQL> set long 1000 --默认为80
vi. pl/sql语言;
Oracle自带的编程语言.
vii. SQL语句书写需要注意的地方;
SQL语句不区分大小写;
SQL语句可以换行写;
SQL语句用分号表示结束;
SQL语句中出现的字符串值是区分大小写的;
viii. 用户, schema, 数据库对象;
一个用户对应一个schema.
用户创建的表,也就是说这个表是属于当前用户的,还能说,这个表是属于当前用户的schema.
scott用户的emp表,它的全名就是scott.emp
ix. 查看当前用户有哪些数据库表;
select object_name , object_type from user_objects where object_type = 'TABLE'
i) select语句;
x. 语法;
格式:
select * from [ where group by having order by]
select子句: 用来选择需要展示的列;
from子句: 用来指定从哪些表中查询数据,表与表之间用逗号分隔;
where子句: 用来指定筛选条件,筛选行;
group by子句: 用来指定分组所依赖的列;
having子句: 用在分组过后,再做筛选的条件;
order by子句: 用来排序;
j) select子句;
select * from emp ; 从emp表中查出来所有行所有列.
select * 表示查出来所有行.
from emp 表示从emp表中查数据
没有where子句,表示返回emp表中多有的行.
select deptno,ename,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO from emp; 比上面的语句的效率要高.
xi. 去除重复的行;(distinct)
1、 select distinct deptno from emp where deptno !=20;
2、 select distinct deptno,job from emp;
xii. 全名;
17. 表全名;
格式:schema.表名
比如:select * from scott.emp;
18. 字段全名;
格式:schema.表名.字段名
比如:
1、 select scott.emp.ename from scott.emp
2、 select ename from emp;
xiii. 别名;
19. 字段别名;
语法:字段名 别名
Select ename 员工姓名,sal 工资 from emp;
20. 表别名;
语法:表名 别名
1、 Select empno ,ename,emp.deptno from emp,dept where emp.edptno=dept.deptno;
2、 Select empno,ename,e.deptno from,emp e,dept d where e.deptno=d.deptno;
k) from子句;
执行检索数据的来源,可以指定多张表,多表用逗号分开;
Select * from emp,dept;
如果这样执行的话,(emp:14 * dept:4)=56条记录,会产生笛卡尔积。
笛卡尔积:当进行多表查询时没有给定合适的条件的时候。
为了避免产生笛卡尔积,应该给定合适的条件。
l) where子句;
有如下几种条件:
xiv. 非; !
21. !=;
xv. 模糊查询; like
22. _; 匹配一个字符
23. %;匹配多个字符

xvi. 范围; in,is null,not
select * from emp where deptno in (10,20);
select * from emp where comm is null;
select * from emp where deptno in (10,20);
select * from emp where ename not like ‘SM%’;

xvii. 区间查询;
24. between..and..;
其实就等同于“>=….and <=…”
select * from emp where sal >=1000 and sal<=2000;
select * from emp where sal between 1000 and 2000;
25. not between..and..;
其实就等同于“<…or…>”
select * from emp where sal <1000 or sal>2000;
select * from emp where sal not between 1000 and 2000;
2、 Day02;
xviii. 连接查询;
26. 内连接;
select * from emp , dept where emp.deptno = dept.deptno;
select * from emp inner join dept on emp.deptno = dept.deptno;  --内连接
select * from emp join dept on emp.deptno = dept.deptno;  -- 默认是内连接
select empno,ename,emp.deptno,sal,grade from emp , dept , salgrade where  emp.deptno = dept.deptno and dept.deptno = 20 and sal between losal and hisal;
--多张表的连接,需要使用子查询.下面再讲
select t.*,grade from
(
select empno,ename,emp.deptno,sal from emp join dept on emp.deptno = dept.deptno and dept.deptno = 20
) t join salgrade on t.sal between losal and hisal;
交叉连接:刻意产生笛卡尔积
select * from emp , dept;
select * from emp cross join dept;
27. 自连接;
自己跟自己去查(数据来自同一张表),技巧是把表拿出来两边,然后给两个别名.
select e.ename,m.ename from emp e , emp m where e.mgr = m.empno;
select e.ename,m.ename from emp e join emp m on e.mgr = m.empno;
28. 自然连接;
select * from emp , dept where emp.deptno = dept.deptno; --等值匹配查询;
select * from emp natural join dept; --自然连接,
select * from emp join dept using(deptno);--自然连接,用在多个表中有多个同名的字段
29. 外连接;
左连接,把左边的表当成主要的表,右边的表作为附加表
select dept.deptno,dname,ename from dept,emp where emp.deptno = dept.deptno; --没有外连接效果.
select dept.deptno,dname,ename from dept,emp where dept.deptno = emp.deptno(+) ; --有+号的一方做为附加表
select dept.deptno,dname,ename from dept left outer join emp on emp.deptno = dept.deptno; --同上
select dept.deptno,dname,ename from dept left join emp on emp.deptno = dept.deptno; --同上
右连接,和左连接反过来.
select dept.deptno,dname,ename from dept,emp where emp.deptno(+) = dept.deptno; --有+号的一方做为附加表
select dept.deptno,dname,ename from emp right outer join dept on emp.deptno = dept.deptno; --同上
select dept.deptno,dname,ename from emp right join dept on emp.deptno = dept.deptno; --同上
30. 全连接;
select * from dept full join emp on emp.deptno = dept.deptno;
xix. 子查询(嵌套查询);
子查询(嵌套查询),把一个查询结果当成一张表(临时视图)来看. 表现是:一条SQL语句内部包含另外一条select语句.
用途: 在查询条件不明确的时候
例题:查询出和smith同部门的员工姓名.
select * from emp where deptno = (select deptno from emp where ename = 'SMITH');
31. 子查询的位置;
where子句里
from子句里
select子句里
32. 子查询的返回结果;
1.单行单列 --适用于where子句的等值查询 =

select * from emp where deptno = (select deptno from emp where ename = 'SMITH');

2.多行单列

select * from emp where deptno = (select distinct deptno from emp where deptno !=20); --单行子查询返回多个行,error
select * from emp where deptno in (select distinct deptno from emp where deptno !=20); --OK
3.多列子查询,适用于"子表查询"
SELECT * FROM EMP,(select deptno,job from emp where ename = 'ALLEN') T WHERE EMP.DEPTNO = T.DEPTNO AND EMP.JOB = T.JOB
SELECT * FROM EMP,(select deptno,job from emp where ename = 'ALLEN' or ename ='FORD') T WHERE EMP.DEPTNO = T.DEPTNO AND EMP.JOB = T.JOB;
xx. 分页查询;
select * from (select temp.*, rownum rn from (select * from emp order by sal desc) temp) where rn between 1 and 3;
between ... and ...
指明想展示的第几条到第几条数据.
具体思想见<mysql oracle 的分页查询.txt>
33. MySQL : limit;
select * from product limit 0,3; --前三条,从下标0开始,取3条记录
Oracle: 嵌套的2个子查询;
select * from (select temp.*,rownum rn from (select * from emp order by sal) temp) where rn between 1 and 3;
第一个子查询中可能需要排序,
第二子查询查询 出排序后的rownum.    ******
第三个查询,取出rownum范围的记录.
rownum是伪列,表中不存在这样的列,但是检索出来的视图中隐含了这个列,用来分页用
select emp.*, rownum rn from emp order by sal desc ;  ---rownum和排序在一个select语句中,会导致rownum紊乱.
因为是先获得了rownum,然后再排序.这样达不到我们想要的效果.
select temp.*,rownum rn from (select * from emp order by sal) temp;
先排序,后获得rownum,这样rownum的值才正确.
例题1:求出部门平均工资前两位的部门号
      select deptno from ( select deptno ,avg(sal) from emp group by deptno order by avg(sal) desc ) t where rownum<=2;
例题2:求出各个部门中员工工资大于该部门工资的姓名和工资
      select ename,sal from emp , (select deptno ,avg(sal) avgsal from emp group by deptno) deptno_avgsal
      where emp.deptno = deptno_avgsal.deptno and emp.sal > avgsal
xxi. select 的分组 group by;
把某个或者某些字段当成分组的依据.
当某个字段或者某些字段组合的值一样,即当成一组.
select * from emp;
select count(*) from emp; --统计emp表中的记录行数,没有group by,就把表中所有记录当成一个组.
select deptno,count(*) from emp group by deptno; --以部门分组,统计各个部门的记录行数.部门号相同,就是一个组.
select empcount from (select deptno,count(*) empcount from emp group by deptno) where deptno  =10; --
select deptno , job ,sum(sal),max(sal),min(sal),avg(sal)  from emp group by deptno , job; -- 以部门和岗位分组,查看工资总和,平均工资等等
34. group by 的使用限制;
出现在group by 子句中的字段,可以出现在select子句中,也可以不出现.
出现在select中的非分组函数的字段,必须出现在group by 子句中.
xxii. 分组函数,聚合函数,多行函数.  函数的概念:类似于高级语言中有返回值的方法;
35. count() , 统计行(非空)数;
select count(*) from emp;
select count(comm) from emp;
36. sum() , 求分组后的总和;
37. avg() , 求分组后的平均值;
38. max() , 求分组后的最大值;
39. min() , 求分组后的最小值;
xxiii. having子句,分组后再做筛选;
例题: 查询出部门平均工资在2000以上的部门信息

select deptno,avg(sal) from emp where avg(sal)>=2000 group by deptno;   -- error  分组函数不能出现在where子句里.
SELECT * FROM (select deptno,avg(sal) avgsal from emp group by deptno) T WHERE T.AVGSAL>=2000;
select deptno,avg(sal) from emp group by deptno HAVING AVG(SAL)>=2000; --ok
xxiv. ORDER BY 子句, 排序,可以排数字,也可以排字符串;
order by xxx ;      默认升序
select * from emp order by deptno;
order by xxx  asc;  升序
select * from emp order by deptno asc;
order by xxx desc;  降序
select * from emp order by deptno desc;
40. 可以对过个字段排序;
select * from emp order by deptno asc , sal desc;
select * from emp order by deptno asc ,sal desc ,hiredate desc;
41. 排字符,按照字母顺序升序或者降序排列;
select * from emp order by ename ;
select * from emp order by ename desc;
xxv. 例题;
例题1:求出部门平均工资前两位的部门号
      select deptno from ( select deptno ,avg(sal) from emp group by deptno order by avg(sal) desc ) t where rownum<=2;
例题2:求出各个部门中员工工资大于该部门工资的姓名和工资
      select ename,sal from emp , (select deptno ,avg(sal) avgsal from emp group by deptno) deptno_avgsal
      where emp.deptno = deptno_avgsal.deptno and emp.sal > avgsal
3、 Day03;
m) 函数;
xxvi. 语法;Function_name[arg1,([arg2,…argn])]
说明; 函数名后面的小括号是用来接受参数的
函数可以有参数也可以没有参数的,要是有多个参数要用逗号隔开
如果函数没有参数,可以不要括号的,比如:sysdate , user 等函数
函数调用完,则必须返回一个结果,否则会出错的
函数可以嵌套,先执行里层函数,在执行外行的函数、嵌套函数
xxvii. dual表;
当测试函数的时候就用dual表
dual--->盲表,这是一个虚拟表,里面没有任何数据的,只是用来补全from子句的
select 函数 from dual
show user ----> 这是一个sqlplus命令;
select user from dual; ----> 这是一个sql语句
n) 内置的单行函数;
xxviii. 字符函数;
42. upper(str) 将字符串转换成大写;
比如:select upper('Hello World') from dual;
返回结果为:HELLO WORLD
43. lower(str)  将字符串转换成小写;
比如:select lower('Hello World') from dual;
返回结果:hello world
44. initcap(str);  将各个单词的首字母换成大写
比如:select initcap('Hello world') from dual;
返回结果:Hello World
45. concat(str1,str2);  将字符串连接起来。等同于 ||
比如:select concat('Hello', 'world') from dual; == select 'Hello'|| 'world' from dual;
返回结果:Helloworld
46. length(str);  球字符串的长度
比如:select length('akjsdhkfahsdkjfh') from dual;
返回结果:
LENGTH('AKJSDHKFAHSDKJFH')
--------------------------
                16
47. substr(str,index[,length]);
截取字符串index从1开始. index的值可以给0或者是负整数
substr('abcdef',0) ==> 'abcdef' 从第0位开始截取到末尾
substr('abcdef',1) ==> 'abcdef' 效果同上,从第一位开始截取到末尾
substr('abcdef',3) ==> 'cdef' 从第三位开始截取到字符串末尾
substr('abcdef',7) ==> null index大于字符串长度,截取出来是null,
length(substr('abcdef',7)) null
substr('abcdef',3,2) ==> 'cd' 从第三位开始截取,截取两位长度
substr('abcdef',-3) ==> 'def' 从末尾往前数3位开始截取,截取到末尾
substr('abcdef',-3,2) ==> 'de' 从末尾往前数3位开始截取,截取2个长度
48. instr (str1,str2);  返回str2在str1中的位置
比如:select instr('1abc','ab') from dual;
返回结果:
INSTR('1ABC','AB')
------------------
           2
要是找不到就返回0
49. trim(str);  去除前后空格
比如:select trim('      1abc ab         ') from dual;
返回结果:
TRIM('1
-------
1abc ab
50. trim(char from str);  去除字符串最前或者最后的字符
比如:select trim('H' FROM 'HelloWorld H') from dual;
返回结果:
TRIM('H'FR
----------
elloWorld
51. replace(str,old_str,new_str);  替换字符串
比如:select replace ('abc','b','123') from dual;
返回结果:
REPLA
-----
a123c
52. lapd(str1,len,str2)\lapd(str1,len,str2);  左补全和右补全字符;
select lpad('abc',5,'*') from dual;==> '**abc'
select lpad('abcdef',5,'*') from dual;==> 'abcde'
select rpad('abc',5,'*') from dual;==> 'abc**'
select rpad('abcdef',5,'*') from dual;==> 'abcde'
注意:如果字符串长度超出给定长度,只显示给定长度个字符.
xxix. 数字函数;
53. round(num[,p]);  根据小数点精度(p)四舍五入数字
round(3.44, 1) ==> 3.4
round(3.45, 1) ==> 3.5
round(3.344, 2) ==> 3.34
round(3.345, 2) ==> 3.35
round(3.4, 0) ==> 3
round(3.4) ==> 3 同上.
round(3.5) ==> 4
round(34, -1) ==> 30
round(35, -1) ==> 40
round(35, -2) ==> 0
round(50,-2) ==> 100
54. trunc(num[,p]); 
根据小数点精度截取数字
trunc(3.45, 1)  ==> 3.4
trunc(3.345, 2) ==> 3.34
trunc(3.5,0) ==> 3
trunc(35, -1) ==> 30
55. mod(num1,num2); 
求num1除以num2之后的余数
mod(10,3) ==> 1
mod(3,0) ==> 3  除数为0 这里不会报错.
select 10/0 from dual 这里会报错.
mod(11,20) ==> 11  除数比被除数大 直接返回被除数
xxx. 日期函数;
56. MONTHS_BETWEEN(date1,date2);
Date1-date2的差是表示相差几个月,该函数运算完之后的结果会有余数,小数点表示过了多少分之一个月
eg1:select months_between('09-4月-11','09-4月-10') from dual;
结果:
MONTHS_BETWEEN('09-4月-11','09-4月-10')
---------------------------------------
                         12
此表示为2011-4-9到2010-4-9相差12个月。
eg2:select months_between(sysdate,'09-4月-10') from dual;
此表示为当前日期到2010-4-9相差几个月。
57. ADD_MOUNTHS(date,num);
表示指定日期的月份增加或者减去若干月后的日期
eg1:select add_months(sysdate,1) from dual;
结果:表示下个月的今天
ADD_MONTHS(SYS
--------------
15-9月 -12
eg2:select add_months(sysdate,-1) from dual;
结果:表示上个月的今天
ADD_MONTHS(SYS
--------------
15-7月 -12
58. NEXT_DAY(date,day);
指定日期的下一个星期几的日期
eg1:select next_day(sysdate,'星期五') from dual;
结果:表示下个星期五是哪天
NEXT_DAY(SYSDA
--------------
17-8月 -12
eg1:select next_day(sysdate,'friday') from dual;
结果:也表示下个星期五是哪天,英文环境的写法。
英文环境的配置(后面有清楚介绍);
59. LAST_DAY(date);
指定日期的当月的最后一天
eg1:select last_day(sysdate) from dual;
结果:
LAST_DAY(SYSDA
--------------
31-8月 -12
eg1:select last_day('02-2月-2012') from dual;
结果:
LAST_DAY('02-2
--------------
29-2月 -12
eg1:select last_day('02-2月-2011') from dual;
结果:
LAST_DAY('02-2
--------------
28-2月 -11
注意:有last_date函数,但是没有first_date函数。如果要求本月的第一天,则应先求出上月的最后一天在加上一天就是本月的第一天
eg1:求出本月的第一天:select last_day(add_months(sysdate,-1))+1 from dual;
结果:
LAST_DAY(ADD_M
--------------
01-8月 -12
60. ROUND(date,参数)
指定日期本周日或者下周日,本月初或者下月初,本年初或者下年初,其参数可以表示为:day、months、year
a、 求周日,以周三和周四作为分界线,周三以前为本周日,周四之后 为下周日。
b、 求月本月初和下月初,以15号和16好作为分界:15好以前的为本月初,16号以后的下月初
c、 求本年初和下年初,是以6月和7月作为分界,6月前为本年初,7月后为下年初
eg1:select round(to_date('06-2月-2012'),'day') from dual;
结果:星期一则为本周日
ROUND(TO_DATE(
--------------
05-2月 -12
eg2:select round(to_date('09-2月-2012'),'day') from dual;
结果:为星期六则为下周日
ROUND(TO_DATE(
--------------
12-2月 -12
eg3:select round(to_date('15-4月-11'),'MONTH') from dual;
结果:为本月初
ROUND(TO_DATE(
--------------
01-4月 -11
eg4:slect round(to_date('16-4月-11'),'MONTH') from dual;
结果:为下月初
ROUND(TO_DATE(
--------------
01-5 -11

eg5:select round(to_date('30-6月-11'),'YEAR') from dual;
结果:为本年初
ROUND(TO_DATE(
--------------
01-1月 -11
eg6:select round(to_date('1-7月-11'),'YEAR') from dual;
结果:为下年初
ROUND(TO_DATE(
--------------
01-1月 -12
61. TRUNC(date,参数);
此函数跟ROUND函数的用法一样,指定日期的本周日、本月初、本年初,没有分界的情况。
eg1:elect trunc(to_date('09-4月-11'),'DAY') from dual;
结果:
TRUNC(TO_DATE(
--------------
03-4月 -11
eg2:select trunc(to_date('09-4月-11'),'MONTH') from dual;
结果:
TRUNC(TO_DATE(
--------------
01-4月 -11
求81年下半年入职的员工信息
eg1:elect * from emp where hiredate between '01-6月-81' and '30-12月-81'
结果:
   EMPNO ENAME   JOB              MGR  IREDATE              SAL       COMM     DEPTNO
---------- ---------- --------- ---------- -------------- ---------- ---------- ----------------------------------------------------------------------
      7654 MARTIN     SALESMAN        7698 28-9月 -81           1250       1400         30
      7782 CLARK      MANAGER         7839 09-6月 -81           2450                    10
      7839 KING       PRESIDENT            17-11月-81           5000                    10
      7844 TURNER     SALESMAN        7698 08-9月 -81           1500          0         30
      7900 JAMES      CLERK           7698 03-12月-81            950                    30
      7902 FORD       ANALYST         7566 03-12月-81           3000                    20
已选择6行。
eg2:elect * from emp where hiredate between add_months(last_day('23-6月-81'),-1)+1 and last_day('2
1-12月-81') order by hiredate;
结果:
     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO
---------- ---------- --------- ---------- -------------- ---------- ---------- --------------------------------------------------------------------------------
      7782 CLARK      MANAGER         7839 09-6月 -81           2450                    10
      7844 TURNER     SALESMAN        7698 08-9月 -81           1500          0         30
      7654 MARTIN     SALESMAN        7698 28-9月 -81           1250       1400         30
      7839 KING       PRESIDENT            17-11月-81           5000                    10
      7900 JAMES      CLERK           7698 03-12月-81            950                    30
      7902 FORD       ANALYST         7566 03-12月-81           3000                    20
已选择6行。
跟eg1结果显示的一样
62. 对日期的补充;
在Oracle中的日期包括的内容:
年、月、日、时、分、秒、上下午、星期等
a、默认的日期格式:DD-MON-YY
b、中文环境下的格式:01-7月-12
c、英文环境下的格式:01-jul-12
所以要把Oracle中的语言环境换成英文环境,需要在环境变量中新增一个环境变量
名为:NLS_LANG  
 值为:AMERICAN_AMERICA.US7ASCII
select sysdate from dual;
sysdate 获得当前系统日期与时间的函数,Sysdate函数返回除了日期还有时间.Sysdate函数没有参数,所以不要小括号.
 08-4月 -11 sqlplus和命令行中的显示
 2011-4-8 13:55:32 PLSQL Developer中显示
63. 日期的算术运算;
a、日期可以计算,日期计算后结果仍为日期。
b、两个日期相减后,返回相差的天数
c、可以用数字除24来向日期中加上或减去小时
select ename,(sysdate-hiredate) 入职天数 from emp;
select ename,(sysdate-hiredate)/7 入职周数 from emp;
select ename, hiredate, hiredate+3 from emp;
xxxi. 转换函数;
如下图:

64. to_char(value,fmt);
参数有两个,第一个是要转换的值,第二个是格式.可以把日期转换成指定格式的字符串,也可以把数字按照指定的格式转成字符换
To_char(date , date_fmt) 日期到字符的转换
格式参数要求:
格式必须包含在单引号中,部分格式元素大小写敏感
可以包含任意的有效的日期格式
格式中包含其他字符串是需要用双引号括起来
可以使用 fm 去掉返回的日期字符串值中的空格或前置的零
Oracle日期格式中元素:

to_char(sysdate,'YYYY') ;
数字表示的当前年份
to_char(sysdate,'MM') 数字表示的当前月份
to_char(sysdate,'DD') 数字表示的当前号数
to_char(sysdate,'YYYY-MM-DD HH24:MI:SS AM')把日期类型按照指定格式转变为字符串 结果类似:2011-04-08 00:22:17 上午
to_char(sysdate,'YY-MM-DD HH:MI:SS AM') YY表示年份的后两位转为字符串,HH是12时制的小时.结果类似:11-04-08 12:22:17 上午
YYYY,MM,DD,HH,MI,SS这几个格式大小写没有严格要求,他们返回的是数字,没有大小写之分。
AM在英文环境下区分大小写,大写返回的是AM|PM,小写则返回am|pm
to_char(sysdate,'year"|"Year"|"YEAR') year,Year,YEAR 返回年份的英文格式:twenty eleven|Twenty Eleven|TWENTY ELEVEN
在格式中如果出现了其他的字符串需要用双引号括住
to_char(sysdate,'HH24"时"MI"分"SS"秒" ') 例如:00时41分14秒
to_char(sysdate,'month"|"Month"|"MONTH')
month , Month , MONTH返回字符串格式的月份
中文: 4月 |4月 |4月
英文:april    |April    |APRIL
to_char(sysdate,'ddspth"|"Ddspth"|"DDspth')
ddspth ,Ddspth, DDspth返回字英文格式的号数  例如:eighth|Eighth|EIGHTH
fm的用法;
to_char(sysdate,'month"|"Month"|"MONTH') 
返回月份的字符串表示:
中文: 4月 |4月 |4月
英文:april    |April    |APRIL
to_char(sysdate,'FMmonth"|"Month"|"MONTH')
中文: 4月|4月|4月
英文:april|April|APRIL   ---空格被去掉
to_char(sysdate,'yyyy-mm-dd')
如果当前日期是2011年4月8日则返回:2011-04-08 月份和号数只有一位的话会补0
to_char(sysdate,'FMyyyy-mm-dd')
2011-4-8 0被去掉
65. to_char(num,fmr) 数字到字符串的转换;
该函数常用格式;
9:数字
0:补零
$:美元符号
L:本地货币符号
.:小数点
,:千位符
0和9的区别;
0: 高位不足,则补零,显示所有位数
9: 高位不足,则不补,只显示实际位数
to_char(9821,'L999,999,999,999.99') ¥9,821.00    
to_char(9821,'L000,000.00') ¥009,821.00
to_char(9821.50,'$999,999.99') $9,821.50
to_char(9821,'$000,000.00') $009,821.00
select sal , to_char(sal,'$999,999,999.99') from emp where ename='KING';5000.00 $5,000.00
to_date(str,date_fmt) 把字符串转成日期;
to_date('2010-12-11' ,'YYYY-MM-DD')
把‘2010-12-11’字符串转换成指定的日期类型
用YYYY对应2010来获取年份 , 用MM对应12来获取月份 , 用DD对应11来获取号数
66. to_number();
这里不做多讲,自己去折磨啦!
xxxii. 通用函数;
67. nvl 空值转换函数;
NVL(表达式1,表达式2);
如果表达式1为空值,NVL返回值为表达式2的值,否则返回表达式1的值。
该函数的目的是把一个空值(null)转换成一个实际的值。
其表达式的值可以是数字型、字符型和日期型。但是表达式1和表达式2的数据类型必须为同一个类型。
对数字型: NVL(comm,0);
对字符型 NVL( TO_CHAR(comm), 'No Commission');
对日期型 NVL(hiredate,'31-DEC-99')
NVL2(表达式1,表达式2,表达式3)
如果表达式1为空,返回值为表达式3的值。如果表达式1不为空,返回值为表达式2的值。
例如 NVL2(comm,'sal+comm',sal)
如果comm为空,就返回sal 的值。如果 comm 不为空(null),就返回表达式 sal+comm的值。
其他,参见<oracle函数大全.doc>
xxxiii. DML : 数据操作语言;
68. insert: 插入数据;
语法1:
insert into tableName [(colnums list)] vlaues(value1,value2...valuen)
语法2:
insert into tableName [(colnums list)] subQuery
insert into emp (empno,ename,job,mgr,hiredate,sal,comm,deptno) values(null,'abc',null,'1234','15-8月-12',null,null,null);
指明给表中那些字段插值
insert into emp values(8994,'abc',null,'1234','15-8月-12',null,null,null);
给表中所有字段插入值,字段的顺序采用表中字段的顺序
null值的插入;
显式插入空值
insert into emp (empno,ename,job,mgr,hiredate,sal,comm,deptno) values(8995,'abc',null,'1234','15-8月-12',null,null,null);
显示的为job,sal,comm,deptno显示的插入了null.
隐式插入空值
insert into emp (empno) values(9000);
emp表中除了empno之外的其他字段都是隐式的插入了空值.
insert into emp_copy (select * from emp where deptno = 10)
insert into emp_copy (empno,ename) (select empno,ename from emp where deptno = 10)
69. delete:删除数据,0~多条数据;
语法:
delete from tableName [where .....]
delete from emp; --删除表中所有行.
delete from emp where 1=2; --没有行被删除
delete from emp where 1=1; --删除表中所有记录行.
70. update:更新数据,0~多条数据;
语法:
update tableName set colnumName1 = value[, colnumName2 value ] [where ....]
update emp set sal = 2000; -- 更新所有记录行的sal字段的值.
update emp set sal = sal+1000 where deptno = 30;
update emp set sal = sal+1000,comm=nvl(comm,0)+200 where deptno = 30
xxxiv. 事务: 由一些列DML语言组成;
在当前事务中操作的数据,可以预览. 事务看不到其他事务未提交的数据.有如下几个关键字commit、rollback、savepoint
71. commit;
72. rollback;
73. savepoint;
74. 事务自动提交;
事务期间,执行了DDL或者DCL;
终端正常退出;
75. 事务自动回滚;
终端异常退出;
系统异常终端;
76. 事务隔离带来的现象;
脏读: 事务2读到了事务1没有提交的数据. 不应该出现的
重复读:不应该出现的.
不可重复读:事务1第一次读取数据内容和第二次读取的数据内容不一样由于在两次读取期间被事务2更新了数据然后提交. 正常的.
幻读:事务1第一次读取数据行数和第二次读取的数据行数不一样,由于在两次读取期间被事务2插入或者删除了数据然后提交. 正常的.
大部分数据库默认杜绝了脏读(未提交读).
77. 事务隔离级别;
未提交读 uncommitted read.
提交读  committed read.
不可重复读
序列度
4、 Day04;
今天主要讲了SQL语句中的DDL语句
了解数据库对象包括哪些:
table、view、sequence、index…………..
模式Schema:
在数据库系统中美创建一个用户就有 与用户相对应的Schema。Schema负责管理当前的数据库对象。
也就是说一个Schema管理这与之相对应的用户的数据库对象。
scott.emp/dept/salgrade/
select * from emp;表示查询当前用户的表。
select * from scott.emp;表示查询scott用的emp表
o) DDL;
create/drop/alter/truncate;数据库对象的创建与删除
xxxv. 数据库对象的命名规则;
a、 必须以字母开头
b、 长度有限制,1-----30个字符之间
c、 出开头字母外字符可以是:A---Z、a---z、0---9、$、_.......
d、 不能重名的
xxxvi. 常用的数据类型;
i、 int  整数类型  38位  等价于NUMBER(38)
ii、 char  固定长度字符类型  长度为1
iii、 char(n) 可变长度的字符类型  最小为1,最大值长度为2000
iv、 varchar(n)  可变长度的字符类型  最小为1,最大为4000
v、 varchar2(n)  Oracle自身的可变长度的字符类型,最小为1,最大为4000
vi、 number  数字类型,包括小数、整数
vii、 number(n)  数字类型,n表示整数部分的长度
viii、 number(p,s)  数字类型,p表示该数字(含小数位)的总长度,s表示入库后小数部分保留的长度,整数部分的长度=p-s
ix、 date  日期类型
xxxvii. 创建表;
语法:
create table [schema.]table_name(col_name datetype[defual defual_value][约束定义][……])
eg1:create table student(id int,name char(20),sex char(5),age int,adds char(40),pone char(11),zip number(6),mail char(30),regdate date);
eg2: create table student ( id int ,name char(20), sex char(5) default '男', age int, adds char(40), phone char(11), zip number(6), mail char(30), regdate date default sysdate ); ---默认值的定义
78. 如何使用默认值;
如果有指定值则采用指定的值;如果隐式插入null,则采用默值
79. 约束Constructs;
约束包括:
非空
not null
唯一
unique
主键
priamry key
外键
foreign key
检查
check
80. 约束的定义;
约束定义时机:
a、 在创建表的时候就定义约束。
b、 对已经存在的表添加约束
约束定义位置:
a、 列级别
b、 表级别
约束定义语法:
列级别:列的定义 【constraint 约束名字】 约束类型
表级别:列的定义 【constraint 约束名字】 约束类型(列名称)
**********注意:not null 不能定义在表级别上的哦**********
eg1:create table student (id int unique,name char(20))  此种方法就是定义列级别的约束
eg2:create table student (id int,name char(20),unique(id));  此种方法就是定义表级别的约束
通过以上两种方式创建的约束,要是没有给约束给名字的话,那么系统将会默认给一个约束名字:sys_XXXXXXX
create table student(id int constraint student_id_unk unique ,name char(20)); 
create table student(id int ,name char(20), constraint student_id_unk unique(id));
---------上面两种约束的定义,给约束定义了名称.
对某个字段定义多个约束:
create table student(id int unique not null ,name char(20));
create table student(id int constraint stu_id_unik unique constraint stu_id_nnk not null ,name char(20));
如果定义多个表级别约束,约束间要用逗号分隔
81. 查看指定表的约束信息;
select OWNER,CONSTRAINT_NAME,CONSTRAINT_TYPE,TABLE_NAME,STATUS,INDEX_NAME from user_constraints where table_name='STUDENT';
82. 查看指定列的约束信息;
select * from user_cons_columns where table_name='STUDENT';
select tt.constraint_name,tt.constraint_type,tt.table_name,ct.column_name from user_constraints tt,user_cons_columns ct where  tt.constraint_name = ct.constraint_name;
83. 约束详解;
a、 not null:非空的约束

i、表示某列的值不能为null
ii、在一张表中,可以定义一个或多个列为not null,也就是说可以定义多个not null约束,且只能定义在列级别上
created table test1(attr1 int not null, attr2 int,attr3 int);  
--定义attr1列为not null
created table test1(attr1 int, attr2 int,attr3 int, not null(attr1)); 
---不合法.....
created table test1(attr1 int not null, attr2 int not null,attr3 int not null); 
--定义了多列为not null
b、 unique:唯一约束
i、表示某列的值唯一,不能重复。
ii、允许插入null。
iii、可以定义一个或多个列为unique,也就是说一张表可以定义多个unique约束
vi、可以把多个类组合起来成为一个复合惟一键,这种方式值只能在表级别上定义的
v、可以再列级别和表级别上定义
create table student(id int unique,name char(20) unique,sex char(20),begiandate date);
create table test1(VAR1 INT UNIQUE,VAR2 INT UNIQUE);
create table test1(VAR1 INT ,VAR2 INT ,unique(var1),unique(var2));
create table student(id int,name char(20),sex char(20),begiandate date ,  unique(id,name));  --把多个列复合成为一个唯一键
c、 primary key:主键约束
已经包括了not null 、 unique约束了
主键:唯一标识一条记录的列
一张表只能有一个主键
在一张表中,可以定义一个列作为主键,或者把多个列组合起来定义为组件(复合组件),后者只能在表级别上定义的
其实复合主键就是吧把组合起来的列当成一个列来看待啦
create table student(id int constraint stu_id_pk primary key,name char(20),sex char(20),begiandate date );
create table student(id int,name char(20),sex char(20),begiandate date,primary key(id) );
create table student(id int,name char(20),sex char(20),begiandate date,primary key(id,name) );  --复合主键
d、 foreign key:外键约束
外键:医用其他的主键或惟一键,达到表关联的目的
一张表可以定义多个外键
可以设置一个列为外键或组合的多列为外键(复合外键)
可以再列级别定义,也可以在表级别中定义
外键中的值必须是其引用的主键或者唯一键中出现过的值,可以重复,是否为null?是的!!
create table province(id int primary key , pname char(20));
create table class (id int primary key, cname char(20));
create table student(id int primary key,name char(20),classid int references class(id)); --列级别
create table student(id int primary key,name char(20),classid int, foreign key(classid) references class(id)); --表级别
create table student(id int primary key,name char(20),classid int references class(id),pid int references province(id)); --一张表可以有多个外键
84. 表关系;
1:m
create table class(id int , cname char(20), primary key (id ,cname)); --表级别复合主键
create table student (id int primary key,name char(20), cid int ,cname char(20), foreign key(cid,cname) references class(id,cname)); --表级别复合外键
1:1
create table student(id int primary key , name .....);
create table stu_info (id int primary key , sex...,age...,adds..., sid int references student(id)); --1:m
create table stu_info (id int primary key , sex...,age...,adds..., sid int unique references student(id)); --1:1  sid有唯一约束的外键
create table stu_info (id int primary key references student(id) , sex...,age...,adds...);-- 1:1 ID既是主键也是外键
m:n
create table student(id int primary key ,name varchar(20)); --学生表
create table course(id int primary key ,name varchar(20)); --课表
create table stu_cour(sid int references student(id),cid int references course(id), primary key(sid,cid)); --选课表,中间表
m:n sid引用student.id,cid引用course.id;
create table exam_result(sid int,cid int,point int,foreign key(sid,cid) references stu_cour(sid,cid), unique(sid,cid)); --考试成绩表
exam_result的复合外键fk(sid,cid)引用 stu_cour表的复合主键pk(sid,cid);
exam_result还有复合唯一键(sid,cid). 这样保证了 exam_result 与 stu_cour 1:1.
85. 主键外键关联中的表现;
如果一张表的主键(父表)或唯一键被其他表(子表)的外键引用的时候.如果要删除父表的时候是不能删除的.要先删除子表才能删除父表.
删除父表的中的数据的时候,如果子表中有引用该条数据,则删除不掉. 应该先删除子表中的数据.
如果想在删除父表中的数据时把子表中相关的数据一并删除的时候,在创建外键约束的时候需要加上外键级联删除设置.
外键级联: 在子表中设置.在设置外键约束的时候设置级联
级联删除:删除父表中的数据时,子表中有引用主表中的数据时,一并把子表中的数据删除.
on delete cascade
create table class (id int primary key,name char(20));
create table student (id int primary key,name char(20),cid int references class(id) on delete cascade );
级联置空:删除父表中的数据时,子表中有引用主表中的数据时,把子表中的的外键设置为null.
on delete set null
create table class (id int primary key,name char(20));
create table student (id int primary key,name char(20),cid int references class(id) on delete set null );
级联更新: 没有级联更新. 如果要做级联更新的话,Pl/SQL 中的触发器次才可以实现
trigger
e、 check:检查约束
检查:表示在向某个字段插入值的时候要做检查,用来约束某个列的值必须符合给定的条件.
一个表中可以设置多个check约束.
可以设置在列级别,也可以设置在表级别.
可以插入null.
create table student (id int primary key ,name char(20), age int check(age >=0 and age<=100));
create table student (id int primary key ,name char(20), age int, check(age >=0 and age<=100));
对表添加约束;
alter table 表名 add [constraint 约束名] 约束类型 -- 相当于在表级别上定义约束
ALTER TABLE student5 add unique(name);
ALTER TABLE student5 add constraint stu5_name_uni unique(name);
ALTER TABLE student5 add constraint stu5_name_che check(length(name)>3);
xxxviii. 从表中删除约束;
alter table 表名 drop constraint 约束名字
ALTER TABLE student5 drop constraint stu5_name_che;
xxxix. 启用表中的约束;
alter table 表名 enable constraint 约束名字
xl. 禁用表中的约束;
alter table 表名 disable constraint 约束名字
用子查询创建表 。原来的表中的约束会丢失,除了not null
语法:
create table 表名 [(col1,col2....coln)] as subQuery ;
例子:
create table emp_bak as (select * from emp); --复制一张表的结构和数据,约束丢弃.
create table emp_bak2 as (select * from emp where 1=2); --只拿表结构(表的字段名,字段类型)
create table emp_bak3 as (select empno,ename from emp ); --只有子查询中指定的字段会放到新创建的表中
create table emp_bak4(id,name) as (select empno,ename from emp ); --给新创建的表设计字段名字
xli. 用子查询向表中插入数据;
语法:
insert into 表名 subQuery;
例子:
insert into emp_bak2 (select * from emp);
注意:
子查询返回的数据集的字段数量,字段类型要和被插入数据的表中的字段数量和字段类型要相符.
p) 修改表;
alter table test add constraint test_id_pk primary key (id) ; --增加约束
alter table test enable constraint test_id_pk; --启用约束
alter table test disable constraint test_id_pk; --禁用约束
alter table test drop constraint test_id_pk; --删除约束
alter table test add name char(20);  --增加name字段 , 增加的字段放在最后。
alter table test drop column name; --删除name字段
alter table test modify name char(30); --修改name字段类型
alter table test modify name char(30) not null; --修改name字段类型,和添加not null 约束
alter table table_name modify col_name new_dateType [default default_value] --修改数据类型或默认值
alter table table_name modify col_name default default_value --修改默认值
alter table test rename column name1 to name2; --改字段名字
xlii. 改表名;
rename ... to ...;
rename table_name1 to table_name2;
q) drop表;
语法: drop table table_name
r) 截断;
truncate table table_name;
truncate 与 delete 的异同:
xliii. 同:两者都是把表中的数据删除;
xliv. 异:前者,删除当前表中数据所占用数据文件的空间.后这只删数据,空间不删;
delete是dml语言,可以rollback
truncate是DDL语言,不可rollback
bin$xxxx
s) 垃圾表的处理;
86. 查看回收站;
SELECT * FROM USER_RECYCLEBIN;
87. 清空回收站;
purge recyclebin;
88. 清除回收站中指定的表;
purge table table_name;
89. 恢复指定的表(闪回);
flashback table table_name to before drop;
90. 闪回和真删除表时的表现;
前提:如果回收站中的多个表(删除前名称一样).
闪回表名. 闪回最近的还是最早的?  闪回最近的..
删除表名. 删除最近的还是最早的? 删除最早的..
t) 视图;
xlv. 视图的作用;
a、 简化查询
select * from emp;
select deptno,avg(sal) from emp group by deptno
b、 可以控制数据的访问
select empno,name from emp where deptno = 10
xlvi. 视图的分类;
91. 简单视图;
视图中涉及到的表只有一个,没有函数,没有分组,没有distinct
92. 复杂视图;
视图中涉及到有多个表,有函数,有分组,有distinct,有表达式
xlvii. 视图创建的方法;
语法;
create【or replace】【force|noforce】view 试图名字【字段名【,字段名……】】as subquery 【with check option【constraint 约束名】】【with read only【constraint 约束名】】
subquery(通过子查询来创建视图,子查询可以是很复杂的select语句)
93. 说明一下:在视图中必须的部分
必须部分:create view 试图名字 as subquery;
or replace:如果当前要创建的视图名子已经存在,怎替换掉。
force:当前视图涉及到的表不存在时,视图是不能创建的,如果要创建成功的话,则使用force先把视图创检查出来。noforce是默认的。
select tt.constraint_name,tt.constraint_type,tt.table_name,ct.column_name from user_constraints tt,user_cons_columns ct where tt.constraint_name = ct.constraint_name;
CREATE or replace VIEW USER_CONSTRAINTS_INFO_VIEW AS select tt.constraint_name 约束名,tt.constraint_type 约束类型,tt.table_name 表名,ct.column_name 列名 from user_constraints tt,user_cons_columns ct where tt.constraint_name = ct.constraint_name;
CREATE or replace VIEW USER_CONSTRAINTS_INFO_VIEW (约束名,约束类型,表名,列名) AS select tt.constraint_name ,tt.constraint_type ,tt.table_name ,ct.column_name
from user_constraints tt,user_cons_columns ct where tt.constraint_name = ct.constraint_name;
注意:
a、视图没有自己的数据.他的数据来源于视图中子查询返回的数据.
b、有时候可以通过视图去修改数据.修改过的数据如果不满足视图子查询的条件,则不会再视图中展示了.
eg:创建员工和工资视图(工号,姓名,工资)
create or replace view emp_sal_view as select empno,ename,sal from emp ;    
select * from emp_sal_view order by sal desc; 
select sum(sal) from emp_sal_view ;
创建员工和部门视图(工号,姓名,部门号,部门名)
create or replace view emp_dept_view as select empno,ename,emp.deptno ,dname from emp ,dept where dept.deptno = emp.deptno;  
select * from emp_dept_view;
注意:
a、视图没有自己的数据.他的数据来源于视图中子查询返回的数据.
b、有时候可以通过视图去修改数据.修改过的数据如果不满足视图子查询的条件,则不会再视图中展示了.
创建员工和工资视图(工号,姓名,工资)
create or replace view emp_sal_view as select empno,ename,sal from emp ;    
select * from emp_sal_view order by sal desc; 
select sum(sal) from emp_sal_view ;
创建员工和部门视图(工号,姓名,部门号,部门名)
create or replace view emp_dept_view as delect empno,ename,emp.deptno ,dname from emp ,dept where dept.deptno = emp.deptno;  
select * from emp_dept_view;
xlviii. 对视图的DML操作 insert update  delete;
94.  emp_sal_view -- 是简单视图;
select * from emp_sal_view;
insert into emp_sal_view values (8888,'fang',8000);
update emp_sal_view set sal = 5000 where empno=8888;
delete from emp_sal_view where empno = 8888;
95.  emp_dept_view -- 复杂视图;
select * from emp_dept_view;
select * from emp;
insert into emp_dept_view values (8889,'fang',50,null); --执行失败
insert into emp_dept_view(empno) values (8889); --执行成功               
update emp_dept_view set ename = 'smith',dname='RESEARCH1' where empno = 7369;  --执行失败
update emp_dept_view set ename = 'smith' where empno = 7369;  --执行成功
delete from emp_dept_view where empno =8889;  -- 执行成功,但是删除不掉数据,因为该部分数据不在视图显示的范围内
xlix. 只读视图;
create or replace view emp_sal_view as select empno,ename,sal from emp with read only;  -- 只读
select * from emp_sal_view;
insert into emp_sal_view values (8888,'fang',8000);
update emp_sal_view set sal = 5000 where empno=8888;
delete from emp_sal_view where empno = 8888;
l. 检查视图;  
create or replace view emp_dept_10_view as select empno 工号,ename 姓名,deptno  from emp where deptno=10  with check option; 
select * from emp_dept_10_view; 
insert into emp_dept_10_view values (8892,'abc',20) ;
select * from emp;
li. 临时视图;
select * from ( select * from emp where deptno = 10 );
                     
lii. 用管理者赋予创建视图的权限给scott
  conn / as sysdba;
  grant create view to scott;
u) 序列;
liii. 序列的概念;
序列是可以产生有规则的数字的一个数据对象。常用来为主键生成主键值的。
先看一下语句;
create table student (sid number(5) primary key,name varchar2(10) );
select * from student;--查看约束信息
select T1.TABLE_NAME,T2.COLUMN_NAME,T1.CONSTRAINT_NAME,   t1.constraint_type
from user_constraints T1,USER_CONS_COLUMNS T2
where
T1.CONSTRAINT_NAME = T2.CONSTRAINT_NAME And
t1.table_name = 'STUDENT' ;
delete from student; 
commit; --提交数据
insert into student values (1,'a');
insert into student values (2,'a');
insert into student values (3,'c');--每次都要指定主键的值.
--如果想像mysql一样对主键进行自增长必须使用序列发生器来产生一个数值.
如此可见,每次都要为主键赋值,这样很麻烦。由于Oracle中不像mysql可以自增长,所以就要引入序列。
liv. 如何创建序列;
96. 语法;
create sequence 序列名字
    [start with num]  --起始值
    [increment by num] --增量
    [maxvalue num]     --最大值
    [minvalue num]     --最小值
    [cycle | nocycle]  --是否循环
    [cache number | nocache] --是否有缓存
97. 创建序列发生器的以下几种方法;
create sequence student_sid_sequence ;
--创建一个默认的序列生成器
--最小值1
--最大值1E27
--增量 1
--不循环
--缓存20
--last_number 1
                  
1、create sequence student_sid_sequence start with 5;
2、create sequence student_sid_sequence start with 5 nocache; -- 不要缓存
3、create sequence student_sid_sequence start with 1
increment by 2 -- 增量2
nocache; -- 不要缓存
4、create sequence student_sid_sequence start with 10                 
                                  increment by -2  --负增长
                                  maxvalue 100
                                  nocache; -- 不要缓存
5、create sequence student_sid_sequence start with 1                 
                                  increment by 1
                                  maxvalue 10
                                  cycle  --如果有循环,必须指定最大值
                                  nocache; -- 不要缓存
6、create sequence student_sid_sequence start with 1                 
                                  increment by 1
                                  maxvalue 10
                                  minvalue -10  -- 小于等于start with
                                  cycle  --如果有循环,必须指定最大值
                                  nocache; -- 不要缓存
98. 在student表中使用序列发生器;
create sequence student_sid_sequence start with 1 increment by 1 maxvalue 999999 nocache;
lv. 使用序列;
nextval:下次可用的数字;
currval:表示上一次用过的数字,必须使用过nextval之后才可以调用currval
select student_sid_sequence.currval from dual;
--没有使用过nextval之前调用currval是无效的.
select student_sid_sequence.nextval from dual; --1
select student_sid_sequence.currval from dual;
99. 使用序列给表中的主键插入;
--原来需要指定主键的值intert into student values(1,‘a’);
--使用序列来产生主键值:
insert into student values (5,'e')
insert into student values (student_sid_sequence.nextval,'&name');
select * from student;
lvi. 查看序列的属性;
select * from user_sequences;  
       --sequence_name 序列名字
       --min_value 最小值
       --max_value 最大值
       --increment_by 增量
       --cycle_flag   是否循环
       --cache_size   缓存大小
       --last_number  下一次从硬盘调用序列生成器时可用的数字
lvii. 删除序列;
drop sequence student_sid_sequence;
lviii. 修改序列;
alter sequence 序列名字              
    [increment by num] --增量
    [maxvalue num]     --最大值
    [minvalue num]     --最小值
    [cycle | nocycle]  --是否循环
    [cache number | nocache] --是否有缓存
select * from user_sequences;  

alter sequence student_sid_sequence
    increment by 2
    maxvalue 20
    minvalue 5
    cycle
    cache 5;
lix. 序列使用上的注意事项;
1.序列生成器是一个共享的对象
任何表的主键可以使用同一个序列生成器来生成主键.
2.利用序列生成器生出的主键值可能会有跳跃的情况.
原因之一,序列生成是共享的.
原因之二,序列生成器只是产生数字,insert语句把值插入到表中.既然做DML操作,就是可以rollback,序列生成器不会倒转.
原因之三,序列生成器可能会设置缓存.
v) 索引(index);
lx. 概念;
索引:类似于书本中的目录,可以帮助DBMS快速的定位数据,方便查找、提高效率。
注意:不是索引越多,效率就越高。
lxi. 索引的创建方式;
a、 自动创建的索引:DBMS会为表中的主键和惟一键自动创建索引(唯一索引)
b、 手工创建索引:人工指定哪个字段创建索引(非惟一键)
lxii. 查看索引的字典;
select * from user_indexes;

select * from user_ind_columns;

select t1.index_name,t1.table_name ,t2.column_name from user_indexes t1 ,user_ind_columns t2 where t1.index_name = t2.index_name;

select t1.index_name,t1.table_name ,t2.column_name ,T1.UNIQUENESS                 from user_indexes t1 ,user_ind_columns t2 where t1.index_name = t2.index_name and t1.table_name='STUDENT';
lxiii. 给表中某个字段添加索引;
create index 索引名字
      on 表名(列名[,列名]);
                
select * from student;

alter table student add (pid char(18) unique,mail varchar(20),age number(3));

alter table student add (sex varchar(5));
                
create index student_mail_index on student(mail);
                
create index student_age_sex_index1 on student(age,sex);  --给多列创建一个索引
lxiv. 使用索引;
索引不是人工使用的,而是系统在查询数据的时候会拿来参考,并用之提率的
lxv. 何时是否要创建索引;
100. 创建;
1.表很大,数据量大的时候
2.字段经常被用作where条件的时候
3.查询出的数据占表中总数据比例在2%-4%
4.表中数据被经常查询的时候
101. 不创建;
1.表很小,数据量少的时候,表中不需要为表中某个字段创建索引
2.某个字段不会被经常用在where条件中,则不用给该字段创建索引
3.查询出的数据占表中总数据比例大于2%-4%,则不用创建索引
4.表中的数据经常被更新,则不用创建索引
lxvi. 索引删除;
drop index 索引名称;
drop index STUDENT_AGE_SEX_INDEX;
drop index SYS_C005172; --唯一索引不能删除
5、 Day05;
接下来我们就进入plsql学习阶段了。
什么是plsql?它是Oracle自带的数据库编程语句,要掌握的有很多。接下来我们为之一一讲解啦。
w) PLSQL的结构;
declare
声明区
begin
执行区
[exception
异常处理区]
end;
史上最简单的plsql语句:
begin
null;
end;
可以没有声明区、异常处理区,但就是不能没有执行区。
x) 课前准备;
在学习plsql之前我们先来了解一下再Oracle中plsql中的标识符、字符集、数据类型
lxvii. 标识符;
变量名,类型名,过程名,函数名,包名等等的统称
102. 标识符的规则;
a、必须以字母开头,其他部分可以是字母,数字,$,_,#组成
b、标识符中不能有空格
c、长度最大30
d、不能是关键字
e、如果标识符中有空格,或者是关键字,或者有非法字符,此时需要用双引号
例子:
   正确的: name, "x+y", "select","emp name", v$_#12
   错误的: 1name, x+y ,  select , emp name,  V~!*^-1,this_is_a_really_long_identifier
lxviii. 字符集;
a、字母: a-z A-Z
b、数字: 0-9
c、空白: 空格,tab,回车
d、符号:
   算术运算符: + - * /
   比较运算:   < > = !
   注释符号:   --单行注释    /*多行注释*/
   其他符号: () : ; ' " @ % & ||
   plsql里字符不区分大小写
lxix. 数据类型;
PLSQL支持标准SQL中的所有数据类型,并且还有自己特有数据类型
103. 数据类型分为;              
A、标量型;
    a、数字类型: int number float
    b、字符类型: char varchar varchar2
    c、日期类型: date
    d、逻辑类型: boolean
    e、扩充 %type
B、组合型
    a、record
    b、扩充 %rowtype
c、table
C、参考性
    a、cursor
b、LOB大对象类型
104. 变量的声明;
规则: 变量要先声明,后使用.
语法:
  变量名 [constant] 数据类型 [not null] [:=值];  
说明:
  变量声明时可以设置初始值,也可以不设置.
  如果变量声明时没有设置初始值,则值为null
  constant表示常量,not null 表示不可为空.
  常量和not null变量在声明时必须给初始值
  赋值符号 " := "
  赋值的时机: 声明时直接赋值或者在执行区做赋值.
变量的作用域:
declare
    --声明区
    i int:= 10;
begin
    --执行区
    dbms_output.put_line(i);
    null;
end;
PLSQL代码块中可以嵌入其他的plsql代码块
declare   
    i int:= 10; 
begin
    i:=20;  
    --dbms_output.put_line(i);
      declare
        j int :=30;  --内部代码块中定义的变量相当于局部变量
      begin
        dbms_output.put_line(i);  --内部代码块中可以访问外部代码块的变量.
        dbms_output.put_line(j);
      end;
      dbms_output.put_line(j);  -- 超出范围.
    null;
end;

例子1:
declare
num int:=5+3;
name char(5);  --没有赋值
begin
dbms_output.put_line(num);
dbms_output.put_line(name); --打印出空内容
end;

例子2:
declare
num constant int:=10;  --常量必须给初始值
name char(5) not null:='abc' ;  --not null 变量必须给初始值
begin
--num := 20;  --常量的值不可修改.
name :='def';
--name := null; --not null 变量不能再赋null值
dbms_output.put_line(num);
dbms_output.put_line(name);
end;

例子3:
declare
--if boolean; --if是关键字,不能作为标识符.
"if" boolean:=true; -- true,false,null
begin                       
--dbms_output.put_line("if"); --boolean类型不能打印,用于做判断操作
if("if")
then
dbms_output.put_line('true');
else dbms_output.put_line('false');
end if;
end;
变量的值:
可是数值串,表达式,函数    
105. 变量的使用;
可以打印,可以运算,可以赋值,还可以用来承接select返回的值
declare                        
num1 number(2);
num2 number(2);
num3 number(2);
begin                       
num1:=10;
num2:=20;
num3 := num1+num2;
dbms_output.put_line(num3);                        
end;
把select返回的值放到变量中:  select xxxs into xxxs  [from ....]    -- 必须保重返回的是单行.
例子1: 把指定工号对应的员工的姓名赋给一个变量,并打印出来
declare
name varchar(20);
num int := &no;
begin
-- select ename from emp where empno = 7369; --标准sql中的select语句.
select ename into name from emp where empno = num ; --把查询出的ename字段的值赋给name变量
dbms_output.put_line(name);
end;
注意: "select .. into 变量" 这种写法必须保证select返回的是单行记录.因为变量只能放一个值.
declare
name varchar(20);                    
begin                     
select ename into name from emp where deptno = 10 ; --select返回多条记录, into使用出错
dbms_output.put_line(name);
end;
例子2: 把指定工号对应的员工的姓名,工资赋给两个变量,并打印出来
declare
v_name varchar(20);
v_sal number(6,2);
num int := &no;
begin                    
select ename,sal into v_name,v_sal from emp where empno = num ;
--把查询出的ename字段的值赋给name变量
dbms_output.put_line('姓名:'||v_name);
dbms_output.put_line('工资:'||v_sal);
end;
注意:多个字段的值放到多个变量中去,字段于字段之间和变量于变量之间都用逗号分隔。 select出来的字段个数和类型要与变量的个数和类型统一.
declare
name char(10);                    
begin                     
select ename into name from emp where empno=7369 ;
dbms_output.put_line(name);
end;
例题3: 把7369号员工的所有信息在控制台打印出来
declare
v_EMPNO NUMBER(4);
v_ENAME VARCHAR2(10);
v_JOB VARCHAR2(9);
v_MGR NUMBER(4);
v_HIREDATE DATE;
v_SAL NUMBER(7,2);
v_COMM NUMBER(7,2);
v_DEPTNO NUMBER(2);
begin
select*into v_EMPNO,v_ENAME,v_JOB,v_MGR,v_HIREDATE,v_SAL,v_COMM,v_DEPTNO from emp where empno=7369;
dbms_output.put_line(v_EMPNO);
dbms_output.put_line(v_ENAME);
dbms_output.put_line(v_JOB);
dbms_output.put_line(v_MGR);
dbms_output.put_line(v_HIREDATE);
dbms_output.put_line(v_SAL);
dbms_output.put_line(v_COMM);
dbms_output.put_line(v_DEPTNO);
end;
106. %type;
在声明变量的时候,可以使得该变量的数据类型和表中指定的字段的数据类型相统一
语法:  变量名 表名.列名%type;
declare
v_EMPNO emp.empno%type;
v_ENAME emp.ename%type;
v_JOB emp.job%type;
v_MGR emp.mgr%type;
v_HIREDATE emp.hiredate%type;
v_SAL emp.sal%type;
v_COMM emp.comm%type;
v_DEPTNO emp.deptno%type;
begin
select*into v_EMPNO,v_ENAME,v_JOB,v_MGR,v_HIREDATE,v_SAL,v_COMM,v_DEPTNO from emp where empno=7369;
dbms_output.put_line(v_EMPNO);
dbms_output.put_line(v_ENAME);
dbms_output.put_line(v_JOB);
dbms_output.put_line(v_MGR);
dbms_output.put_line(v_HIREDATE);
dbms_output.put_line(v_SAL);
dbms_output.put_line(v_COMM);
dbms_output.put_line(v_DEPTNO);
end;
lxx. 组合数据类型 1. record;
组合类型是帮我们维护着一组变量的类型.
语法:
   1.创建record类型
      TYPE record_name IS RECORD ( 变量的定义[,变量定义] ) ;
   2.使用新创建的类型声明一个该类型的变量,方便代码中使用这个变量.
      record变量名字 record_name ;
   3.访问record里的变量
      record变量名.record里的变量名.
用法: 改善上面的代码;
declare
type emp_record_type is record(  --创建了一个record类型
      v_EMPNO emp.empno%type,
      v_ENAME emp.ename%type,
      v_JOB emp.job%type,
      v_MGR emp.mgr%type,
      v_HIREDATE emp.hiredate%type,
      v_SAL emp.sal%type,
      v_COMM emp.comm%type,
      v_DEPTNO emp.deptno%type
                           );
emp_rec emp_record_type;          --用record类型创建了变量.
begin
     /*
     select * into
          emp_rec.v_EMPNO ,emp_rec.v_ENAME,
          emp_rec.v_JOB, emp_rec.v_MGR,
          emp_rec.v_HIREDATE, emp_rec.v_SAL,
          emp_rec.v_COMM,emp_rec.v_DEPTNO
          from emp where empno=7369;
     */
     select * into emp_rec
          from emp where empno=7369;
          dbms_output.put_line(emp_rec.v_EMPNO);
          dbms_output.put_line(emp_rec.v_ENAME);
          dbms_output.put_line(emp_rec.v_JOB);
          dbms_output.put_line(emp_rec.v_MGR);
          dbms_output.put_line(emp_rec.v_HIREDATE);
          dbms_output.put_line(emp_rec.v_SAL);
          dbms_output.put_line(emp_rec.v_COMM);
          dbms_output.put_line(emp_rec.v_DEPTNO);
end;
练习: 根据输入的编号,打印出员工工号,姓名,job,部门信息.       
/* --格式:
员工信息:
7369 smith CLERK
部门信息:
20 RESEARCH DALLAS
*/         
declare
type emp_record_type is record(
v_EMPNO emp.empno%type,
v_ENAME emp.ename%type,
v_JOB emp.job%type,                         
v_DEPTNO emp.deptno%type,
v_dname dept.dname%type,
v_loc dept.loc%type
                        );
emp_info emp_record_type;       
begin                    
select empno,ename,job,dept.deptno,dname,loc into emp_info from emp , dept where empno=&empno and dept.deptno = emp.deptno;
dbms_output.put_line('员工信息:');
dbms_output.put_line(emp_info.v_EMPNO||' '||emp_info.v_ENAME||' '||emp_info.v_JOB);                     
dbms_output.put_line('部门信息:');
dbms_output.put_line(emp_info.v_DEPTNO||' '||emp_info.v_dname||' '||emp_info.v_loc );
end;
107. %rowtype;
表示用一个变量来涵盖表中的所有字段及数据类型
语法:变量名 表名%rowtype;
使用方式: 变量名.表中字段名
declare
emp_info emp%rowtype;
begin                   
select * into emp_info from emp where empno=7369;
dbms_output.put_line(emp_info.EMPNO);
dbms_output.put_line(emp_info.ENAME);
dbms_output.put_line(emp_info.JOB);
dbms_output.put_line(emp_info.MGR);
dbms_output.put_line(emp_info.HIREDATE);
dbms_output.put_line(emp_info.SAL);
dbms_output.put_line(emp_info.COMM);
dbms_output.put_line(emp_info.DEPTNO);
end;
=============================================================
declare
emp_info emp%rowtype;
dept_info dept%rowtype;
begin  
--select emp.* , dept.* into emp_info,dept_info                 
select ename,dname into emp_info.ename,dept_info.dname from emp,dept where empno=7369 and emp.deptno = dept.deptno;                     
dbms_output.put_line(emp_info.ENAME);
dbms_output.put_line(dept_info.dname);
end;
108. record 于rowtype的异同;
相同点: 都维护者多个字段或变量
不同点: record可以自定义你的变量,rowtype不能自己定义只能拿到整张表的表头字段
lxxi. 组合类型2: table;
table:类似高级语言中的数组,可以用来存放多条记录
和高级语言中的数组的区别: 没有固定大小,下标可以是负数.
109. 创建语法;
1.创建类型
    TYPE table_type_name IS TABLE OF 类型
    index by binary_integer;  --表示用整数作为下标值
2.创建变量
    变量名 table_type_name;
    用法: 
        table类型变量名(下标)
        table类型变量名(下标).字段名
例子1:
declare
emp_info emp%rowtype;    --只能存放单行记录                
begin                   
select * into emp_info from emp where empno=7369;
dbms_output.put_line(emp_info.EMPNO);
select * into emp_info from emp where empno=7499;
dbms_output.put_line(emp_info.EMPNO);
end;
table使用的例子1;
declare                         
TYPE emp_row is table of emp%rowtype
index by binary_integer;  
er emp_row;  --声明table类型变量
begin                   
select * into er(-1)  --使用table类型变量是需要指定下标
from emp where empno=7369;                     
select * into er(2)  --使用table类型变量是需要指定下标
from emp where empno=7499;                     
dbms_output.put_line(er(-1).empno);
dbms_output.put_line(er(2).empno);
end;
table使用的例子2;
declare                         
TYPE emp_row is table of varchar2(20)  --声明数据类型
index by binary_integer;  
er emp_row;  --声明table类型变量
begin                   
select ename into er(-1)  --使用table类型变量是需要指定下标
from emp where empno=7369;                     
select ename into er(2)  --使用table类型变量是需要指定下标
from emp where empno=7499;                     
dbms_output.put_line(er(-1));
dbms_output.put_line(er(2));
end;
table使用的例子3;
declare  
type ename_sal_record is record (
      v_ename emp.ename%type,
      v_sal emp.sal%type
                           );
TYPE emp_row is table of ename_sal_record  -- table类型是record
index by binary_integer;  
er emp_row;  --声明table类型变量
begin                   
select ename,sal into er(-1)  --使用table类型变量是需要指定下标
from emp where empno=7369;
dbms_output.put_line(er(-1).v_ename);
dbms_output.put_line(er(-1).v_sal);                   
end;
lxxii.  plsql中的控制语句;
a、 条件控制
b、 循环控制
110. 条件控制;
a、 if语句
关键字:if 、then、 elsif 、else、 end if
语法结构:
if(条件) then dosth;
[elsif(条件) then dosth ;]
[else dosth; ]
end if;
例子:
begin
    if(to_char(sysdate,'day')='星期六') then dbms_output.put_line('打球');
    elsif(to_char(sysdate,'day')='星期日') then dbms_output.put_line('逛街');
    else dbms_output.put_line('上班');
    end if;
end;
     
begin
    if(null) then dbms_output.put_line('条件为真'); --判断语句中的条件如果是null则等同于false
    else dbms_output.put_line('条件为假');
    end if;
end;
b、 case语句
关键字:case、when、case when、then、else、end case
语法结构:
case 字段|变量
when 值 then dosth;
[when 值 then dosth;]
else doth;
end case;
      
case
when 条件判断 then dosth;
[when 条件判断 then dosth;]
[else doth;]
end case;
例子:
declare
num number(2):=&num;
begin
case num
when 1 then dbms_output.put_line(1);
when 2 then dbms_output.put_line(2);
else dbms_output.put_line('其他数字');
end case;
end;
      
declare
num number(2):=&num;
begin
case
    when num=0 then dbms_output.put_line('等于0');
    when num<0 then dbms_output.put_line('负数');
    else dbms_output.put_line('正数');
end case;
end;
111. 循环控制;
a、 loop简单循环
语法:
loop
--dosth;
--在loop里必须编写退出循环的条件
--dosth;
end loop;
退出循环的写法:
1、 if treu | false then exit;end if;
2、 exit when true | false;
执行过程:
先进入循环体,在作出判断。
注意:
如果循环体内没有其他的执行语句的话,也要用null语句来补全,但是此时的循循环式无限循环,因为没有条件退出来。
例子1:打印1~100之间的整数
declare
num number(2):=1;
begin
  loop
     dbms_output.put_line(num);
     num:=num+1;
     exit when num>100;
  exit loop;
end;

declare
  num number(2):=1;
begin
  loop
     exit when num>100;
dbms_output.put_line(num);
num:=num+1;
  end loop;
end;
例子2:plsql中可以执行DML.  把姓名,job,入职时间,工资其中一项为空的员工删除掉.
declare
del_empno number(4):=7935;
max_empno number(4);
begin
select max(empno) into max_empno from emp;
loop
delete from emp where empno = del_empno;
exit when del_empno>= max_empno;
del_empno := del_empno+1;
end loop;
--delete from emp where empno is null or job is null or hiredate is null or sal is null;
end;
练习:给员工表中插入记录 工号8000+, 姓名mark1...n
declare
num number(10):=1;
name varchar(5):='mark';
begin
loop
insert into emp(empno,ename) values (8000+num,name||num);
num:= num+1;
exit when num>3;
end loop;
end;
select * from emp;
b、 while循环
语法:
while true|false loop
--dosth;
end loop;
执行方式:
先判断,再进入循环体
例子1:打印1-10之间的整数
declare
num number(2):=1 ;
begin
while num<=10 loop
dbms_output.put_line(num);
num := num+1;
end loop;
end;
c、 for循环
语法:
for loop_flag in [reverse] min_bound .. max_bound loop
--dosth;
end loop;
执行方式:
判断,循环标记的值在最低值和最高值之间即执行循环体
注意:
    循环标记可以不用声明,直接给变量名字.
    循环标记本身就是个一个整数变量,作用范围在for循环内部.
例子1: 打印1-10之间的整数
begin
for i in 1 .. 10 loop 
--不用声明i的数据类型.在循环体的内部不用编写i值的更改方式
dbms_output.put_line(i);
end loop;
end;
例子2:打印10-1之间的整数
begin
for i in reverse 1 .. 10 loop
dbms_output.put_line(i);
end loop;
end;
例子3:打印10-1之间的整数
begin
for i in 1 .. 10 loop
dbms_output.put_line(i);
end loop;
dbms_output.put_line(i);  --此处的i没有声明
end; 
   
例子3: 打印10-1之间的整数
declare
i number(2):=80;  --变量i.
    begin
        dbms_output.put_line('before:'||i);
        for i in reverse 1 .. 10 loop  --此处的i是for循环的局部变量.
            dbms_output.put_line(i);
     end loop;
     dbms_output.put_line('after:'||i); 
end; 
练习:分别用loop,while,for循环把1到100的累加和打印出来
--loop
declare
i number(3):=1;
sum_num number(4):=0;
begin
loop
sum_num:=sum_num+i;              
i := i+1;
exit when i>100;
end loop;
dbms_output.put_line(sum_num);
end;
          
--while
declare
i number(3):=1;
sum_num number(4):=0;
begin
while i<=100 loop
sum_num := sum_num+i;
i:=i+1;
end loop;             
dbms_output.put_line(sum_num);
end;  

--for
declare           
sum_num number(4):=0;
begin
for i in 1..100 loop            
sum_num := sum_num+i;             
end loop;             
dbms_output.put_line(sum_num);
end;
嵌套循环练习: 分别用Loop,while,for循环模拟打印出11分钟以内的分钟,秒钟. 格式如: 01分08秒  10分59秒
begin
  dbms_output.enable(999999999999999999999);   --设置输出语句的缓冲区的最大的字符长度.
  --如过长度不够则抱ORA-2000:ORU-10027的异常. 输出内容过多,超出了缓冲区长度,
  for i in 0..10 loop 
    for j in 0..59 loop
        dbms_output.put_line(to_char(i,'00')||'分'||to_char(j,'00')||'秒');
    end loop;
  end loop;
end;
112. 循环补充;
  a、java中有break,contine
  b、PLSQL中有goto,exit
c、java PLSQL里都有标签的概念
  d、Oracle10g里没有continue,11g有continue
 
  PLSQL中标签的语法:
         <<标签名字>>
  标签用来标记循环,还可以标记代码结构
  标签的用法:
         goto 标签
         exit 标签
例1 ;
begin
  dbms_output.enable(999999999999999999999);   --设置输出语句的缓冲区的最大的字符长度.
  如过长度不够则抱ORA-2000:ORU-10027的异常. 输出内容过多,超出了缓冲区长度,
  for i in 0..5 loop 
    for j in 0..59 loop
        dbms_output.put_line(to_char(i,'00')||'分'||to_char(j,'00')||'秒');       
        exit when i=3 and j = 15;  -- 退出当前循环.
    end loop;
  end loop;
end;
例2;
begin
  dbms_output.enable(999999999999999999999);   --设置输出语句的缓冲区的最大的字符长度.
  --如过长度不够则抱ORA-2000:ORU-10027的异常. 输出内容过多,超出了缓冲区长度,
  <<outer>>
  for i in 0..5 loop 
    <<innser>>
    for j in 0..59 loop       
        dbms_output.put_line(to_char(i,'00')||'分'||to_char(j,'00')||'秒');
        exit outer when i=3 and j = 15;  --退出指定标签
    end loop;
  end loop;
end;
例3;
begin
      <<a>>
         dbms_output.put_line(1);
         goto b;   --goto到指定标签.
         dbms_output.put_line(2);
      <<b>>
         dbms_output.put_line(3);
      <<c>>
         dbms_output.put_line(4);
         goto a;  --goto,用在从前向后goto;千万不要从后向前goto
      <<d>>
         dbms_output.put_line(5);
end;
lxxiii. 游标cursor;
  游标可以存放多条数据
  游标的使用步骤:
    1.声明
    2.打开
    3.使用游标提取数据
    4.关闭游标
113.   声明;
语法 : CURSOR cur_name IS subQuery;
例子: 声明游标.
declare
CURSOR emps_dept10_cur is select * from emp where deptno = 10; 
-- 声明一个游标,游标内存放着10号部门的员工信息.
begin
null;
end;
114. 打开 open 与关闭 close;
语法: open cur_name;
close cur_name;
注意:
在使用游标之前一定要打开游标.
没有打开游标就去对游标做操作,比如fetch,比如调用属性都是非法操作.
打开一个已经打开的游标和关闭一个已经关闭的游标都是非法的.
例子: 打开,关闭游标.
declare
         CURSOR emps_dept10_cur is select * from emp where deptno = 10; 
begin
open emps_dept10_cur; --打开
       null;
close emps_dept10_cur; --关闭
end;
115. 使用游标,从游标中取值 fetch;
语法:
1. fetch cur_name into var1,var2, .... ;
2. fetch cur_name into record_var ;
例子1: 从游标取出值放到变量里去. 要求保证存放游标值的变量个数要和游标中字段的个数和数据类型相统一
declare
CURSOR emps_dept10_cur is select empno,ename,job,deptno from emp where deptno = 10; 
v_empno emp.empno%type;
v_ename emp.ename%type;
v_job emp.job%type;
v_deptno emp.deptno%type;
begin
open emps_dept10_cur;
fetch emps_dept10_cur into v_empno,v_ename,v_job,v_deptno;
dbms_output.put_line(v_empno||' '||v_ename||' '||v_job||' '||v_deptno);
         /* -- 再去取值
         fetch emps_dept10_cur into v_empno,v_ename,v_job,v_deptno;
         dbms_output.put_line(v_empno||' '||v_ename||' '||v_job||' '||v_deptno);
         fetch emps_dept10_cur into v_empno,v_ename,v_job,v_deptno;
         dbms_output.put_line(v_empno||' '||v_ename||' '||v_job||' '||v_deptno);
         */
close emps_dept10_cur;
end;
例子2: 从游标取出值放到组合类型 record %rowtype table
declare
CURSOR emps_dept10_cur is select empno,ename,job,deptno from emp where deptno = 10; 
type emp_record is record (  --record
v_empno emp.empno%type,
v_ename emp.ename%type,
v_job emp.job%type,
v_deptno emp.deptno%type
);
er emp_record;
begin
open emps_dept10_cur;
fetch emps_dept10_cur into  er; ------
dbms_output.put_line(er.v_empno||' '||er.v_ename||' '||er.v_job||' '||er.v_deptno);
close emps_dept10_cur;
end;
例子2.1: 从游标取出值放到组合类型 record %rowtype table
declare
CURSOR emps_dept10_cur is select * from emp where deptno = 10; 
emp_row emp%rowtype;    --rowtype
begin
open emps_dept10_cur;
fetch emps_dept10_cur into  emp_row;
dbms_output.put_line(emp_row.empno||' '||emp_row.ename||' '||emp_row.job||' '||emp_row.deptno);
close emps_dept10_cur;
end;
例子2.3: 从游标取出值放到组合类型 record %rowtype table
declare
CURSOR emps_dept10_cur is select * from emp where deptno = 10;
type emps_table is table of emp%rowtype index by binary_integer;
empr emps_table;
begin
open emps_dept10_cur;
fetch emps_dept10_cur into empr(1);
fetch emps_dept10_cur into empr(2);
fetch emps_dept10_cur into empr(3);
dbms_output.put_line(empr(1).empno||' '||empr(1).ename||' '||empr(1).job||' '||empr(1).deptno);        
dbms_output.put_line(empr(2).empno||' '||empr(2).ename||' '||empr(2).job||' '||empr(2).deptno);
dbms_output.put_line(empr(3).empno||' '||empr(3).ename||' '||empr(3).sal);
close emps_dept10_cur;
end;
116. 游标属性的使用;
1.%found -- 是boolean值,表示是否有数据
2.%notfound --是boolean值, 表示是否没有数据
3.%isopen --是boolean值,表示游标是否打开
4.%rowcount --是整数, 表示游标的指针位置
属性使用例子1: isopen
declare
CURSOR emps_dept10_cur is select * from emp where deptno = 10;   
mp_row emp%rowtype;  
begin
dbms_output.put('游标是否打开?');
if (emps_dept10_cur%isopen) then
dbms_output.put_line('已经打开')  ;
else
dbms_output.put_line('未打开')  ;
dbms_output.put_line('open 游标');
open emps_dept10_cur;
end if;
dbms_output.put('游标是否打开?');
if (emps_dept10_cur%isopen) then
dbms_output.put_line('打开')  ;
else
dbms_output.put_line('未打开')  ;
end if;
  close emps_dept10_cur;
end;
属性使用例子2: found notfound
declare
emps_dept10_cur is select * from emp where deptno = 10;   
emp_row emp%rowtype;  
begin
open emps_dept10_cur; 
--把游标打开,就是把指针定位到了游标中第一行数据的上面,并没有指向任何数据行.   
--所以此时用%found 或是%notfound来判断是否有数据时不准确的.
--要正确的使用found和notfound必须使指针指向数据才有效果.            
dbms_output.put_line('刚打开游标,并没有对游标做任何操作,判断是否能拿到数据');
if (emps_dept10_cur%found) then
dbms_output.put_line('能拿到数据')  ;
else dbms_output.put_line('拿不到数据')  ;
end if; 
dbms_output.put_line('刚打开游标,判断是否拿完数据');
if (emps_dept10_cur%notfound) then
dbms_output.put_line('数据已经拿完了')  ;
else dbms_output.put_line('数据还么拿完')  ;
end if;  
close emps_dept10_cur;            
end;
没有循环的fetch,要手动的去控制fetch的次数.
declare
CURSOR emps_dept10_cur is select * from emp where deptno = 10;    --10号部门现在有三条记录
emp_row emp%rowtype;    --rowtype
begin
open emps_dept10_cur;
fetch emps_dept10_cur into  emp_row;
dbms_output.put_line(emp_row.empno||' '||emp_row.ename||' '||emp_row.job||' '||emp_row.deptno);
fetch emps_dept10_cur into  emp_row;
dbms_output.put_line(emp_row.empno||' '||emp_row.ename||' '||emp_row.job||' '||emp_row.deptno);
fetch emps_dept10_cur into  emp_row;
dbms_output.put_line(emp_row.empno||' '||emp_row.ename||' '||emp_row.job||' '||emp_row.deptno);
--总共有三条记录,我们可以fetch 3次拿出3条记录.但是当第四次fetch的时候发现拿到得是最后一条.
fetch emps_dept10_cur into  emp_row;  --拿到最后一条记录
dbms_output.put_line(emp_row.empno||' '||emp_row.ename||' '||emp_row.job||' '||emp_row.deptno); 
fetch emps_dept10_cur into  emp_row;  --拿到最后一条记录
dbms_output.put_line(emp_row.empno||' '||emp_row.ename||' '||emp_row.job||' '||emp_row.deptno);                                  
close emps_dept10_cur;
end;
一般fetch语句要和循环配合起来使用. 需要指定循环退出的条件.退出条件可以使用游标的属性.
loop循环使用游标
利用%found控制循环
declare
CURSOR emps_dept10_cur is select * from emp where deptno = 10;   
emp_row emp%rowtype;    --rowtype
begin
open emps_dept10_cur;  --操作游标前要先打开游标
loop
fetch emps_dept10_cur into  emp_row; 
--先fetch一边才可以用found 和notfound来判断是否有数据
if(emps_dept10_cur%found) then  --判断是否有数据
dbms_output.put_line(emp_row.empno||' '||emp_row.ename||' '||emp_row.job||' '||emp_row.deptno);
else exit; --没有数据就退出
end if;
end loop;
close emps_dept10_cur;
end;
利用%notfound控制循环
declare
CURSOR emps_dept10_cur is select * from emp where deptno = 10; 
emp_row emp%rowtype;   
begin
open emps_dept10_cur;  --打开游标
loop
fetch emps_dept10_cur into  emp_row;  --先fetch一边才可以用found 和notfound来判断是否有数据
exit when emps_dept10_cur%notfound;  --判断是否有数据,没有数据就退出
dbms_output.put_line(emp_row.empno||' '||emp_row.ename||' '||emp_row.job||' '||emp_row.deptno);
end loop;
close emps_dept10_cur;
end;
while循环使用游标
declare
CURSOR emps_dept10_cur is select * from emp where deptno = 10;  
emp_row emp%rowtype;   
begin
open emps_dept10_cur; 
fetch emps_dept10_cur into emp_row;
--在while里使用游标的%found %notfound判断是否有数据,必须在while之前fetch一下游标
while (emps_dept10_cur%found) loop 
dbms_output.put_line(emp_row.empno||' '||emp_row.ename||' '||emp_row.job||' '||emp_row.deptno);
fetch emps_dept10_cur into  emp_row; --是指针下移一行.
end loop;
close emps_dept10_cur;
end;
for循环使用游标
declare
CURSOR emps_dept10_cur is select * from emp where deptno = 10; 
begin
for emp_row in emps_dept10_cur loop
dbms_output.put_line(emp_row.empno||' '||emp_row.ename||' '||emp_row.job||' '||emp_row.deptno);                  
end loop;
end;               
注意: for语句去循环遍历游标无需打开和关闭右边,无需fetch游标.
     for的循环控制标记就充当了rowtype类型变量.
     只需要在for循环内部去循环 控制变量.字段名取值即可.
lxxiv. 异常处理;
代码中出现过的错误;
b、 错误:代码语法的错误,系统错误,无需处理
c、 异常:运行时候的错误,可以处理
异常处理的结构:在执行去内部书写
declate
begin
  exception – 异常处理
when 异常名字 then 异常处理的代码
end;
异常的作用是捕获执行区代码抛出的异常,并且进行处理的
系统预定的异常:
117. ZERO_DIVIDE--除数为0异常;
除数为0的异常;
select 10 / 0 from dual; -- 普通SQL中除数为0也是个异常
declare
  num number(2);
begin
  num := 10 / 0; --PLSQL中除数为0 会抛出异常
end;
--除数为0 的异常处理
declare
  num number(2);
begin
  num := 10 / 0; --抛出zero_divide
exception
  when zero_divide then
    dbms_output.put_line('除数为0,发生了异常');
end;
在普通的SQL中没有返回的数据不算异常
select * from emp where empno = 8369;
-- emp表中没有8369这个工号的记录,在PLSQL种是会抛出异常的.
declare
  name emp.ename%type;
begin
  select ename into name from emp where empno = 8369;
end;
118. NO_DATA_FOUND --无数据异常;
没有数据异常的处理
declare
  name emp.ename%type;
begin
  select ename into name from emp where empno = 8369; --抛出no_data_found
exception
  when no_data_found then
    dbms_output.put_line('没有该工号的员工信息');
end;
119. TOO_MANY_ROWS--返回太多行;
在PLSQL种返回的行数太多.
declare
  name emp.ename%type;
begin
  select ename into name from emp where deptno = 10;
end;
返回太多行异常的处理
declare
  name emp.ename%type;
begin
  select ename into name from emp where deptno = 10;
end;
120. VALUE_ERROR--值错误异常;
begin
  dbms_output.put_line(to_number('ab')); --VALUE_ERROR
end;
--VALUE_ERROR的处理   
declare
  num number(4);
begin
  num := 'abc'; --VALUE_ERROR
  --dbms_output.put_line(to_number('a'));  --VALUE_ERROR
exception
  when VALUE_ERROR then
    dbms_output.put_line('数据类型不匹配!');
end;
i. 异常的捕获;
当执行的代码跑出了某个异常,则要在异常区域用when来匹配相应的异常,匹配成功则执行相应的代码,否则会向下匹配,如果从头到尾度没有匹配上的话,则依然抛出。
异常捕获的顺序,从上到下依次去匹配when中列出的异常. others关键字可以捕获所有异常
declare
  num  number(4);
  name emp.ename%type;
begin
  --num := 10/0 ; --
  --num := 'abc'; --VALUE_ERROR
  --select ename into name from emp where deptno = 10;  --抛出too_many_rows
  select ename into name from emp where empno = 888; --
exception
  when too_many_rows then
    dbms_output.put_line('返回了太多数据行');
  when VALUE_ERROR then
    dbms_output.put_line('数据类不匹配!');
  when zero_divide then
    dbms_output.put_line('除数为0!');
    --when no_data_found then dbms_output.put_line('没有数据!');
  --when others then dbms_output.put_line('某个未知异常!');  --others可以捕获所有异常
end;
ii. 自定义异常;
exception是一个类型,自定义异常就是根据exception区创建一个变量
语法:
自定义异常名字 exception;
用法:
当我们认为该抛出异常的时候抛出就可以
异常抛出的语法:
reise 异常名字
自定义异常也可以在异常处理区捕获并处理
eg:
declare
  so_low_sal_exception exception; --自定义异常
  v_sal emp.sal%type;
begin
  select sal into v_sal from emp where empno = &empno;
  if (v_sal < 1000) then
    raise so_low_sal_exception; --手动抛出异常
  end if;
exception
  when so_low_sal_exception then
    dbms_output.put_line('工资太低了!');
end;
iii. 练习;
输入一个员工姓名,如果该员工的岗位是经理,则把他部门下的所有员工信息打印出,否则只打印他自己的信息
所需要用到的知识点:
    plsql结构
    &
    游标
    循环
    判断
=解法1==================
declare
  v_name   emp.ename%type := '&name';
  v_job    emp.job%type;
  v_deptno emp.deptno%type;

  cursor emp_in_deptno is
    select * from emp where deptno = v_deptno; --游标定义,用于存放多条记录.
  emp_info emp%rowtype;
begin
  select * into emp_info from emp where ename = v_name;
  v_job    := emp_info.job;
  v_deptno := emp_info.deptno;

  if v_job = 'MANAGER' then
    for emps in emp_in_deptno loop
      dbms_output.put_line(emps.empno || ' ' || emps.ename || ' ' ||
                           emps.job || ' ' || emps.mgr || ' ' ||
                           emps.hiredate || ' ' || emps.sal || ' ' ||
                           emps.comm || ' ' || emps.deptno);
    end loop;
  else
    dbms_output.put_line(emp_info.empno || ' ' || emp_info.ename || ' ' ||
                         emp_info.job || ' ' || emp_info.mgr || ' ' ||
                         emp_info.hiredate || ' ' || emp_info.sal || ' ' ||
                         emp_info.comm || ' ' || emp_info.deptno);
  end if;
exception
  when no_data_found then
    dbms_output.put_line('找不到该员工!!');
end;
=解法2==================
declare
  v_name   emp.ename%type := '&name';
  v_job    emp.job%type;
  v_deptno emp.deptno%type;
  emp_info emp%rowtype;
begin
  select * into emp_info from emp where ename = v_name;
  v_job    := emp_info.job;
  v_deptno := emp_info.deptno;

  if v_job = 'MANAGER' then
    for emps in (select * from emp where deptno = v_deptno) loop
      --就是隐式游标.隐式游标不用声明,直接用用在for循环里
      dbms_output.put_line(emps.empno || ' ' || emps.ename || ' ' ||
                           emps.job || ' ' || emps.mgr || ' ' ||
                           emps.hiredate || ' ' || emps.sal || ' ' ||
                           emps.comm || ' ' || emps.deptno);
    end loop;
  else
    dbms_output.put_line(emp_info.empno || ' ' || emp_info.ename || ' ' ||
                         emp_info.job || ' ' || emp_info.mgr || ' ' ||
                         emp_info.hiredate || ' ' || emp_info.sal || ' ' ||
                         emp_info.comm || ' ' || emp_info.deptno);
  end if;
exception
  when no_data_found then
    dbms_output.put_line('找不到该员工!!');
end;
=解法3=游标的参数. 游标中定义的子查询可以用的变量.=
declare
  v_name   emp.ename%type := '&name';
  v_job    emp.job%type;
  v_deptno emp.deptno%type;
  cursor emp_in_deptno(dno emp.deptno%type) is
    select * from emp where deptno = dno; --有参数的游标
  emp_info emp%rowtype;
begin
  select * into emp_info from emp where ename = v_name;
  v_job    := emp_info.job;
  v_deptno := emp_info.deptno;

  if v_job = 'MANAGER' then
    for emps in emp_in_deptno(v_deptno) loop
      dbms_output.put_line(emps.empno || ' ' || emps.ename || ' ' ||
                           emps.job || ' ' || emps.mgr || ' ' ||
                           emps.hiredate || ' ' || emps.sal || ' ' ||
                           emps.comm || ' ' || emps.deptno);
    end loop;
  else
    dbms_output.put_line(emp_info.empno || ' ' || emp_info.ename || ' ' ||
                         emp_info.job || ' ' || emp_info.mgr || ' ' ||
                         emp_info.hiredate || ' ' || emp_info.sal || ' ' ||
                         emp_info.comm || ' ' || emp_info.deptno);
  end if;
exception
  when no_data_found then
    dbms_output.put_line('找不到该员工!!');
end;
6、 Day06;
y) PlSQL子程序;
plsql子程序中包括过程、函数和包。
iv. 那么我们先看看那过程的创建于法:
create [or replace] procedure<过程名字> [(参数定义 [参数定义])] is | as <过程体>
121. 参数定义的方式;
<参数名字> [{参数模式in|out|in out}] 数据类型;
参数中数据类型说明:
      不要指定长度
122. 过程体的说明;
就是plsql代码块begin、、、end;
is | as到bengin之间就变成了声明区了
123. 过程体创建的例子;
create or replace procedure output_emps(name char) is
  --is 或as 到begin之间就是声明区
  v_name   emp.ename%type := name;
  v_job    emp.job%type;
  v_deptno emp.deptno%type;
  cursor emp_in_deptno is
    select * from emp where deptno = v_deptno; --游标定义,用于存放多条记录.
  emp_info emp%rowtype;
begin
  select * into emp_info from emp where ename = v_name;
  v_job    := emp_info.job;
  v_deptno := emp_info.deptno;

  if v_job = 'MANAGER' then
    for emps in emp_in_deptno loop
      dbms_output.put_line(emps.empno || ' ' || emps.ename || ' ' ||
                           emps.job || ' ' || emps.mgr || ' ' ||
                           emps.hiredate || ' ' || emps.sal || ' ' ||
                           emps.comm || ' ' || emps.deptno);
    end loop;
  else
    dbms_output.put_line(emp_info.empno || ' ' || emp_info.ename || ' ' ||
                         emp_info.job || ' ' || emp_info.mgr || ' ' ||
                         emp_info.hiredate || ' ' || emp_info.sal || ' ' ||
                         emp_info.comm || ' ' || emp_info.deptno);
  end if;
exception
  when no_data_found then
    dbms_output.put_line('找不到该员工!!');
end;
v. 调用过程;
124. 在plsql中调用;
begin
  output_emps('&name'); --如果过程有参数,则必须传参.
end;
125. 命令行调用;
set serveroutput on;
    execute output_emps('&name');
vi. 删除过程;
    drop procedure 过程名;
vii. 查看当前用户下的所有过程;
    select * from user_procedures;
viii. 有名块和无名块;
自己去了解了
z)     过程或函数中参数的输入输出模式;
ix. in;
in输入参数,表示可以给过程输入值,但是in参数的值是不可以修改的
x. out;
out输出参数,表示过程调用后会把值输出给调用者。out参数在调用时会忽略传入变量的值,但是运行结束后会把out参数的值赋给调用时传入的标量
xi. in out;
in out具备以上两种的特点,可以输入,也可输出。
没有参数的过程;
create or replace procedure test_pro1 is
begin
  dbms_output.put_line('hello');
end;
--调用
begin
test_pro1;
end;
我们来看一下啊in 、out 、in out的详细例子吧
in参数,输入参数,在调用过程时把实参传入到过程形参中,形参的值可以是在定义区和执行区使用,但是不能修改
create or replace procedure test_pro2(num number, str in char) --默认in模式的参数
is
  id number(5) := num;
begin
  dbms_output.put_line(num);
  dbms_output.put_line(id);
  dbms_output.put_line(str);
  --num := 20; --in类型的值不能修改.
end;
--调用
begin
test_pro2(10, 'abc');
end;
out参数,输出参数,在调用过程中是,必须传入一个变量于这个输出参数相对应,而不能传入值,在过程执行完之后,刚才输入的变量就被过程赋值。
create or replace procedure test_pro3(name out char) --name参数必须传入变量来调用. 并且name会忽略传进来变量的值.
is
begin
  dbms_output.put_line(name);
  name := 'smith'; --可以对out模式的变量赋值.
end;
--调用
declare v_n char(20) := 'KING';
begin
  --test_pro3('smith'); 由于test_pro3过程中的参数是out模式的,所以必须传入变量才可以调用.
test_pro3(v_n); dbms_output.put_line(v_n); --变量v_n的值来自于过程中name参数的值. 过程调用完毕后,参数name会把它的值输出给v_n.
end;
in out参数,输入输出参数,具有in和out模式的参数特点。
create or replace procedure test_pro4(name in out char) --name参数必须传入变量来调用.
is
begin
  dbms_output.put_line(name);
  name := 'smith';
end;
--调用
declare v_n char(20) := 'KING';
begin
test_pro4(v_n); dbms_output.put_line(v_n);
end;
in 、out 、in out
create or replace procedure inout_test_pro(var1 char,
                                           var2 in char,
                                           var3 out char,
                                           var4 in out char) is
begin
  dbms_output.put_line(var1); --in参数
  dbms_output.put_line(var2); --in参数
  dbms_output.put_line(var3); --out参数 , 是用来输出数值的,不用来接收数值. 调用具有out参数的过程,必须用变量调用.
  dbms_output.put_line(var4);
  --var1 := var1*10; -- in模式的参数,值不能修改.
  var3 := 'A';
  var4 := 'D';
end;
--调用
declare
  var3_val char;
  var4_val char;
begin
  --inout_test_pro(1,2,3,4);
  var3_val := 'a';
  var4_val := 4;
  inout_test_pro(1, 2, var3_val, var4_val); --用var3_val对应过程中的out参数
  dbms_output.put_line(var3_val);
  dbms_output.put_line(var4_val);
end;
aa) 查看过程,函数的错误命令;
   show errors procedure|function 过程或函数名字; 
eg:查看错误的命令(命令窗口执行): 
show errors procedure inout_test_pro ;
xii. 练习;
写一个过程,功能要求输入员工编号in,输出工资out. 在PLSQL种调用这个过程,传入参数,打印出工资。
create or replace procedure emp_sal(v_empno  in number,
                                    v_sal    out number,
                                    isExists out boolean) is
begin
  dbms_output.put_line('查询' || v_empno || '的工资..');
  select sal into v_sal from emp where empno = v_empno;
  isExists := true;
  dbms_output.put_line('查询' || v_empno || '的工资结束');
exception
  when no_data_found then
    dbms_output.put_line('查无此人!');
    isExists := false;
end;
-------------------------------调用
declare empno emp.empno%type := &empno; sal emp.sal%type; isExists boolean;
begin
emp_sal(empno, sal, isExists); if isExists then dbms_output.put_line(empno || '的工资是:' || sal); else dbms_output.put_line('输入错误,请从新输入');
end if;
end;
bb) 调用过程或函数的传参方式;
1、 位置传参
2、 名称传参
3、 混合传参
create or replace procedure test_pro5(num1 int,
                                      str1 char,
                                      num2 int,
                                      str2 char) is
begin
  null;
end;

  --调用,因为有参数,所以要传参调用.
declare vn1 int := 1; vs1 varchar(20) := 'a'; vn2 int := 10; vs2 varchar(20) := 'A';
begin
test_pro5(vn1, vs1, vn2, vs2); test_pro5(vn2, vs2, vn1, vs1); test_pro5(5, 'abc', 50, 'ABC'); --以上个都是 位置传递
  --test_pro5(vs1,vn1,vn2,vs2);

test_pro5(num1 => 1, num2 => 2, str2 => 'ab', str1 => 'de'); -- 名称传递 语法:  参数名 => 值
  --通过名称传递,可以不用关注参数的位置和顺序.

test_pro5(1, vs1, str2 => vs2, num2 => 100); --混合传递 ,要求:第一个参数必须采用位置传递..
end;
cc) 函数 function;
其在plsql中也是有名字的,也可以在其他的plsql中被调用
xiii. 函数和过程的区别是;
1、 函数必须有返回值,return
2、 过程可以是plsql的一个语句,但是函数必须作为plsql语句的一部分,或者是sql语句中的一部分,不能单独存在的
xiv. 函数于法的创建;
create or replace function 函数名字
      [(参数定义[,参数定义...])]
      return 数据类型
      is|as
      function_body;
126. 例子:创建一个无参的函数;
create or replace function test_fun1 return char --函数有返回类型,所以执行体中必须有返回语句 return
is
  str char(20);
begin
  str := 'abc';
  return str; --如果没有返回语句,函数创建可以成功,但是在调用时会出错. ORA06503:函数未返回值               
end;

begin
dbms_output.put_line(test_fun1);
end;
127. 例子:创建一个有参的函数;
规则:比较两个数字,打印出最大的,如果两个数相等则不打印
create or replace function test_fun2(var1   in int,
                                     var2   in char,
                                     maxnum out int) return boolean --函数有返回类型,所以执行体中必须有返回语句 return
is
  bigger_or_less boolean := true;
begin
  if (var1 = var2) then
    bigger_or_less := false;
  elsif var1 > var2 then
    maxnum := var1;
  else
    maxnum := var2;
  end if;
  return bigger_or_less;
end;

declare num1 int := &num1; num2 int := &num2; maxnum int;
begin
if (test_fun2(num1, num2, maxnum)) then dbms_output.put_line(maxnum); else dbms_output.put_line('两个数字相等');
end if;
end;
128. 例子:有参数的函数. 接受员工编号,姓名然后插入到emp表中;
create or replace function test_fun3(v_empno int, v_ename char)
  return boolean is
  isOk boolean := false;
begin
  insert into emp (empno, ename) values (v_empno, v_ename);
  commit;
  isOk := true;
  return isOk;
exception
  when others then
    dbms_output.put_line('数据插入时发生某异常');
    isOk := false;
    return isOk;
end;

begin
if (test_fun3(&empno, '&ename')) then dbms_output.put_line('一行被插入'); else dbms_output.put_line('插入失败.');
end if;
end;
129. 例子:有参数的函数. 接受员工编号,姓名然后插入到emp表中;
create or replace function test_fun4(v_empno int, v_ename char) return int is
  flag int; --1表示插入成功.
begin
  insert into emp (empno, ename) values (v_empno, v_ename);
  commit;
  flag := 1;
  return flag;
exception
  when others then
    dbms_output.put_line('数据插入时发生某异常');
    flag := 0;
    return flag;
end;

begin
if (test_fun4(&empno, '&ename') = 1) then dbms_output.put_line('一行被插入'); else dbms_output.put_line('插入失败.');
end if;
end;
dd) 函数调用的注意事项;
1、 函数必须是PLSQL语句的组成部分,不能单独存在.
2、 函数可以在SQL语句中调用,但是不是所有函数都可以.
如果函数中有out参数,不能在SQL调用.
函数中有PLSQL种特有的数据类型,也不能再SQL中调用.
如果函数中有DML操作的时候可在SQL种调用函数,但是函数执行不成功.
1:
begin
  test_pro1; -- 过程可以单独运行.
  --test_fun1;  --函数必须是语句中的组成部分.
  dbms_output.put_line(test_fun1);
end;
2:
select sysdate from dual; --函数做为SQL语句的组成部分.
select test_fun1 from dual;
select test_fun1(1, 2, xxx ?) from dual; --因为函数中有out参数,所以不能调用.
select test_fun3(1234, 'abc') from dual; --函数返回boolean类型,但是SQL中没有boolean类型,所以调用失败.
select test_fun4(1232, 'ABC') from dual; --函数中有DML,则此处调用能得到返回值,但是函数执行不成功.

SELECT * from emp;
ee) 包PACKAGE;
  把函数,过程,自定义类型,游标,变量等等涵盖在包的内部.
  我们可以通过包来区分不同对象.
  除了可以解决重名的问题,还可以进行功能的划分.
xv. 包体的结构;
1、 包头
包头里面只有定义,没有实现(函数、过程)类似于java中的接口
2、 包体
对包头中的函数、过程去做实现的
xvi. 调用包里的函数、过程、类型、变量;
语法:
包名.过程|函数|类型|变量名
130. 创建包头;
create or replace package 包名字 
        is|as
        [变量的定义;]
        [类型的定义;]
        [游标的定义;]
        [异常的定义;]
        [函数的定义;]
        [过程的定义;]
end;
eg:创建包头的例子;
create or replace package emp_utils is
  num     int := 10;
  emp_row emp%rowtype;
  type emp_record is record(
    v_empno emp.empno%type);
  er emp_record;
  myExctpion exception;
  cursor emps is
    select * from emp;
  procedure mypro;
  function myfun return int;
end;
--上面这个包头中出了函数和过程不能调用外(因为没有实现,他们的实现要在包体里实现),其他都可以通过包头名来调用了.
131. 使用包中的内容;
begin
emp_utils.num := emp_utils.num + 1; dbms_output.put_line(emp_utils.num); --11
end;

begin
dbms_output.put_line(emp_utils.num); --11
end;

begin
select * into emp_utils.emp_row from emp where empno = 7369;
end;

declare er2 emp_utils.emp_record;
begin
select empno into emp_utils.er from emp where empno = 7369;
--raise emp_utils.myExctpion;
open emp_utils.emps; emp_utils.mypro; null;
end;
xvii. 创建包体的语法;
create or replace package body 包名字
is | as
对包头中所声明的函数, 过程做实现.
end;
创建包体的例子:
--创建包体的例子:
create or replace package body emp_utils is
  procedure mypro is
  begin
    null; --实现代码略...
  end;

  function myfun return int is
  begin
    null;
    return 1;
  end;
end;
注意:
1、包头和包体的名字要统一
2、包头中定义的变量和类型,只要是能访问到包的用户都可以访问到.并且作为全局变量或全局类型.
3、如果包头编译错误,包体也会错误.
4、可以只有包头,不要包体.但此时如果包头中定义了函数和过程,则不能去调用,因为没有在包体中实现.
ff) 包内部的函数,过程的重载
函数或过程名一样,但是参数不一样的就是重载
参数不一样是指:个数、参数类型族,顺序不一样
什么是类型族?
int number
char varchar varchar2
eg:
--包头
create or replace package overloade_pack is
  procedure print;

  procedure mypro;
  procedure mypro(id int); --重载了上面的过程
  --procedure mypro(id number);  --重载了上面的过程,参数属于同类型族,重载失败

  function myfun return int;
  --function myfun return number;  --函数名相同,参数相同,重载也失败
  --function myfun return char;    --函数名相同,参数相同,重载也失败
  function myfun(id int) return int;
  --function myfun(num1 out int) return int;
--函数的重载和 返回值,参数模式没有关系; 只和参数个数,顺序,类型族有关系.
end;

--包体
create or replace package body overloade_pack is
  procedure print --作用域在当前包体内部.
   is
  begin
    dbms_output.put_line('hello');
  end;

  procedure mypro is
  begin
    null;
    print; --使用上面的过程.
end;

  procedure mypro(id int) is
  begin
    null;
  end;

  procedure mypro(id number) is
  begin
    null;
  end;

  function myfun return int is
  begin
    return 1;
  end;
  /*
  function myfun return number
  is begin
     return 2;
  end; 
 
  function myfun return char
  is begin
     return 'a';
  end; 
  */
  function myfun(id int) return int is
  begin
    return id * 2;
  end;
  /*
  function myfun(id1 out int) return int
  is begin
     id1 := 5;
     return id1;
  end;
  */
end;

-- 调用
declare
  num number := 100;
begin
  overloade_pack.mypro;
  --overloade_pack.print;   
  overloade_pack.mypro(1);
  dbms_output.put_line(overloade_pack.myfun);
  dbms_output.put_line(overloade_pack.myfun(2));
  dbms_output.put_line(overloade_pack.myfun(num));
  dbms_output.put_line(num);
end;
gg) 触发器;
触发器也是有名的plsql块来的
也存储与数据库端的
过程和函数是要手动调用的
触发器不是手动调用的,而是被事件触发,系统来执行触发器的
触发器被用在当指定表中的数据发生了变化的时候会自动执行,用来隐式的书里数据的
xviii. 触发器的分类;
根据3个不同因素来分类:
1、 触发事件
insert: 当发生insert操作触发器
update: 当发生update操作触发器
delete:当发生delete操作触发器
2、 触发时间
before: 在触发事件前执行触发器
after:在触发事件后执行触发器
3、 触发级别
行触发级别:每一行数据被修改是都执行触发器
语句触发级别: 当insert、update、delete语句执行触发器
.
.
.
一共有12个触发器
xix. 触发器的创建语法;
create or replace trigger <触发器名字> [before|after]
    <触发事件 [or 触发事件...]> [of 字段名] on <表名>  --[of 字段名]是用在update事件中可以指定字段的
    [for each row]
    <PL/SQL程序体>
eg:周六日、工作时间之外不允许对emp表做DML操作
create or replace trigger dml_emp
  before insert or update or delete on emp
declare
begin
  dbms_output.put_line('dml...');
  if (to_char(sysdate, 'day') in ('星期六', '星期日')) or
     to_number(to_char(sysdate, 'hh24')) not between 8 and 18 then
    dbms_output.put_line('周六日或非工作时间不要操作emp表,请退出');
  end if;
  --rollback; --触发器中不能直接或者间接调用commit,rollback,savepoint语句.
end;

select * from emp; delete from emp where empno in (8888, 1234); rollback; insert into emp(empno) values(8888);
xx. *级联更新触发*(记住);
--级联更新触发
select * from emp, dept where emp.deptno = dept.deptno; select * from dept; update dept set deptno = 10 where deptno = 60;

create or replace trigger update_emp_deptno_trigger
  after update of deptno on dept
  for each row --
declare begin dbms_output.put_line('dml emp deptno'); update emp set deptno = :new.deptno --新值
where deptno =ld.deptno; --老值
end;
inserting updating deleting 来判定触发触发事件
create or replace trigger dml_emp
  before insert or update or delete on emp
  for each row
declare
begin
  if inserting then
    --插入操作
    dbms_output.put_line('insert');
  elsif updating then
    dbms_output.put_line('update');
  elsif deleting then
    dbms_output.put_line('delete');
  end if;
end;

rollback insert into emp(empno) values(8889); update emp set empno = 8890 where empno = 8889; delete from emp where empno >= 8888
练习,创建一张dml日志表, id日志单号,user_name用户名,time操作事件,
create table dml_log(id int primary key, user_name varchar(20) default user, time_flag varchar(20) default sysdate, dmltype varchar(20));

CREATE SEQUENCE DML_LOG_PK_V start with 1 increment by 1;

--DML
insert into emp(empno) values(8889); update emp set empno = 8890 where empno = 8889; delete from emp where empno >= 8888

select * from dml_log;
--TRIGGER      
create or replace trigger dml_emp
  before insert or update or delete on emp
--for each row
declare begin if inserting then --插入操作
insert into dml_log(id, dmltype) values(dml_log_pk_v.nextval, 'insert'); elsif updating then insert into dml_log(id, dmltype) values(dml_log_pk_v.nextval, 'update'); elsif deleting then insert into dml_log(id, dmltype) values(dml_log_pk_v.nextval, 'delete'); end if; end;

rollback
7、 详细介绍触发器(触发器的使用);
hh) 触发器;
触发器(trigger)是个特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作( insert,delete, update)时就会激活它执行。触发器经常用于加强数据的完整性约束和业务规则等。 触发器可以从 DBA_TRIGGERS ,USER_TRIGGERS 数据字典中查到。
xxi. 数据库领域名词;
  触发器可以查询其他表,而且可以包含复杂的 SQL 语句。它们主要用于强制服从复杂的业务规则或要求。例如:您可以根据客户当前的帐户状态,控制是否允许插入新订单。
  触发器也可用于强制引用完整性,以便在多个表中添加、更新或删除行时,保留在这些表之间所定义的关系。然而,强制引用完整性的最好方法是在相关表中定义主键和外键约束。如果使用数据库关系图,则可以在表之间创建关系以自动创建外键约束。
xxii. 创建触发器的SQL语法;
DELIMITER
  CREATE TRIGGER `<databaseName>`.`<triggerName>`
  < [ BEFORE | AFTER ] > < [ INSERT | UPDATE | DELETE ] >
  ON <tableName>
  FOR EACH ROW
  BEGIN
  --do something
END;
xxiii. 触发器的优点;
  触发器可通过数据库中的相关表实现级联更改;不过,通过级联引用完整性约束可以更有效地执行这些更改。触发器可以强制比用 CHECK 约束定义的约束更为复杂的约束。与 CHECK 约束不同,触发器可以引用其它表中的列。例如,触发器可以使用另一个表中的 SELECT 比较插入或更新的数据,以及执行其它操作,如修改数据或显示用户定义错误信息。触发器也可以评估数据修改前后的表状态,并根据其差异采取对策。一个表中的多个同类触发器(INSERT、UPDATE 或 DELETE)允许采取多个不同的对策以响应同一个修改语句。
xxiv. 比较触发器与约束;
  约束和触发器在特殊情况下各有优势。触发器的主要好处在于它们可以包含使用 Transact-SQL 代码的复杂处理逻辑。因此,触发器可以支持约束的所有功能;但它在所给出的功能上并不总是最好的方法。实体完整性总应在最低级别上通过索引进行强制,这些索引或是 PRIMARY KEY 和 UNIQUE 约束的一部分,或是在约束之外独立创建的。假设功能可以满足应用程序的功能需求,域完整性应通过 CHECK 约束进行强制,而引用完整性 (RI) 则应通过 FOREIGN KEY 约束进行强制。在约束所支持的功能无法满足应用程序的功能要求时,触发器就极为有用。
  例如:除非 REFERENCES 子句定义了级联引用操作,否则 FOREIGN KEY 约束只能以与另一列中的值完全匹配的值来验证列值。
  CHECK 约束只能根据逻辑表达式或同一表中的另一列来验证列值。如果应用程序要求根据另一个表中的列验证列值,则必须使用触发器。
  约束只能通过标准的系统错误信息传递错误信息。如果应用程序要求使用(或能从中获益)自定义信息和较为复杂的错误处理,则必须使用触发器。
  触发器可通过数据库中的相关表实现级联更改;不过,通过级联引用完整性约束可以更有效地执行这些更改。
  触发器可以禁止或回滚违反引用完整性的更改,从而取消所尝试的数据修改。当更改外键且新值与主键不匹配时,此类触发器就可能发生作用。例如,可以在 titleauthor.title_id 上创建一个插入触发器,使它在新值与 titles.title_id 中的某个值不匹配时回滚一个插入。不过,通常使用 FOREIGN KEY 来达到这个目的。
  如果触发器表上存在约束,则在 INSTEAD OF 触发器执行后但在 AFTER 触发器执行前检查这些约束。如果约束破坏,则回滚 INSTEAD OF 触发器操作并且不执行 AFTER 触发器。
  触发器到底可不可以在视图上创建 在 SQL Server™ 联机丛书中,是没有说触发器不能在视图上创建的, 并且在语法解释中表明:
  在 CREATE TRIGGER 的 ON 之后可以是视图。 然而,事实似乎并不是如此,很多专家也说触发器不能在视图上创建。我也专门作了测试,的确如此,不管是普通视图还是索引视图,都无法在上面创建触发器,真的是这样吗?请点击详细,但是无可厚非的是:当在临时表或系统表上创建触发器时会遭到拒绝。 深刻理解 FOR CREATE TRIGGER 语句的 FOR 关键字之后可以跟 INSERT、UPDATE、DELETE 中的一个或多个,也就是说在其它情况下是不会触发触发器的, 包括 SELECT、TRUNCATE、WRITETEXT、UPDATETEXT。相关内容 一个有趣的应用我们看到许多注册系统在注册后都不能更改用户名,但这多半是由应用程序决定的, 如果直接打开数据库表进行更改,同样可以更改其用户名, 在触发器中利用回滚就可以巧妙地实现无法更改用户名……详细内容 触发器内部语句出错时…… 这种情况下,前面对数据更改操作将会无效。举个例子,在表中插入数据时触发触发器,而触发器内部此时发生了运行时错误,那么将返回一个错误值,并且拒绝刚才的数据插入。不能在触发器中使用的语句 触发器中可以使用大多数 T-SQL 语句,但如下一些语句是不能在触发器中使用的。
  CREATE 语句,如:CREATE DATABASE、CREATE TABLE、CREATE INDEX 等。
  ALTER 语句,如:ALTER DATABASE、ALTER TABLE、ALTER INDEX 等。
  DROP 语句,如:DROP DATABASE、DROP TABLE、DROP INDEX 等。
  DISK 语句,如:DISK INIT、DISK RESIZE。
  LOAD 语句,如:LOAD DATABASE、LOAD LOG。
  RESTORE 语句,如:RESTORE DATABASE、RESTORE LOG。
  RECONFIGURE
TRUNCATE TABLE 语句在sybase的触发器中不可使用!
(本人:说的似乎不正确)
xxv. 慎用触发器;
触发器功能强大,轻松可靠地实现许多复杂的功能,为什么又要慎用呢。触发器本身没有过错,但由于我们的滥用会造成数据库及应用程序的维护困难。在数据库操作中,我们可以通过关系、触发器、存储过程、应用程序等来实现数据操作…… 同时规则、约束、缺省值也是保证数据完整性的重要保障。如果我们对触发器过分的依赖,势必影响数据库的结构,同时增加了维护的复杂程序。
xxvi. 触发器命名规则;
命名规则:对象名称_状态_DML命令_类型
对象名称:指表名或者视图名称;
状态:after(AFT)、before(BEF)、instead(INS);
DML命令:insert(INS)、update(UPD)、delete(DEL),
两个DML命令的写法(INS_UPD),
三具DML命令的写法IUD;
    类型:语句、行(ROW)。
ii) 触发器应用;
xxvii. 功能;
1、 允许/限制对表的修改
2、 自动生成派生列,比如自增字段
3、 强制数据一致性
4、 提供审计和日志记录
5、 防止无效的事务处理
6、 启用复杂的业务逻辑
xxviii. 触发器的组成部分;
1、 触发器名称
2、 触发语句
3、 触发器限制
4、 触发操作
132. 触发器名称;
create trigger biufer_employees_department_id
命名习惯:
biufer(before insert update for each row)
employees 表名
department_id 列名
133. 触发语句;
比如:
表或视图上的DML语句
DDL语句
数据库关闭或启动,startup shutdown 等等
before insert or update
  of department_id
  on employees
referencing old as old_value
     new as new_value
for each row
说明:
1、 无论是否规定了department_id ,对employees表进行insert的时候
2、 对employees表的department_id列进行update的时候
3、 触发器限制
when (new_value.department_id<>80 ),限制不是必须的。此例表示如果列department_id不等于80的时候,触发器就会执行。其中的new_value是代表跟新之后的值。
4、 触发操作
是触发器的主体
begin
:new_value.commission_pct :=0;
end;
主体很简单,就是将更新后的commission_pct列置为0
触发:
insert into employees
  (employee_id, last_name, first_name, hire_date, job_id, email, department_id, salary, commission_pct)
values
  (12345, ’Chen’, ’Donny’, sysdate, 12, ‘donny@hotmail.com’,  60,  10000, .25);

select commission_pct from employees where employee_id = 12345;
触发器不会通知用户,便改变了用户的输入值。
xxix. 触发器类型;
1、 语句触发器
2、 行触发器
3、 INSTEAD OF 触发器
4、 系统条件触发器
5、 用户事件触发器
134. 语句触发器;
是在表上或者某些情况下的视图上执行的特定语句或者语句组上的触发器。能够与INSERT、UPDATE、DELETE或者组合上进行关联。但是无论使用什么样的组合,各个语句触发器都只会针对指定语句激活一次。比如,无论update多少行,也只会调用一次update语句触发器。
安全检查;
需要对在表上进行DML操作的用户进行安全检查,看是否具有合适的特权。
1、创建数据表
Create table foo(a number);
2、创建触发器
Create Or Replace Trigger biud_foo
  Before insert or update or delete On foo
declare
  not_library_user exception;
Begin
  If user not in ('WHN') then
    RAISE not_library_user;
  End if;
EXCEPTION
  WHEN not_library_user THEN
    RAISE_APPLICATION_ERROR(-20001,
                            'You don t have access to modify this table.');
End;
3、测试
即使SYS,SYSTEM用户也不能修改foo表中的数据。
insert into whn.foo values (11,sysdate);

记录dml操作日志;
对修改表的时间、人物进行日志记录。
1、建立试验表
create table employees_copy as select *from hr.employees
2、建立日志表
-- Create table
create table EMPLOYEES_LOG
(
  WHO    VARCHAR2(30),
  WHEN   DATE,
  ACTION CHAR(1)
);
3、创建触发器
在employees_copy表上建立语句触发器,在触发器中填充employees_log 表。
Create Or Replace Trigger biud_employee_copy
  Before insert or update or delete On employees_copy
Declare
  L_action employees_log.action%type;
Begin
  if inserting then
    L_action := 'I';
  elsif updating then
    L_action := 'U';
  elsif deleting then
    L_action := 'D';
  else
    raise_application_error(-20001,
                            'You should never ever get this error.');
  end if;
  Insert into employees_log
    (Who, when, action)
  Values
    (user, sysdate, L_action);
End;
4、测试
update employees_copy set salary= salary*1.1;
5、查询
select *from employess_log;

135. 行触发器;
是指为受到影响的各个行激活的触发器,定义与语句触发器类似,有以下两个例外:
1、 定义语句中包含FOR EACH ROW子句
2、 在BEFORE……FOR EACH ROW触发器中,用户可以引用受到影响的行值。
比如:
定义:
create trigger biufer_employees_department_id
before insert or update
  of department_id
  on employees_copy
referencing old as old_value
     new as new_value
for each row
when (new_value.department_id<>80 )
begin
:new_value.commission_pct :=0;
end;
Referencing 子句:
执行DML语句之前的值的默认名称是ld ,之后的值是 :new
insert 操作只有:new
delete 操作只有ld
update 操作两者都有
referencing子句只是将new 和old重命名为new_value和old_value,目的是避免混淆。比如操作一个名为
new的表时。
作用不很大。
为主健生成自增序列号;
1、创建数据表
create table foo(id number, data varchar2(20));
2、创建序列
create sequence foo_seq;
3、创建触发器
create or replace trigger bifer_foo_id_pk
before insert on foo
for each row
begin
select foo_seq.nextval into :new.id from dual;
end;
4、插入数据测试
insert into foo(data) values(‘donny’);
insert into foo values(5,’Chen’);
5、查询
select * from foo;
备份数据(插入、修改、删除)
1、 创建测试表
create table employees_copy as select *from hr.employees;(上面已经创建)
2、 创建备份表
create table Employees_bak as select * from employees_copy where 1=0;
3、创建触发器
create or replace trigger employees_copy_bef_dui
  before insert or update or delete on employees_copy
  referencing old as old_value new as new_value
  for each row
--when (new_value.department_id <> 80)
begin
  if inserting then
    insert into employees_bak
    values
      (:new_value.EMPLOYEE_ID,
       :new_value.FIRST_NAME,
       :new_value.LAST_NAME,
       :new_value.EMAIL,
       :new_value.PHONE_NUMBER,
       :new_value.HIRE_DATE,
       :new_value.JOB_ID,
       :new_value.SALARY,
       :new_value.COMMISSION_PCT,
       :new_value.MANAGER_ID,
       :new_value.DEPARTMENT_ID);
  else
    insert into employees_bak
    values
      (:old_value.EMPLOYEE_ID,
      ld_value.FIRST_NAME,
      ld_value.LAST_NAME,
      ld_value.EMAIL,
      ld_value.PHONE_NUMBER,
      ld_value.HIRE_DATE,
      ld_value.JOB_ID,
      ld_value.SALARY,
      ld_value.COMMISSION_PCT,
      ld_value.MANAGER_ID,
      ld_value.DEPARTMENT_ID);
  end if;
end;
4、 测试
插入、删除、修改employees_copy表中的数据。
5、查看
select * from employees_bak t;
对数据操作的约束;
根据用户和数据表中某一个字段的值来限定修改和删除,只有指定的用户才可以修改和删除。
1、创建数据表
create table TRG_EMPLOYESS as select* from HR.EMPLOYEES;
2、创建触发器
当TRG_EMPLOYESS中HIRE_DATE大于1999-1-1或者SALARY大于10000时,数据不能被其它用户修改或删除,只有USER_NAME这个用户可以删除。
create or replace trigger TRG_EMPLOYESS_BEF_DU
  before update or delete on TRG_EMPLOYESS
  referencing old as old_value new as new_value
  for each row
  when (old_value.HIRE_DATE > to_date
  ('1990-1-1', 'yyyy-mm-dd') or old_value.SALARY > 10000)
begin
  if updating and (:old_value.SALARY > 10000 or
    ld_value.HIRE_DATE > to_date('1999-1-1', 'yyyy-mm-dd')) and
     user <> 'WHN' then
    RAISE_APPLICATION_ERROR(-20002, '你无权修改此数据,请联系管理员!');
  end if;
  if deleting and (:old_value.SALARY > 10000 or
    ld_value.HIRE_DATE > to_date('1999-1-1', 'yyyy-mm-dd')) and
     user <> 'WHN' then
    RAISE_APPLICATION_ERROR(-20001, '你无权删除此数据,请联系管理员!');
  end if;
end;
3、测试
delete from whn.trg_employess t where t.employee_id = 199;
update whn.trg_employess t
   set t.last_name = t.last_name || '11'
where t.employee_id = 199;

delete from whn.trg_employess t where t.employee_id = 205;
update whn.trg_employess t
   set t.last_name = t.last_name || '11'
where t.employee_id = 205;
136. INSTEAD OF 触发器更新视图
1、创建视图
Create or replace view vw_trg_employess as
Select first_name || ', ' || last_name name,
       email,
       phone_number,
       employee_id emp_id
  From trg_employess;
2、尝试更新email和name
--name无法修改,提示“ORA-01733:此处不请允许虚拟列”
update vw_trg_employess
set name='Chen1, Donny1',email='163'
where emp_id=202;
--email可以修改
update vw_trg_employess
set email='163'
where emp_id=202
3、创建触发器
create or replace trigger VW_TRG_EMPLOYESS_INS_UPD
  INSTEAD OF Update on VW_TRG_EMPLOYESS
Begin
  Update TRG_EMPLOYESS
     Set EMPLOYEE_ID  = :new.EMP_ID,
         FIRST_NAME   = substr(:new.NAME, instr(:new.NAME, ',') + 2),
         LAST_NAME    = substr(:new.NAME, 1, instr(:new.NAME, ',') - 1),
         PHONE_NUMBER = :new.PHONE_NUMBER,
         EMAIL        = :new.EMAIL --通过测试,此列必须得写,要不然无法更新此值,即使更新语句中已经写了更新此列
   Where EMPLOYEE_ID =ld.EMP_ID;
end;
4、测试
update vw_trg_employess
set name='Chen1, Donny1',email='163'
where emp_id=202;

137. 模式触发器;
用户事件:CREATE / ALTER / DROP / ANALYZE / AUDIT / GRANT / REVOKE / RENAME / TRUNCATE / LOGOFF
1、创建日志表;
-- Create table
create table TRG_DDL_LOG
(
  OBJECT_OWNER    VARCHAR2(30),
  OBJECT_NAME     VARCHAR2(30),
  OBJECT_TYPE     VARCHAR2(30),
  ALTER_BY_USER   VARCHAR2(30),
  SYS_EVENT       VARCHAR2(30),
  ALTERATION_TIME DATE
);
2、创建触发器;
--DDL所有操作日志
create or replace trigger trg_ddl_database
  after ddl on database
BEGIN
  if sys.sysevent in ('CREATE', 'ALTER', 'DROP', 'ANALYZE', 'AUDIT', 'GRANT',
      'REVOKE', 'RENAME', 'TRUNCATE') then
    INSERT INTO trg_ddl_log
    VALUES
      (sys.dictionary_obj_owner,
       sys.dictionary_obj_name,
       sys.dictionary_obj_type,
       sys.login_user,
       sys.sysevent,
       sysdate);
  end if;
END;
某个用户的删除操作;
create or replace trigger trg_ddl_username_drop
  before DROP on username.schema
BEGIN
  INSERT INTO trg_ddl_log
  VALUES
    (sys.dictionary_obj_owner,
     sys.dictionary_obj_name,
     sys.dictionary_obj_type,
     sys.login_user,
     sys.sysevent,
     sysdate);
END;
3、测试;
create table drop_me(a number);
create view drop_me_view as select *from drop_me;
drop view drop_me_view;
drop table drop_me;
4、查询;
select * from trg_ddl_log t;
138. 数据库触发器;
    可以创建在数据库事件上的触发器,包括错误、登录、注销、关闭和启动。
数据库启动、关闭;
1、 创建日志表
-- Create table
create table TRG_SYSTEM_LOG
(
  INSTANCE_NUM  NUMBER,
  DATABASE_NAME VARCHAR2(50),
  LOGIN_USER    VARCHAR2(30),
  SYSEVENT      VARCHAR2(20),
  TIME          DATE
)
tablespace TEST_DATA
  pctfree 10
  initrans 1
  maxtrans 255
  storage
  (
    initial 64K
    minextents 1
    maxextents unlimited
  );
-- Add comments to the columns
comment on column TRG_SYSTEM_LOG.INSTANCE_NUM
  is '实例名';
comment on column TRG_SYSTEM_LOG.DATABASE_NAME
  is '数据库名';
comment on column TRG_SYSTEM_LOG.LOGIN_USER
  is '登录用户名';
comment on column TRG_SYSTEM_LOG.SYSEVENT
  is '系统事件';
comment on column TRG_SYSTEM_LOG.TIME
  is '操作时间';;
2、 创建触发器;
--启动日志
create or replace trigger trg_system_startup
after startup
  on database
begin
  insert into trg_system_log
  values
    (ora_instance_num,
     ora_database_name,
     ora_login_user,
     ora_sysevent,
     sysdate);
end;
关闭日志;
create or replace trigger trg_system_shutdown
  before shutdown on database
begin
  insert into trg_system_log
  values
    (ora_instance_num,
     ora_database_name,
     ora_login_user,
     ora_sysevent,
     sysdate);
end;
3、 查询日志信息;
select * from trg_system_log t
数据库连接触发器;
1、 创建日志表
-- Create table
create table TRG_LOGONOFF_LOG
(
  USERNAME VARCHAR2(20),
  LOG_TIME DATE,
  ONOFF    VARCHAR2(6),
  ADDRESS  VARCHAR2(30)
);
2、创建触发器
记录用户TEST的连接日志信息。
--登录日志
create or replace trigger trg_logon
  after logon on database
begin
  if ora_login_user <> 'TEST' then
    insert into trg_logonoff_log
    values
      (ora_login_user, sysdate, 'logon', ora_client_ip_address);
  end if;
end;
退出日志
create or replace trigger trg_logoff
  before logoff on database
begin
  if ora_login_user <> 'TEST' then
    insert into trg_logonoff_log
    values
      (ora_login_user, sysdate, 'logoff', ora_client_ip_address);
  end if;
end;
3、查询日志
select * from trg_logonoff_log t
xxx. 禁用和启用触发器
方法一:
要使用此命令,必须拥有表或者拥有ALTER ANY TRIGGER 系统权限。
alter trigger <trigger_name> disable;
alter trigger <trigger_name> enable;
方法二:
    使用alter table命令,必须拥有有或者拥有ALTER ANY TABLE系统权限。
Alter table <table_name> disable all triggers;
Alter table <table_name> enable all triggers;

事务处理:
在触发器中,不能使用commit / rollback
因为ddl语句具有隐式的commit,所以也不允许使用
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics