`
yanbochen
  • 浏览: 6686 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

oracle学习笔记

阅读更多
连接命令
1)conn [ect]
用法:conn 用户名/密码@网络服务名 [as sysdba/ sysoper]
当用特权用户身份连接时,必须带上 as sysdba 或是 as sysoper
2)disc [onnect]
说明:该命令用来断开与当前数据库的连接
3)passw [ord]
说明:该命令用于修改用户的密码。如果要想修改其它用户的密码,需要用sys/system登录。
show user
说明:显示当前用户名
exit
说明:该命令会断开与数据库的连接,同时会退出sqlplus

文件操作命令
start和@
说明:运行sql脚本
例子:sql>  @ d:\a.sql 或者 sql> start d:\a.sql
&
说明:可以替代变量,而该变量在执行时,需要用户输入
sql>  select * from emp where job = '&job'
edit
说明:该命令可以编辑指定的sql脚本
例子:sql>  edit d:\a.sql
spool
说明:该命令可以将sqlplus屏幕上的内容输出到指定文件中去
例子:sql>  spool d:\b.sql 并输入 sql>  spool off

显示和设置环境变量
概述:可以用来控制输出的各种格式, set show 如果希望永久的保存相关的设置,可以去修改glogin.sql脚本
linesize
说明:设置显示行的宽度,默认是80个字符
sql>  show linesize
sql>  set linesize 90
pagesize
说明:设置每页显示的行数目,默认是14
用法和linesize一样
至于其它环境参数的使用也是大同小异

Oracle用户的管理
创建用户
概述:在oracle中要创建一个新的用户使用create user 语句,一般是具有dba(数据库管理员)的权限才能使用。
sql>  create user 用户名identified by 密码;

给用户修改密码
概述:如果要给自己修改密码可以直接使用
sql>  password 用户名
如果给别人修改密码则需要具有dba的权限,或是拥有alter user 的系统权限
sql>  alter user 用户名 identified by 新密码;

删除用户
概述:一般以dba的身份去删除某个用户,如果用其它的用户去删除用户则需要具有drop user的权限。比如 drop user 用户名 【cascade】

用户管理的综合案例
概述:创建的新用户是没有任何权限的,甚至连登录数据库的权限都没有,需要为其指定相应的权限。给一个用户赋权使用命令grant,回收权限使用命令 revoke。

希望xiaoming 用户可以去查询scott的emp表
sql>  grant select on emp to xiaoming; 
希望xiaoming 用户可以去修改/删除、查询、添加 scott 的 emp 表
sql>  grant all on emp to xiaoming;
Scott希望回收 xiaoming 对emp表的查询权限
sql>  revoke select on emp from xiaoming;

// 对权限的维护
希望对xiaoming 用户可以去查询scott的emp表,还希望xiaoming可以把这个权限继续交给别人 grant select on emp to xiaoming with grant option;
--如果是对象权限,就加入with grant option
--如果是系统权限.system给xiaoming 权限时:
sql>  grant connect to xiaoming with admin option;

如果scott把xiaoming对emp表的查询权限回收,那么xiaohong的权限会怎么样? 
被回收(oracle采用的是级联回收的机制来管理权限)



Oracle权限分类
系统权限:用户对数据库的相关权限。如登录、删除、创建。
对象权限:用户对其它用户的数据对象操作的权限。(select、insert、update、delete、all、create index ...)
数据对象:比如表,视图,过程等等。。。

Oracle角色分类
自定义角色
预定义角色

使用profile管理用户口令
概述:profile是口令限制,资源限制的命令集合,当建立数据库时,oracle会自动建立名称为default的profile。当建立用户没有指定profile选项,那oracle就会将default分配给用户。
账户锁定
概述:指定该账户(用户)登录时最多可以输入密码的次数,也可以指定用户锁定的时间(天)一般用dba的身份去执行该命令
例子:指定scott这个用户最多只能尝试3次登录,锁定时间为2天
sql>  create profile lock_account limit failed_login_attempts  3  password_loack_time 2;
sql>  alter user scott profile lock_account;

给账户(用户)解锁
sql>  alter user scott account unlock;

终止口令
为了让用户定期修改密码可以使用终止口令的指令来完成,同样这个命令也需要dba身份来操作。
例子:给用户scott创建一个profile文件,要求该用户每隔10天要修改自家的登录密码,宽限期为2天。
sql>  create profile myprofile limit password_life_time 10  password_grace_time  2;
Sql>  alter user scott profile myprofile;

口令历史
概述:如果希望用户在修改密码时,不能使用以前使用过的密码,可以使用口令历史,这样oracle就会将口令修改的信息存放到数据字典中,这样当用户修改密码时,oracle就会对新旧密码进行比较,当发现新旧密码一样时,就提示用户重新输入密码。
例子:
建立profile
sql>   create profile password_history limit
password_life_time 10 password_grace_time 2 password_reuse_time 10;
password_reuse_time //指定口令可重用时间即10天后就可以重用
分配给某个用户。

删除profile
概述:当不需要某个profile文件时,可以删除该文件。
sql>  drop profile passwod_history 【cascade】



Oracle表的管理

表名和列的命名规则
必须以字母开头
长度不能超过30字符
不能使用oracle的保留字
只能使用如下字符 A-Z,a-z,0-9,$,#等

Oralce 支持的数据类型
字符型(字符串)
char 定长 最大2000个字符 效率高
例子: char(10)  '小韩'  前四个字符放'小韩',后面添加6个空格补全 数据库中实际存放的为 '小韩      '

varchar2(20) 变长 最大4000个字符 例子: varchar2(10)  '小寒' oracle分配四个字符  这样可以节省空间

clob(character large object ) 字符型大对象 最大4G

数字型
number 范围 -10的38次方ˉ10的38次方
可以表示整数,也可以表示小数。

number(5, 2) 表示一个小数一共有5位有效数,2位小数 范围-999.99ˉ999.99
number(5) 表示一个五位整数 范围-99999ˉ99999

日期类型
date 包含年月日和时分秒
timestamp 这是oracle9i对date数据类型的扩展

图片类型
blob 二进制数据 可以存放图片/声音 4G


Oracle 建表
--学生表
sql>  create table student ( -- 表名
xh number(4),   -- 学号
xm varchar2(20), -- 姓名
sex char(2),    -- 性别
birthday date,   -- 出生日期
sal number(7, 2) -- 奖学金
);

--班级表
sql>  create table class (
classId number(2),
cname varchar2(20));
-修改表
添加一个字段
sql>  alter table student add (classId number(2));
修改字段的长度
sql>  alter table student modify (xm varchar2(30));
修改字段的类型/或是名字(不能有数据)
sql>  alter table student modify (xm char(30));
删除一个字段
sql>  alter table student drop column sal;
修改表的名字
sql>  rename student to stu;
删除表
sql>  drop table student;

查看表结构命令  sql>  desc 表名

添加数据
所有字段都插入
sql>  insert into student values ('A001', '张三', '男', '01-5月-05' , 10);
Oracle中默认的日期格式'DD-MON-YY' dd 日(天) mon 月份 yy 2位的年 '09-6月-99' 表示1999年6月9号

修改日期的默认格式
sql>  alter session set nls_date_format = 'yyyy-mm-dd' ;
修改后,可以用我们书序的格式添加日期类型:
sql>  insert into student values ('A002', 'MIKE', '男', '1905-05-06', 10);

插入部分字段
sql>  insert into student(xh, xm, sex) values ('A003', 'JOHN', '女');
插入空值
sql>  insert into student(xh, xm, sex, birthday) values ('A004', 'MARTIN', '男', null);

查询birthday为空的语句
sql>  select * from student where birthday is null;

修改一个字段
sql>  update student set sex= '女' where xh = 'A001';

修改多个字段
sql>  update student set sex= '男', birthday = '1980-04-01' where xh = 'A001';

修改含有null 值的数据
is null , is not null

删除数据
sql>  delete from student; 删除所有记录,表结构还在,写日志,数据可以恢复,速度慢
设置保存点 sql>  savepoint 保存点名称; 恢复保存点 sql>   rollback to 保存点名称 ; 恢复删除的数据
sql>  drop table student;  删除表的结构和数据
sql>  delete from student where xh = 'A001'; 删除一条记录
sql>  truncate table student; 删除表中的所有记录,表结构还在,不写日志,无法找回删除的记录(数据不可恢复),速度快

表基本查询

emp表

dept表



salgrade表


查看表结构
sql>  desc dept;
查看所有列
sql>  select * from dept;
查看指定列
sql>  select ename, sal, job, deptno from emp;
如何取消重复行
sql>  select distinct deptno, job from emp;

使用算数表达式
?显示每个雇员的年工资
sql>  select ename "姓名", sal * 12 + nvl(comm,0) * 12 as "年工资", comm as "奖金" from emp;

使用列的别名
sql>  select ename "姓名", sal * 12 as "年收入" from emp;

如何处理null值
使用nvl()函数来处理

如何连接字符串(||)
sql>  select ename || 'is a' || job from emp;

使用where子句
?如何显示工资高于3000的员工 sql>  select ename, sal from emp where sal >= 3000;

?如何查找1982.1.1 后入职的员工
sql>  select ename, hiredate from emp where hiredate > '1-1月-82';
?如何显示工资在2000 到 5000 之间的员工情况
sql>  select ename, sal from emp where sal >= 2000 and sal <= 5000;

如何使用like操作符
%::表示任意0到多个字符 _:表示任意单个字符
?如何显示首字符为S的员工姓名和工资
sql>  select ename, sal from emp where ename like 'S%';
?如何显示第三个字符为大写O的所有员工的姓名和工资
sql>  select ename, sal from emp where ename like '__O%';

在where条件中使用in
?如何显示empno为123,345,800...的雇员情况
sql>  select * from emp where empno in(123, 345, 800);

使用is null 的操作符
?如何显示没有上级的雇员的情况
sql>  select * from emp where mgr is null;

使用逻辑操作符号
?查询工资高于500或是岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为大写的J
sql>  select ename, sal from emp where (sal >= 500 or job = 'MANAGER') and ename like 'J%';
使用order by 字句
?如何按照工资的从低到高的顺序显示雇员的信息
sql>  select * from emp order by sal;

?如何按照工资的从高到低的顺序显示雇员的信息
sql>  select * from emp order by sal desc;

?按照部门号升序而雇员的工资降序排列
sql>  select * from emp order by deptno asc(可写可不写), sal desc;

使用列的别名排序
sql>  select ename, (sal + nvl(comm,0)) * 12 "年薪" from emp order by "年薪";  别名需要使用“”号圈中




复杂查询
数组分组(聚合函数)-max, min, avg, sum, count

?如何显示所有员工中最高工资和最低工资
sql>  select ename, sal from emp where sal = (select max(sal) from emp);
sql>  select ename, sal from emp where sal = (select min(sal) from emp);

?显示所有员工的平均工资和工资总和
sql>  select avg(sal) from emp;
sql>  select sum(sal) from emp;

?计算共有多少员工
sql>  select count(*) from emp;

扩展要求:
?请显示工资最高的员工的名字,工作岗位
sql>  select ename, job from emp where sal= (select max(sal) from emp);

?请显示工资高于平均工资的员工信息
sql>  select ename, sal from emp where sal >= (select avg(sal) from emp);

group by 和 having 子句
group by用于对查询的结果分组统计
having子句限制分组显示结果

?显示每个部门的平均工资和最高工资
sql>  select avg(sal), max(sal), deptno from emp group by deptno;

?显示每个部门的每种岗位的平均工资和最低工资
sql>  select avg(sal), min(sal), deptno, job from emp group by deptno, job;

?显示平均工资低于2000的部门号和它的平均工资
sql>  select avg(sal), deptno from emp group by deptno having avg(sal) < 2000;

对数据分组的总结
分组函数只能出现在选择列表、having、order by 子句中
如果在select语句中同时包含有group by,having,order by 那么它们的顺序是 group by, having,order by
在选择列中如果有列、表达式、和分组函数,那么这些列和表达式必须有一个出现在group by子句中,否则就会出错

如sql>  select deptno, avg(sal), max(sal) from emp group by deptno having avg(sal) < 2000; 这里deptno就一定要出现在group by中


多表查询
说明:多表查询是指基于两个和两个以上的表或是视图的查询。在实际应用中,查询单个表可能不能满足你的需求,(如显示sales部门位置和其员工的姓名),这种情况下需要使用到多表查询。(dept表和emp表)

?显示雇员名,雇员工资及所在部门的名字
方法1)sql>  select emp.ename, emp.sal, dept.dname from emp, dept where emp.deptno = dept.deptno;

方法2)sql>select ename, sal, dname from emp inner join dept on emp.deptno = dept.deptno;

笛卡尔集规定:多表查询的条件是 至少不能少于  表的个数 - 1

?显示部门号为10的部门名,员工名和工资
方法1)sql>  select dept.dname, emp.ename, emp.sal from dept, emp where dept.deptno = emp.deptno and dept.deptno = 10;

方法2)sql>  select dept.dname, emp.ename, emp.sal from dept inner join emp on dept.deptno = emp.deptno and dept.deptno = 10;

?显示各个员工的姓名,工资,及其工资的级别
sql>  select emp.ename, emp.sal, salgrade.grade from emp, salgrade where emp.sal between salgrade.losal and salgrade.hisal;

扩展要求:
?显示雇员名,雇员工资及所在部门的名字,并按部门编号排序
方法1)sql>  select emp.ename, emp.sal, dept.dname from emp, dept where emp.deptno = dept.deptno order by dept.deptno;

方法2)sql>  select emp.ename, emp.sal, dept.dname from emp inner join dept on emp.deptno = dept.deptno order by dept.deptno;

自连接
自连接是指在同一张表的连接查询
?显示某个员工的上级领导的姓名 比如显示'FORD'的上级
sql>  select worker.ename, boss.ename from emp worker, emp boss where worker.mgr = boss.empno and worker.ename = 'FORD';



子查询

什么是子查询
子查询是指嵌入在其它sql语句中的select语句,也叫嵌套查询

单行子查询
单行子查询是指只返回一行数据的子查询语句
?显示与SMITH同一部门的所有员工
sql>  select * from emp where deptno = (select deptno from emp where ename = 'SMITH');

多行子查询
多行子查询指返回多行数据的子查询
?查询和部门10的工作相同的雇员的名字、岗位、工资、部门号
sql>  select * from emp where job in(select distinct job from emp where deptno = 10);

在多行子查询中使用all操作符
?显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号
方法1)sql>  select ename, sal, deptno from emp where sal > all(select sal from emp where deptno = 30);

方法2)sql>  select ename, sal, deptno from emp where sal > (select max(sal) from emp where deptno = 30);

在多行子查询中使用any操作符
?显示工资比部门30的任意一个员工的工资高的员工的姓名、工资和部门号
方法1)sql>  select ename, sal, deptno from emp where sal > any (select sal from emp where deptno = 30);

方法2)sql>  select ename, sal, deptno from emp where sal > (select min(sal) from emp where deptno = 30);

多列子查询
单行子查询是指子查询只返回单列、单行数据,多行子查询是指返回单列多行数据,都是针对单列而言的,而多列子查询则是指查询返回多个列数据的子查询语句

?查询SMITH的部门和岗位完全相同的所有雇员
sql>  select * from emp where (deptno, job) = (select deptno, job from emp where ename = 'SMITH');

在from子句中使用子查询
?显示高于自己部门平均工资的员工的信息
sql>  select a2.ename, a2.sal, a2.deptno, a1.mysal from emp a2, (select deptno, avg(sal) mysal from emp group by deptno) a1 where a2.deptno = a1.deptno and a2.sal > a1.mysal;

在from子句中使用子查询
这里需要说明的是当在from子句中使用子查询时,该子查询会被作为一个视图来对待,因此叫做内嵌视图,当在from子句中使用子查询时,必须给子查询指定别名。(给表取别名时不能加 as )



分页查询

Oracle分页一共有三种方式
1)根据ROWID来分sql>  select * form t_xiaoxi where rowid in (select rid from (select rownum rn, rid from (select rowid rid , cid from t_xiaoxi order by cid desc ) where rownum < 10000) where rn > 9980) order by cid desc;   ---效率最高  执行时间0.03秒

2)按分析函数来分
sql>  select * from (select t.*, rownum rn from (select * from t_xiaoxi order by cid desc) t where rownum < 10000) where rn > 9980; 
---效率最差 执行时间1.01秒

3)按ROWNUM来分
ROWNUM分页步骤1 )rownum 分页sql>  (select * from emp);2 )显示rownum[oracle分配的]sql>  select a1.*, rownum rn from (select * from emp) a1;3)假如选择rn为6-10的话就先选择 < 10的,然后再选择 > 6的(oracle中的rownum只能用一次而不能像rownum > 10 and rownum < 6 这么用)sql>  select a1.* from rownum rn from (select * from emp) a1 where rownum <= 10;    -----先选择 < 10的列4)再选择 > 6的列sql>  select * from (select a1.*, rownum rn (select * from emp) a1 where rownum <= 10) where rn >= 6;    ----rownum格式只能这样表示不能有and注意里面的用rownum < 10 外面的用 rn > 6 如果想改变现实的字段只需要改变最内层的select就行了
---效率适中 执行时间0.1秒

其中t_xiaoxi为表名称,cid 为表的关键字段,取按CID降序排序后的第9981-9999条记录,t_xiaoxi表有70000多条记录

几个查询变化
a.指定查询列,只需修改最里层的子查询
?如只显示雇员的姓名和雇员的薪水显示第6条到第10条数据
sql>  select * from (select a1.*, rownum rn from (select ename, sal from emp) a1 where rownum <= 10) where rn >= 6;

b.如何排序,也只需要修改最里层的子查询
?按雇员的工资从低到高排序显示第6条到第10条数据
sql>  select * from (select a1.*, rownum rn from (select ename, sal from emp order by sal) a1 where rownum <= 10) where rn >= 6;

?查询4到9之间的数据
sql>  select * from (select a1.*, rownum rn from (select * from emp) a1 where rownum <= 9) where rn >= 4;

用查询结果创建新表
这个命令是一种快捷的建表方法
sql>  create table mytable (id, name, sal, job, deptno) as select empno, ename, sal, job, deptno from emp;




合并查询

有时在实际应用中,为了合并多个select语句的结果,可以使用集合操作符号  union, union all, intersect, minus

union
该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中重复行。
sql>  select ename, sal, job from emp where sal > 2500 union select ename, sal, job from emp where job = 'MANAGER';

union all
该操作符与union相似,但是它不会取消重复行,而且不会排序。
sql>  select ename, sal, job from emp where sal > 2500 union all select ename, sal, job from emp where job = 'MANAGER';

intersect
使用该操作符用于取得两个结果集的交集。
sql>  select ename, sal, job from emp where sal > 2500 intersect  select ename, sal, job from emp where job = 'MANAGER';

minus
使用该操作符用于取得两个结果集的差集,它只会显示存在第一个集合中,而不存在第二个集合中的数据。
sql>  select ename, sal, job from emp where sal > 2500 minus select ename, sal, job from emp where job = 'MANAGER';


Oracle创建新的数据库

创建数据库有两种方法:
通过oracle提供的向导工具 Database Configuration Assistant (数据库配置助手)
可以用手工步骤直接创建


使用特定格式插入日期值

使用to_date函数
sql>  insert into emp values(7935, 'xiaobai', 'MANAGER', 7782, to_date('1987-1-1','yyyy-mm-dd'), 1000, 0, 10);


使用子查询插入数据
当使用values子句时,一次只能插入一行数据,当使用子查询插入数据时,一条insert语句可以插入大量的数据。当处理行迁移或者装载外部表的数据到数据库时,可以使用子查询来插入数据。

?将emp表中部门号为10的数据插入到emp2表中
sql>  insert into emp2(myId, myName, Mydeptno) select empno, ename, deptno from emp where deptno = '10';

使用子查询更新数据
使用update语句更新数据时,既可以使用表达式或者数值直接修改数据,也可以使用子查询修改数据。

?希望员工SCOTT的岗位、工资、补助与SMITH员工一样
sql>  update emp set (job, sal, comm) = (select job, sal, comm from emp where ename = 'SMITH') where ename = 'SCOTT';




Oracle中事务处理

什么是事务
使用用于保持数据的一致性,它由一组相关的dml语句组成,该组的dml语句要么全部执行,要么全不执行。


事务和锁
当执行事务操作时(dml语句),oracle会在被作用的表上加锁,防止其它用户更改表的结构。

提交事务
当使用commit语句可以提交事务,当执行了commit语句后,会确认事务的变化、结束事务、删除保存点、释放锁,当使用commit语句结束事务后,其它会话将可以查看到事务变化后的新数据。

回退事务
在介绍回退事务前,我们先介绍一下保存点(savepoint)的概念和作用。保存点是事务中的一点。用于取消部分事务,当结束事务时,会自动的删除该事务所定义的所有保存点。当执行rollback时,通过指定保存点可以回退到指定的点。

事务的几个重要操作
设置保存点 savepoint a
取消部分事务 rollback to a
取消全部事务 rollback

只读事务
只读事务是指只允许执行查询的操作,而不允许执行任何其它dml操作的事务,使用只读事务可以确保用户只能取得某时间点的数据。在设置了只读事务后,尽管其它会话可能会提交新的事务,但是只读事务将不会取得最新数据的变化,从而可以保证取得特定时间点的数据信息。

设置只读事务
sql>  set transaction read only


sql函数的使用

字符函数
介绍:字符函数是oracle中最常用的函数
lower(char):将字符串转换为小写的格式
upper(char):将字符串转换为大写的格式
length(char):返回字符串的长度
substr(char, m, n):取字符串的子串
replace(char1, search_string, replace_string):替换字符串
instr(char1, char2, [,n[,m]]):取子串在字符串的位置

?将所有员工的名字按小写的方式显示
sql>  select lower(ename) from emp;

?将所有员工的名字按大写的方式显示
sql>  select upper(ename) from emp;

?显示正好为5个字符的员工的姓名
sql>  select * from emp where length(ename) = 5;

?显示所有员工姓名的前三个字符
sql>  select substr(ename, 1, 3) from emp;

?以首字母大写的方式显示所有员工的姓名
sql>  select upper(substr(ename, 1, 1)) ||  lower(substr(ename, 2, length(ename) - 1)) from emp;

?以首字母小写的方式显示所有员工的姓名
sql>  select lower(substr(ename, 1, 1)) ||  upper(substr(ename, 2, length(ename) - 1)) from emp;

?显示所有员工的姓名,用 "我是A" 替换所有"A"
sql>  select replace(ename, 'A', '我是A') from emp;

数学函数
数学函数的输入参数和返回值的类型数据都是数字类型的。数学函数包括cos,cosh,exp,ln,log,sin,sinh,sqrt,tan,tanh,acos,asin,atan,round
常用数学函数:
round(n, [m]):该函数用于执行四舍五入,如果省掉m,则四舍五入到整数;如果m是正数,则四舍五入到小数点的m位后。如果m是负数,则四舍五入到小数点的m位前
trunc(n, [m]):该函数用于截取数字。如果m,就截去小数部分,如果m是正数就截取到小数点的m位后,如果m是负数,则截取到小数点的前m位
mod(m, n):取模(取余数)
floor(n):返回小于或是等于n的最大整数
ceil(n):返回大于或是等于n的最小整数

对数字的处理,在财务系统或银行系统中用的最多,不同的处理方法,对财务报表有不同的结果。

?显示在一个月为30天的情况所有员工的日薪金,忽略余数
方法1)sql>  select ename, trunc(sal / 30) from emp;
方法2)sql>  select ename, floor(sal / 30) from emp;

其它数学函数:
abs(n):返回数字n的绝对值
acos(n):返回数字的反余弦值
asin(n):返回数字的正余弦值
atan(n):返回数字的反正切
cos(n)
exp(n):返回e的n次幂
log(m, n):返回对数值
power(m, n):返回m的n次幂

日期函数
日期函数用于处理date类型的数据
默认情况下日期格式是dd-mm-yy 即12-7月-78
sysdate:该函数返回系统时间
add_months(d, n): 该函数用于添加月份
last_day(d):返回指定日期所在月份的最后一天

?查找已经入职8个月多的员工
sql>  select ename from emp where sysdate > add_months(hiredate,;

?显示满10年服务年限的员工的姓名和受雇日期
sql>  select ename, hiredate from emp where sysdate
>= add_months(hiredate, 12 * 10);

?对于每个员工,显示其加入公司的天数
sql>  select ename, trunc(sysdate - hiredate) "入职天数" from emp;

?找出各月倒数第3天受雇的所有员工
sql>  select ename, hiredate from emp where last_day(hiredate) - 2 = hiredate;

转换函数
转换函数用于将数据类型从一种转换为另外一种。在某些情况下,oracle server允许值的数据类型和实际的不一样,这时oracle server会隐含的转换数据类型

比如:
ceate table t1(id int);
insert into t1 values('10'); -->这样oracle会自动的将'10' -> 10

ceate table t2(id varchar2(10));
insert into t2 values(1'); -->这样oracle会自动的将'1 -> '1';
尽管oracle可以进行隐含的数据类型的转换,但是它并不适应所有的情况,为了提高程序的可靠性,我们应该使用转换函数进行转换。

to_char

?日期是否可以显示 时/分/秒
sql>  select ename, to_char(hiredate, 'yyyy-mm-dd hh24:mi:ss') from emp;

?薪水是否可以显示指定的货币符号
指定为$元 sql>  select ename, to_char(sal, '$99,999.99') from emp;


显示本地的货币符号 sql>  select ename, to_char(sal, 'L99,999.99') from emp;

yy:两位数字的年份 2004 --> 04 9:显示数字,并忽略前面0
yyyy:四位数字的年份 2004年 0:显示数字,如位数不足,则用0补齐
mm:两位数字的月份 8月 --> 08 .:在指定位置显示小数点
dd:2位数字的天 30号--> 30 ,:在指定位置显示逗号
hh24:8点 --> 20 $:在数字前加美元
hh12:8点--> 08 L:在数字前加本地货币符号
mi、ss --> 显示分钟\秒 C:在数字前加国际货币符号
G:在指定位置显示组分隔符
D:在指定位置显示小数点符号(.)


?显示1980年入职的所有员工
sql>  select * from emp where to_char(hiredate, 'yyyy') = 1980;

?显示所有12月份入职的员工
sql>  select * from emp where to_char(hiredate, 'mm') = 12;


to_date
用于将字符串转换成date类型的数据

?按照中国人习惯的方式 年-月-日添加日期
sql>  insert into emp values(1234, 'xiaobai', 'clear', 8888, to_date('1988-8-8', 'yyyy-mm-dd'), 100, 0, 20);



系统函数
sys_context
terminal:当前会话客户所对应的终端的标识符
language:语言
db_name:当前数据库名称
nls_date_format:当前会话客户所对应的日期格式
session_user:当前会话客户所对应的数据库用户名
current_schema:当前会话客户所对应的默认方案名
host:返回数据库所在主机的名称

通过该函数,可以查询一些重要信息,比如在使用哪个数据库
sql>  select sys_context('userenv', 'db_name') from dual;


数据库管理

管理数据库的用户主要是sys和system
主要区别:
最重要的区别,存储的数据的重要性不同
sys:所有oracle的数据字典的基表和视图都存放在sys用户中,这些基表和视图对于oracle的运行是至关重要的,由数据库自己维护,任何用户都不能手动更改。sys用户拥有dba,sysdba,sysoper角色或权限,是oracle权限最高的用户。
system:用于存放次一级的内部数据,如oracle的一些特性或工具的管理信息。system用户拥有dba,sysdba角色或系统权限。

其次的区别,权限的不同
sys用户必须以as sysdba 或 as sysoper形式登录。不能以normal方式登录数据库
system如果正常登录,它其实就是一个普通的dba用户,但是如果以as sysdba登录,其结果实际上它是作为sys用户登录的,从登录信息里面我们可以看出来。


系统权限 sysdba sysoper
区别 Startup(启动数据库) startup
Shutdown(关闭数据库) shutdown
alter database open/mount/backup alter database open/mount/backup
改变字符集 none
create database(创建数据库) None不能创建数据库
drop database(删除数据库) none
create spfile create spfile
alter database archivelog(归档日志) alter database archivelog
alter database recover(恢复数据库) 只能完全恢复,不能执行不完全恢复
拥有restricted session(会话限制)权限 拥有restricted session权限
可以让用户作为sys用户连接 可以进行一些基本的操作,但不能查看用户数据
登录之后用户是sys 登录之后用户是public



dba权限的用户
dba用户是指具有dba角色的数据库用户。特权用户可以执行启动实例,关闭实例等特殊操作,而dba用户只有在启动数据库后才能执行各种管理工作。

管理初始化参数
初始化参数用于设置实例或是数据库的特征,oracle9i提供了200多个初始化参数,并且每个初始化参数都有默认值。

显示初始化参数
show parameter命令
如何修改参数
需要说明的是如果你希望修改这些初始化的参数,
可以找到文件oralce_home%\admin\databaseName\pfie\init.ora文件中去修改。
如修改数据库实例的名字

数据库(表)的逻辑备份与恢复
逻辑备份是指使用工具export将数据对象的结构和数据导出到文件的过程。
逻辑恢复是指当数据库对象被误操作而损坏后使用工具import利用备份的文件把数据对象导入到数据库的过程。

物理备份即可在数据库open的状态下进行也可在关闭数据库后进行,但是逻辑备份和恢复只能在open的状态下进行。


导出
导出具体的分为:导出表,导出方案,导出数据库三种方式。

特别说明:在导入和导出的时候,要到oracle目录的bin目录下。

导出使用exp命令来完成的,该命令常用的选项有:
userid:用于指定执行导出操作的用户名,口令,连接字符串
tables:用于指定执行导出操作的表
owner:用于指定执行导出操作的方案
full = y:用于指定执行导出操作的数据库
inctype:用于指定执行导出操作的增量类型
rows:用于指定执行导出操作是否要导出表中的数据
file:用于指定导出文件名


导出表
导出自己的表
exp userid = scott/tiger@orcl tables = (emp) file = d:\emp1.dmp

导出其它方案的表
如果用户要导出其它方案的表,则需要dba的权限或是exp_full_database的权限,比如system就可以导出scott的表
expuserid = system/admin@orcl tables = (scott.emp) file = d:\emp2.dmp

导出表的结构
exp userid = scott/tiger@orcl tables = (emp) file = d:\emp3.dmp rows = n

使用直接导出方式
exp userid = scott/tiger@orcl tables = (emp) file = d:\emp4.dmp direct = y
这种方式比默认的常规方式速度要快,当数据量大时,可以考虑使用这样的方法

这时需要数据库的字符集要与客户端字符集完全一致,否则会报错。



导出方案
导出方案是指使用export工具导出一个方案或是多个方案中的所有对象(表,索引,约束...)和数据。并存放到文件中

导出自己的方案
exp scott/tiger@orcl owner = scott file = d:\scott.dmp

导出其它方案
如果用户要导出其它方案,则需要dba的权限或是exp_full_database的权限,例如system用户就可以导出任何方案
exp system/admin@orcl owner = (system, scott) file = d:\system.dmp



导出数据库
导出数据库是指利用export导出所有数据库中的对象及数据。要求该用户具有dba的权限或是exp_full_database权限 (因为数据量大,所以耗时较长)
exp userid = system/admin@orcl full = y inctype = complete file = d:\dba.emp



导入
导入就是使用工具import将文件中的对象和数据导入到数据库中,但是导入要使用的文件必须是export所导出的文件。与导出相似,导入也分为导入表,导入方案,导入数据库三种方式。
imp常用的选项有
userid:用于指定执行导入操作的用户名,口令,连接字符串
tables:用于指定执行导入操作的表
formuser:用于指定源用户
touser:用于指定目标用户
file:由于指定导入文件名
full = y :用于指定执行导入整个文件
inctype:用于指定执行导入操作的增量类型
rows:指定是否要导入表行(数据)
ignore:如果表存在,则只导入数据

导入表
导入自己的表
imp userid = scott/tiger@orcl tables = (emp) file = d:\xx.dmp

导入表到其它用户
要求该用户具有dba的权限,或是imp_full_database
imp userid = system/admin@orcl tables = (emp) file = d:\xx.dmp touser = scott

导入表的结构
只导入表的结构而不导入数据
imp userid = scott/tiger@orcl tables = (emp) file = d:\xx.dmp rows = n

导入数据
如果对象(比如表)已经存在可以只导入表的数据
imp userid = scott/tiger@orcl tables = (emp) file = d:\xx.dmp ignore = y


导入方案
导入方案是指使用import工具将文件中的对象和数据导入到一个或是多个方案中。如果要导入其它方案,要求该用户具有dba的权限,或是imp_full_database

导入自身的方案
imp userid = scott/tiger file = d:\xxx.dmp
导入其它方案
要求该用户具有dba的权限
imp userid = system/admin@orcl file = d:\xxx.dmp fromuser = system touser = scott



导入数据库
在默认情况下,当导入数据库时,会导入所有对象结构和数据
imp userid = system/admin full = y file = d:\xxx.dmp


数据字典和动态性能视图

数据字典是oracle数据库中最重要的组成部分,它提供了数据库的一些系统信息。
动态性能视图记载了例程启动后的相关信息

数据字典
数据字典记录了数据库的系统信息,它是只读表和视图的集合,数据字典的所有者为sys用户。
用户只能在数据字典上执行查询操作(select 语句),而其维护和修改是由系统自动完成的。

数据字典的组成:数据字典包括数据字典基表和数据字典视图,其中基表存储数据库的基本信息,普通用户不能直接访问数据字典的基表。数据字典视图是基于数据字典基表所建立的视图,普通用户可以通过查询数据字典视图取得系统信息。

数据字典视图主要包括 user_xxx,all_xxx,dba_xxx三种类型。

user_tables:
用于显示当前用户所拥有的所有表,它只返回用户所对应方案的所有表
如:sql>  select table_name from user_tables;

all_tables:
用于显示当前用户可以访问的所有表。它不仅会返回当前用户方案的所有表,还会返回当前用户可以访问的其它方案的表
如:sql>  select table_name from all_tables;

dba_tables:
它会显示所有方案拥有的数据库表。但是查询这种数据库字典视图,要求用户必须是dba角色或是select any table 系统权限。
如:当用system用户查询数据字典视图dba_tables时,会返回system,sys,scott...方案对应的数据库表


用户名,权限,角色
在建立用户时,oracle会把用户的信息存放到数据字典中,当给用户授予权限或是角色时,oracle会将权限和角色的信息存放到数据字典。

通过查询dba_users可以显示所有数据库用户的详细信息;
通过查询数据字典视图dba_sys_privs可以显示用户所具有的系统权限;
通过查询数据字典视图dba_tab_privs可以显示用户具有的对象权限;
通过查询数据字典dba_col_privs可以显示用户具有的列权限;
通过查询数据库字典视图dba_role_privs可以显示用户所具有的角色;

如:要查看scott具有的角色,可查询dba_role_privs:
sql>  select * from dba_role_privs where GRANTEE = 'SCOTT';
查询oracle中所有的系统权限,一般是dba
sql>  select * from system_privilege_map order by name;

查询oracle中所有的角色,一般是dba
sql>  select * from dba_roles;

查询oracle中所有的对象权限,一般是dba
sql>  select distinct privilege from dba_tab_privs;

查询数据库的表空间
sql>  select tablepace_name from dba_tablespaces;

查询某个用户具有哪些角色
sql>  select * from dba_role_privs where grantee = '用户名';

查询某个角色包括的系统权限
sql>  select * from dba_sys_privs where grantee = 'DBA';
或者是:
sql>  select * from role_sys_privs where role = 'DBA';

查询某个角色包括的对象权限
sql>  select * from dba_tab_privs where grantee = '角色名'




显示当前用户可以访问的所有数据字典视图
sql>  select * from dict where comments like '%grant%';

显示当前数据库的全称
sql>  select * from global_name;

其它说明
数据字典记录有oracle数据库的所有系统信息。通过查询数据字典可以取得以下系统信息:
对象定义情况
对象占用空间大小
列信息
约束信息
....
这些信息也可以通过pl/sql developer工具查询得到




动态性能视图
动态性能视图用于记录当前例程的活动信息,当启动oracle server时,系统会建立动态性能视图;当停止oracle server时,系统会删除动态性能视图。Oracle的所有动态性能视图都是以v_$开始的,并且oracle为每个动态性能视图都提供了相应的同义词,并且其同义词是以V$开始的。
列如V_$datafile的同义词为V$datafile;动态性能视图的所有者为sys,一般情况下,由dba或是特权用户来查询动态性能视图。



管理表空间和数据文件
介绍:表空间是数据库的逻辑组成部分。从物理上讲,数据库数据存放在数据文件中;从逻辑上讲,数据库则是存放在表空间中,表空间由一个或是多个数据文件组成。

数据库的逻辑结构
介绍:oracle中逻辑结构包括表空间、段、区和块。
说明一下数据库由表空间构成,而表空间又是由段构成,而段又是由区构成,而区又是由oracle块构成的这样的一种结构,可以提高数据库的效率。


表空间
介绍:表空间用于从逻辑上组织数据库的数据。数据库逻辑上是由一个或是多个表空间组成的。通过表空间可以达到以下作用:
控制数据库占用的磁盘空间
dba可以将不同数据类型部署到不同的位置,这样有利于提高i/o性能,同时利用备份和恢复等管理操作

建立表空间
建立表空间是使用create tablespace 命令完成的,需要注意的是,一般情况下,建立表空间是特权用户或是dba来执行的,如果用其它用户来创建表空间,则用户必须要具有create tablespace的系统权限。

建立数据表空间
在建立数据库后,为便于管理表,最好建立自己的表空间
sql>  create tablespace data01 datafile 'd:\data01.dbf' size 20m uniform size 128k;

说明:执行完上述命令后,会建立名称为data01的表空间,并为该表空间建立名称为data01.dbf的数据文件,区的大小为128k

使用数据表空间
sql>  create table mypart(deptno number(4), dname varchar2(14), loc varchar2(13)) tablespace data01;

改变表空间的状态
当建立表空间时,表空间处于练级的(online)状态,此时该空间是可以访问的,并且该表空间是可以读写的,即可以查询该表空间的数据,而且还可以在表空间执行各种语句。但是在进行系统维护或是数据维护时,可能需要改变表空间的状态。一般情况下,由特权用户或是dba来操作。

使表空间脱机(使表空间不可用)
sql>  alter tablespace 表空间名 users offline;

使表空间联机
sql>  alter tablespace 表空间名 users online;

只读表空间
当建立表空间时,表空间可以读写,如果不希望在该表空间上执行update,delete,insert操作,那么可以将表空间修改为只读
sql>  alter tablespace 表空间名 query_data read only;

使表空间可读写
sql>  alter tablespace 表空间名 read write;


举例说明只读特性:

知道表空间名,显示该表空间包括的所有表
sql>  select * from all_tables where tablespace_name = '表空间名';

知道表名,查看该表属于哪个表空间
sql>  select tablespace_name, table_name from user_tables where table_name = '表名';

删除表空间
一般情况下,由特权用户或是dba来操作,如果是其它用户操作,那么要求用户具有drop tablespace 系统权限。
sql>  drop tablespace 表空间名 including contents and datafiles;

说明:including contents 表示删除表空间时,删除该空间的所有数据库对象,而datafiles表示将数据库文件也删除。

扩展表空间
表空间是由数据文件组成的,表空间的大小实际上就是数据文件相加后的大小。那么我们可以想象,假定表employee存放到data01表空间上,初始大小就是2m,当数据满2m空间后,如果再向employee表中插入数据,这样就会显示空间不足的错误。


案例说明:
建立一个表空间 sp01
sql>  create tablespace sp01 datafile 'd:\sp01.dbf' size 20m  uniform size 128k;

在该表空间上建立一个普通表 mydment 其结构和dept一样
sql>  create table mydment (deptno number(2), dname varchar2(14), loc varchar2(13)) tablespace sp01;

向该表中加入数据 insert into mydment select * from mydment;
sql>  insert into mydment select * from mydment;

当一定时候就会出现无法扩展的问题,怎么办?
ORA-01653: 表 SYSTEM.MYDMENT 无法通过 16 (在表空间 SP01 中) 扩展

就扩展该表空间,为其增加更多的存储空间。有三种方法:
扩展表空间
增加数据文件
sql>  alter tablespace sp01 add datafile 'd:\sp02 .dbf' size 20m;

增加数据文件的大小
sql>  alter tablespace 表空间名 'd:\sp01.dbf' resize 20m;
这里需要注意的是数据文件的大小不要超过500m

设置文件的自动增长
sql>  alter tablespace 表空间名 'd:\sp01.dbf' autoextend on next 10m maxsize 500m;

移动数据文件(故障处理)
有时,如果你的数据文件所在的磁盘损坏时,该数据文件将不能再使用,为了能够重新使用,需要将这些文件的副本移动到其它的磁盘,然后恢复。

下面以移动数据文件sp01.dbf为例来说明:
确定数据文件所在的表空间(找到该数据文件所在的表空间)
sql>  select tablespace_name from dba_data_files where file_name = 'd:\sp01.dbf';

使表空间脱机
确保数据文件的一致性,将表空间转变为offline的状态。
sql>  alter tablespace sp01 offline;

使用命令移动数据文件到指定的目标位置
sql>  host move d:\sp01.dbf c:\sp01.dbf;
执行alter tablespace 命令
在物理上移动了数据后,还必须执行alter tablespace 命令对数据库文件进行逻辑修改
sql>  alter tablespace sp01 rename datafile 'd:\sp01.dbf' to c:\sp01.dbf;

使表空间联机
在移动了数据文件后,为了使用户可以访问该表空间,必须将其转变为online状态
sql>  alter tablespace sp01 online;



显示表空间信息
查询数据字典视图dba_tablespaces,显示表空间的信息:
sql>  select tablespace_name from dba_tablespaces;

显示表空间所包含的数据文件
查询数据字典视图dba_data_files,可显示表空间所包含的数据文件,如下:
sql>  select file_name, bytes from dba_data_files where tablespace_name = '表空间名';

其它表空间
除了最常用的数据表空间外,还有其它类型表空间:
索引表空间
undo表空间
临时表空间
非标准块的表空间



维护数据的完整性
数据的完整性用于确保数据库数据遵从一定的商业和逻辑规则。在oralce中,数据完整性可以使用约束、触发器、应用程序(过程、函数)三种方法来实现,在这三种方法中,因为约束易于维护,并且具有最好的性能,所以作为维护数据完整性的首选。

约束
约束用于确保数据库满足特定的商业规则。
在oracle中,约束包括:not null、unique、primary key、foreign key和 check五种。

not null(非空)
如果在列上定义了not null,那么当插入数据时,必须为列提供数据。

unique(唯一)
当定义了唯一约束后,该列值不能是重复的。但是可以为null。

primary key(主键)
用于唯一的标示表行的数据,当定义主键约束后,该列不但不能重复而且不能为null。
需要说明的是:一张表最多只能有一个主键,但是可以有多个unique约束。

foreign key(外键)
用于定义主表和从表之间的关系。外键约束要定义在从表上,主表则必须具有主键约束或是unique约束。当定义外键约束后,要求外键列数据必须在主表的主键列存在或是为null。

check
用于强制行数据必须满足的条件,假定在sal列上定义了check约束,并要求sal列值在1000~2 000之间如果不在1000~2000之间就会提示出错。

商店售货系统表设计案例
现有一个商店的数据库,记录客户及其购物情况,由下面三个表组成:

商品goods(商品号goodsId,商品名称goodsName,单价unitprice,商品类别category,供应商provider);

客户customer(客户号customerId,姓名name,住址address,电邮email,性别sex,身份证cardId);

购买purchase(客户号customerId,商品号goodsId,购买数量nums);

请用SQL语言完成下列功能:
建表,在定义中要求声明:
每个表的主外键;
客户的姓名不能为空值;
单价必须大于0,购买数量必须在1到30之间;
电邮不能够重复。
客户的性别必须是 男或者女,默认是男

商品表
sql>  create table goods( goodsId char(8) primary key, --主键
       goodsName varchar2(30),
       unitprice number(10,2) check (unitprice > 0),  -- check
       category varchar2(8), provider varchar2(30));

客户表
sql>  create table customer(customerId char(8) primary key, --主键
       name varchar2(50) not null, --非空
       address varchar2(50),
       email varchar2(50) unique, --唯一
       sex char(2) default '男' check(sex in('男', '女')), --默认 -check
   cardId char(18));
购买表
sql>  create table purchase(customerId char(8)
references customer(customerId),
   goodsId char(8) references goods(goodsId),
   nums number(10) check (nums between 1 and 30);


商店售货系统表设计案例2
如果在建表时忘记建立必要的约束,则可以在建表后使用alter table命令为表增加约束。但是要注意:增加not null约束时,需要使用modify选项,而增加其它四种约束使用add选项。

每个表的主外键;
客户的姓名不能为空值; -- 增加商品名也不能为空
sql>  alter table goods modify goodsName not null;
单价必须大于0,购买数量必须在1到30之间;
电邮不能够重复。-- 增加身份证也不能重复
sql>  alter table customer add constraint card_unique(约束名称) unique(cardId);
客户的性别必须是 男或者女,默认是男
增加客户的住址只能是‘海淀’、‘海淀’、‘东城’、‘西城’、‘通州’、‘崇文’
sql>   alter table customer add constraint address_check(约束名称) check (address in('海淀', '海淀', '东城', '西城', '通州', '崇文'));

删除约束
当不再需要某个约束时,可以删除
sql>   alter table 表名 drop constraint 约束名称;

在删除主键约束的时候,可能有错误,比如:
sql>   alter table 表名 drop primary key;
这是因为如果在两张表存在主从关系,那么在删除主表的主键约束时,必须带上cascade选项。如:
sql>   alter table 表名 drop primary key cascade;


显示约束信息
显示约束信息
通过查询数据字典视图user_constraint,可以显示当前用户所有的约束的信息。
sql>   select constraint_name, constraint_type, status, validated from user_constraints where table_name = '表名';
显示约束列
通过查询数据字典视图user_cons_columns,可以显示约束所对应的表列信息。
sql>   select column_name, position from user_cons_columns where constraint_name = '约束名';
也可以直接使用pl/sql developer工具查看
表级定义列级定义

列级定义
列级定义是指在定义列的同时定义约束

如在department表定义主键约束
sql>  create table department4(dept_Id number(2), constraint pk_department primary key, name varchar2(12), loc varchar2(12));

表级定义
表级定义是指在定义了所有列后,再定义约束。这里需要注意:not null约束只能在列级上定义。

以在建立employee2表时定义主键约束和外键约束为例:
sql>  create table employee2(emp_Id number(4), name varchar2(15), dept_Id number(2), constraint pk_employee primary key (emp_Id), constraint fk_department foreign key (dept_Id) references department4 (dept_Id));




管理索引
介绍:索引是用于加速数据存取的数据对象。合理的使用索引可以大大降低i/o次数,从而提高数据访问性能。索引有很多种我们主要介绍常用的几种:

创建索引

单列索引
单列索引是基于单个列所建立的索引,比如:
sql>  create index 索引名 on 表名(列名)

复和索引
复合索引是基于两列或是多列的索引。在同一张表上可以有多个索引,但是要求列的组合必须不同,比如:
sql>  create index emp_idx1 on emp (ename, job);
sql>  create index emp_idx1 on emp (job, ename);


使用原则
在大表上建立索引才有意义
在where子句或是连接条件上经常饮用的列上建立索引
索引的层次不要超过4层

索引的缺点
索引缺点分析
索引有一些先天不足:
建立索引,系统要占用大约为表的1.2倍的硬盘和内存空间来保存索引。
更新数据的时候,系统必须要有额外的时间来同时对索引进行更新,以维持数据和索引的一致性。
实践表明,不恰当的索引不但于事无补,反而会降低系统性能。因为大量的索引在进行插入、修改和删除操作时比没有索引花费更多的系统时间。


比如在如下字段建立索引应该是不恰当的:
很少或从不引用的字段;
逻辑性的字段,如男或女(是或否)等。综上所述,提高查询效率是以消耗一定的系统资源为代价的,索引不能盲目的建立,这是考验一个DBA是否优秀的很重要的指标。

其它索引
按照数据存储方式,可以分为B*树、反向索引、位图索引;
按照索引列的个数分类,可以分为单列索引、复合索引;
按照索引列值的唯一性,可以分为唯一索引和非唯一索引;
此外还有函数索引、全局索引、分区索引...

说明:在不同的情况我们会在不同的列上建立索引,甚至建立不同种类的索引。
如:B*树索引建立在重复值很少的列上,而位图索引则建立在重复值很多、不同值相对固定的列上。

显示索引的信息
显示表的所有索引
在同一张表上可以有多个索引,通过查询数据字典视图dba_indexs和user_indexs,可以显示索引信息。其中dba_indexs用于显示数据库所有的索引信息,而user_indexs用于显示当前用户的索引信息:
sql>  select index_name, index_type from user_indexes where table_name = '表名';

显示索引列
通过查询数据字段视图user_ind_columns,可以显示索引对应的列的信息
sql>  select table_name, column_name from user_ind_columns where index_name = 'IND_ENAME';
也可以通过pl/sql deveoper 工具查看索引信息


管理权限和角色
当刚刚建立用户时,用户没有任何权限,也不能执行任何操作。如果要执行某种特定的数据库操作,则必须为其授予系统的权限;如果用户要访问其它方案的对象,则必须为其授予对象的权限,为了简化权限的管理,可以使用角色。
权限
权限是指执行特定类型sql命令或是访问其它方案对象的权利,包括系统权限和对象权限两种:

系统权限介绍
系统权限是指执行特定类型sql命令的权利。它用于控制用户可以执行的一个或是一组数据库操作。比如当用户具有create table权限时,可以在其方案中建表,当用户具有create any table权限时,可以在任何方案中建表。oracle提供了100多种系统权限。
常用的有:
create session 连接数据库 create table 建表
create view 建视图 create public synonym 建同义词
create procedure 建过程、函数、包  create trigger 建触发器
create cluster 建簇

显示系统权限
oracle提供了100多种系统权限,而且oracle的版本越高,提供的系统权限就越多,我们可以查询数据字典视图system_privilege_map,可以显示所有系统权限
sql>  select * from system_privilege_map order by name;

授予系统权限
一般情况下,授予系统权限是由dba完成的,如果用其它用户来授予系统权限,则要求该用户必须具有grant any privilege的系统权限。在授予系统权限时,可以带有with admin option选项。这样,被授予权限的用户或是角色还可以将该系统权限授予其它的用户或是角色。

举例说明:
创建两个用户ken,tom。在初始阶段他们没有任何权限,如果登录就会给出错误的信息
sql>  create user ken(用户名) identified by ken(用户密码);

给用户ken授权
sql>  grant create session, create table to ken with admin option;
sql>  grant create view to ken;

给用户tom授权
我们可以通过ken给tom授权,因为with admin option 是加上的。
当然也可以通过dba给tom授权,我们就用ken给tom授权:
sql>  grant create session, create table to tom;
sql>  grant create view to tom; ----> ok吗? 不可以
回收系统权限
在一般情况下,回收系统权限是dba来完成的,如果其它的用户来回收系统权限,要求该用户必须具有相应系统权限及转授系统权限的选项(with admin option)。回收系统权限使用revoke来完成。

当回收了系统权限后,用户就不能执行相应的操作了,但是请注意,系统权限级联收回问题? 【不是级联回收】
system------------------------>ken------------------------>tom
(create session)      (create session)       (create session)

用system执行如下操作:
sql>  revoke create session from ken; tom用户还可以登录吗? 可以

对象权限介绍
对象权限指访问其它方案对象的权利,用户可以直接访问直接方案的对象,但是如果要访问别的方案的对象,则必须具有对象的权限。
比如SMITH用户要访问scott.emp表(scott:方案,emp:表)则必须在scott.emp表上具有对象的权限。
常用的有:
alter 修改 delete 删除 select 查询 insert 添加
update 修改 index 索引 references 引用 execute 执行

显示对象权限
通过数据字段视图可以显示用户或是角色所具有的对象权限。
视图为dba_tab_privs
sql>  conn system/admin;
sql>  select distinct privilege from dba_tab_privs;
sql>  select grantor, ower, table_name, privilege from dba_tab_privs where grantee = 'blake';

授予对象权限
在oracle9i前,授予对象权限是由对象的所有者来完成的,如果其它的用户来操作,则需要用户具有相应的(with grant option)权限,从oracle9i开始,dba用户(sys,system)可以将任何对象上的对象权限授予其它用户。授予对象权限是用grant命令来完成的。
对象权限可以授予用户,角色,和public。在授予权限时,如果带有with grant option选项,则可以将该权限转授给其它用户。但是要注意with grant option选项不能被授予角色。

monkey用户要操作scott.emp表,则必须授予相应的对象权限
希望monkey可以查询soctt,emp的表数据,怎样操作?
sql>  conn scott/tiger;
sql>  grant select on emp to monkey;

希望monkey可以修改soctt,emp的表数据,怎样操作?
sql>  grant update on emp to monkey;

希望monkey可以删除soctt,emp的表数据,怎样操作?
sql>  grant delete on emp to moneky;

有没有更加简单的方法,一次把所有权限赋给monkey?
sql>  grant all on emp to monkey;

能否对monkey访问权限更加精细控制。(授予列权限)
希望monkey只可以修改scott.emp表的sal字段,怎样操作?
sql>  grant update on emp(sal) to monkey;

希望moneky只可以查询scott.emp表的ename,sal数据,怎样操作?
sql>  grant select on emp(ename, sal) to monkey;

授予alter权限
如果blake用户要修改scott.emp表的结构,则必须授予alter对象权限
sql>  conn scott/tiger;
sql>  grant alter on emp to blake;
当然也可以用system, sys来完成这件事

授予execute权限
如果用户想要执行其它方案的包/过程/函数,则必须有execute权限。
比如为了让ken可以执行包dbms_transaction,可以授execute权限
sql>  conn system/tiger;
sql>  grant execute on dbms_transaction to ken;

授予index权限
如果想在别的方案的表上建立索引,则必须具有index对象权限。
如为了让blake可以在scott.emp表上建立索引,就给其index的对象权限。
sql>  conn scott/tiger;
sql>  grant index on emp to blake;

使用with grant option选项
该选项用于转授对象权限,但是该选项只能被授予用户,而不能授予角色
sql>  conn scott/tiger;
sql>  grant select on emp to blake with grant option;
sql>  conn blake/admin;
sql>  grant select on scott.emp to jones;

回收对象权限
在oracle9i中,收回对象的权限可以由对象的所有者来完成,也可以用dba用户(sys,system)来完成。
这里需要说明的是:收回对象权限后,用户就不能执行相应的sql命令,但是要注意的是对象的权限是否会被级联收回?【级联回收】

如:
scott----------------------------.>blake------------------------------->jones
(select on emp) (select on emp) (select on emp)

sql>  conn scott/tiger@orcl;
sql>  revoke select on emp from blake;
请大家思考,jones能否查询scott.emp表数据。 不能



角色
角色就是相关权限的命令集合,使用角色的主要目的就是为了简化权限的管理。假定有用户a,b,c为了让它们都拥有权限。
连接数据库
在scott.emp表上select,insert,update,delete
如果采用直接授权操作,则需要进行12次授权

如果采用角色就可以简化:首先将create session,select on scott.emp,insert on scott.emp,update on scott.emp,delete on scott.emp授予角色,然后将该角色授予a,b,c用户,这样就可以三次授权搞定。

角色分为预定义和自定义角色两类:

预定义角色
预定义角色是指oracle所提供的角色,每种角色都用于执行一些特定的管理任务,下面我们介绍常用的预定义角色connect,resource,dba

connect角色
connect角色具有一般应用开发人员需要的大部分权限,当建立了一个用户后,多数情况下,只要给用户授予connect和resource角色就够了,那么connect角色具有哪些系统权限呢?
alter session create cluster create database link create session
create table create view create sequence

resource角色
Resource角色具有应用开发人员所需要的其它权限,比如建立存储过程、触发器等。这里需要注意的是resource角色隐含了unlimited tablespace系统权限。
resource角色包含以下系统权限:
create cluster create indextype create table create sequence
create type create procedure create trigger

dba角色
dba角色具有所有的系统权限,及with admin option选项,默认的dba用户为sys和system他们可以将任何系统权限授予其它用户。但是要注意的是dba角色不具备sysdba和sysoper的特权(启动和关闭数据库)

自定义角色
顾名思义就是自己定义的角色,根据自己的需要来定义。一般是dba来建立,如果用别的用户来建立,则需要具有create role 的系统权限。在建立角色时可以指定验证方式(不验证,数据库验证等)

建立角色(不验证)
如果角色是公用的角色,可以采用不验证的方式建立角色。
sql>  create role 角色名 not identified;

建立角色(数据库验证)
采用这样的方式时,角色名,口令存放在数据库中。当激活该角色时,必须提供口令。在建立这种角色时,需要为其提供口令。
sql>  create role 角色名 identified by admin(口令);

角色授权
当建立角色时,角色并没有任何权限,为了使得角色完成特定任务,必须为其授予相应的系统权限和对象权限。

给角色授权
给角色授予权限和给用户授权没有大多区别,但是要注意,系统权限的 unlimited tablespace和对象权限的with grant option 选项是不能授予角色的。
sql>  conn system/admin;
sql>  grant create session to 角色名 with admin option;
sql>  conn scott/tiger@orcl;
sql>  grant select on emp to 角色名;
sql>  grant insert, update, delete on emp to 角色名;
通过上面的步骤,就给角色授权了。

分配角色给某个用户
一般分配角色是由dba来完成的,如果要以其它用户身份分配角色,则要求用户必须具有grant any role的系统权限。
sql>  conn system/admin;
sql>  grant 角色名 to blake with admin option;
因为我给了with admin option 选项,所以blake可以把system分配给它的角色分配给别的用户。

删除角色
使用 drop role,一般是dba来执行,如用其它用户则要求该用户具有drop any role系统权限。
sql>  conn system/admin;
sql>  drop role 角色名;

如果角色被删除了,那么使用该角色的用户是否还可以登录? 不能

显示角色信息
显示所有角色
sql>  select * from dba_roles;

显示角色具有的系统权限
sql>  select privilege, admin_option from role_sys_privs where role = '角色名';

显示角色具有的对象权限
通过查询数据字典视图dba_tab_privs可以查看角色具有的对象权限或是列的权限。

显示用户具有的角色,及默认角色
当以用户的身份连接到数据库时,oracle会自动的激活默认的角色,通过查询数据字典视图dba_role_privs可以显示某个用户具有的所有角色及当前默认的角色。
sql>  select granted_role, default_role from dba_role_privs where grantee = '用户名';

精细访问控制
精细访问控制是指用户可以使用函数、策略实现更加细微的安全访问控制。如果使用精细访问控制,则当在客户端发出sql语句(select,insert,update,delete)时,oracle会自动在sql语句后追加谓词(where子句),并执行新的sql语句。通过这样的控制,可以使得不同的数据库用户在访问相同表时,返回不同的数据信息。如:
用户: scott blake jones

策略: emp_access

数据库表emp

如上所示:通过策略emp_access,用户scott,blake,jones在执行相同的sql语句时,可以返回不同的结果。例如:当执行select ename from emp;时,根据实际情况可以返回不同的结果。


plsql编程

pl/sql是什么
pl/sql(procedural language/sql)是oracle在标准的sql语言上的扩展。pl/sql不仅允许嵌入sql语言,还可以定义变量和常量,允许使用条件语句和循环语句,允许使用例外处理各种错误,这样使得它的功能变得更加强大。

过程,函数,触发器是pl/sql编写的
过程,函数,触发器是在oracle中的
pl/sql是非常强大的数据库过程化语言
用pl/sql编写的过程,函数可以在java程序中调用

学习pl/sql的必要性
提高应用程序的运行性能
模块化的设计思想【分页的过程,订单的过程,转账的过程...】
减少网络传输量
提高安全性
pl/sql的缺点:移植性不好

sqlpuls开发工具
sqlpuls是oracle公司提供的一个工具,这个因为我们在以前介绍过的:
举一个简单案例:
编写一个存储过程,该过程可以向某表中添加记录。
创建一个简单的表
sql>  conn scott/tiger;
sql>  create table mytest(name varchar2(30), password varchar2(30));

创建存储过程
sql>  create or replace procedure pro1(存储过程名) is
   begin
       --执行部分
       insert into mytest values ('小白', 'm123');
       end;
       /

replace:表示如果有pro,就替换。

如何查看错误信息:sql>  show error;

如何调用该过程
sql>  exec 过程名(参数值1, 参数值2...);
sql>  call 过程名(参数值1, 参数值2...);

sql>  exec pro1;

pl/sql developer开发工具
pl/sql developer是用于开发pl/sql块的集成开发环境(IDE),它是一个独立的产品,而不是oracle的一个附带品。

举一个简单案例:
编写一个存储过程,该过程可以删除某表记录。
sql>  conn scott/tiger;
sql>  create or replace procedure pro2(存储过程名) is
       begin
       --执行部分
       delete from mytest where name = '小白';
       end;
       /

sql>  exec pro2;

介绍:开发人员使用pl/sql编写应用模块时,不仅需要掌握sql语句的编写方法,还需要掌握pl/sql语句及语法规则。pl/sql编程可以使用变量和逻辑控制语句,从而可以编写非常有用的功能模块。比如:分页存储过程模块、订单处理存储过程模块、转账存储过程模块...而且如果使用pl/sql编程,我们可以轻松的完成非常复杂的查询要求。

简单分类

| ---------- 过程(存储过程)
|
| ---------- 函数
块(编程)---------------|
| ---------- 触发器
|
| ---------- 包

编写规范
注释
单行注释 --
sql>  select * from emp where empno = 7788;     --取得员工信息
多行注释 /*....*/ 来划分

标识符号(变量)的命名规范
当定义变量时,建议用 v_ 作为前缀 v_sal
当定义变量时,建议用 c_ 作为前缀 c_rate
当定义游标时,建议用 _cursor 作为后缀 emp_cursor
当定义例外时,建议用 e_ 作为前缀 e_error

pl/sql块
介绍:块(block)是pl/sql的基本程序单元,编写pl/sql程序实际上就是编写pl/sql块。要完成相对简单的应用功能,可能只需要编写一个pl/sql块;但是如果想要实现复杂的功能,可能需要在一个pl/sql块中嵌套其它的pl/sql块。

块结构示意图
pl/sql块由三个部分构成:定义部分、执行部分、例外处理部分。

如下所示:
declare
/*定义部分-------定义常量、变量、游标、例外、复杂数据类型*/
begin
/*执行部分-------要执行的pl/sql语句和sql语句*/
exception
/*例外处理部分-------处理运行的各种错误*/
end;







实例1 - 只包括执行部分的pl/sql块
sql> 
set serveroutput on  -- 打开输出选项
begin
dbms_output.put_line('hello, world');
end;
/

相关说明:
dbms_output是oracle所提供的包(类似java的开发包),该包包含一些过程,put_line就是dbms_output包的一个过程。

实例2 - 包含定义部分和执行部分的pl/sql块

sql> 
set serveroutput on -- 打开输出选项
declare
-- 定义变量
v_ename varchar2(5);
begin
-- 执行部分
select ename into v_ename from emp where empno = &no(no可以是任意字符变量);
-- 在控制台显示雇员名
dbms_output.put_line('雇员名:' || v_ename);
end;
/

相关说明:
& 表示要接收从控制台输入的变量

实例3 - 包含定义部分、执行部分和例外处理部分的pl/sql块
为了避免pl/sql程序的运行错误,提高pl/sql的健壮性,应该对可能的错误进行处理,这个很有必要:


比如在实例2中,如果输入了不存在的雇员号,应当做例外处理。
有时出现异常,希望用例外的逻辑处理

sql> 
declare
-- 定义变量
v_ename varchar2(5);
V_sal nmuber(7, 2);
begin
-- 执行部分
select ename, sal into v_ename, v_sal from emp where empno = &no;
-- 在控制台显示雇员名
dbms_output.put_line('雇员名:' || v_ename || ' 薪水:' || v_sal);
-- 异常处理
exception
when no_data_found then
dbms_output.put_line('雇员编号输入错误,请重新输入!');
end;
/

相关说明:oracle实现预定义了一些例外,no_data_found就是找不到数据的例外。

过程
过程用于执行特定的操作。当建立过程时,既可以指定输入参数(in),也可以指定输出参数(out)。通过在过程中使用输入参数,可以将数据传递到执行部分;通过使用输出参数,可以将执行部分的数据传递到应用环境。在sqlplus中可以使用create procedure 命令来建立过程。


实例如下:

请考虑编写一个过程,可以输入雇员名,新工资 可以修改雇员的工资
sql>  create procedure pro3 (pName varchar2, pSal number) is
       begin
       -- 执行部分,根据雇员名修改雇员工资
       update emp set sal = pSal where ename = pName;
       end;
       /

如何调用过程有两种方法:
sql>  exec pro3('SCOTT', '4000');
sql>  call pro3('SCOTT', '4000');


如何在java程序中调用一个存储过程
//调用存储过程 
   try { 
           Class.forName("Oracle.jdbc.driver.OracleDriver"); 
           Connection con =
DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "scott", "tiger"); 
           String sql ="{ call PP_test(?,?) }"; 
           // 创建 CallableStatement
           CallableStatement call = conn.prepareCall(sql); 
  
           call.setString(1, "SMITH"); 
           call.setInt(2, 1000); 
           call.execute(); 
        } catch (Exception e) { 
              e.prtintStackTrace();
        }finally{ 
            // 关闭数据库连接 
        } 


函数
函数用于返回特定的数据,当建立函数时,在函数头部必须包含return子句,而在函数体内必须包含return语句返回的数据。我们可以使用create function 来建立函数。

实际案例:
-- 输入雇员的姓名,返回该雇员的年薪。
sql>  create function fun(fName varchar2)
       return number is
       yearSal number(7, 2);
       begin
       -- 执行部分
       select sal * 12 + nvl(comm, 0) * 12 into yearSal from emp where ename = fName;
       return yearSal;
       end;
       /

在sqlplus中调用函数
sql>  var abc number;
sql>  call fun('SCOTT') into:abc;
sql>  print abc;

同样我们可以在java程序中调用该函数
select fun('SCOTT') from emp;  //这样
可以通过rs.getInt(1)得到返回的结果

包用于在逻辑上组合过程和函数,它由包规范和包体两部分组成。
我们可以使用 create package 命令来创建包:
实例:
       -- 创建包
       -- 创建一个包 sp_package
       -- 声明该包有一个过程 pro
       -- 声明该包有一个函数 fun
sql>  create package sp_package(包名) is
       procedure pro(pName varchar2, pSal number);
       function fun(name varchar2) return number;
       end;
       /
包的规范只包含了过程和函数的说明,但是没有过程和函数的实现代码。包体用于实现包规范中的过程和函数。

建立包体可以使用 create package body 命令
   -- 给包 sp_package 实现包体
sql>  create or replace package body sp_package is
       procedure pro(pName varchar2, pSal number) is
       begin
     update emp set sal = pSal where ename = pName;
   end;

      function fun(name varchar2)
       return number is
     yearSal number;
     begin
     Select sal * 12 + nvl(comm, 0) into yearSal from emp where ename = fName;
          return yearSal;
       end;
       end;
       /

如何调用包的过程或是函数
当调用包的过程或是函数时,在过程和函数前需要带有包名,如果要访问其它方案的包,还需要在包名前加方案名。
如:
sql>  call sp_package.pro('SCOTT', 1500);
sql>  exec sp_package.pro('SCOTT', 1500);
特别说明:包是pl/sql非常重要的部分


触发器
触发器是指隐含的执行的存储过程。当定义触发器时,必须指定触发的事件和触发的操作,常用的触发事件包括insert,update,delete语句,而触发操作实际就是一个pl/sql块。可以使用create trigger 来建立触发器。




定义并使用变量
在编写pl/sql程序时,可以定义变量和常量;在pl/sql程序中包括有:
标量类型(scalar)
复合类型(composite)
参照类型(reference)
lob(large object)

标量(scalar) - 常用类型
在编写pl/sql块时,如果要使用变量,需在定义部分定义变量。pl/sql中定义变量和常量的语法如下:
identifier  [constant]  datatype  [not null]   [:= |  default expr]
identifier:名称
constant:指定常量。需要指定它的初始值,且其值是不能改变的
datatype:数据类型
not null:指定变量值不能为null
:= 给变量或是常量指定初始值
default:用于指定初始值
expr:指定初始值的ps/sql表达式,可以是文本值、其它变量、函数等。


标量定义的案例
定义一个变长字符串
sql>  v_ename varchar2(10);

定义一个小数 范围 -99999.99 ~ 99999.99
sql>  v_sal number(6, 2);

定义一个小数并给一个初始值为5.4 := 是pl/sql的赋值号
sql>  v_sal2 number(6, 2):= 5.4

定义一个日期类型的数据
sql>  v_hiredate date;

定义一个布尔变量,不能为空,初始值为false
sql>  v_valid boolean not null default false;

标量(scalar) - 使用标量
在定义好变量后,就可以使用这些变量。这里需要说明的是pl/sql块为变量赋值不同于其它的编程语言,需要在等号前加冒号(:=)

下面以输入员工号,显示雇员姓名,工资、个人所得税(税率为0.03)为例。说明变量的使用,看看如何编写。
sql>  set serveroutput on
       declare
       c_tax_rate number(3, 2) := 0.03;
       v_ename varchar2(5);
       v_sal number(7, 2);
       v_tax_sal number(7, 2);
       begin
       -- 执行部分
       select ename, sal into v_ename, v_sal from emp where empno = &no;
       -- 计算所得税
       v_tax_sal := v_sal * c_tax_rate;
       --  输出
       dbms_output.put_line('姓名是:' || v_ename || '工资:' || v_sal ||    '个人所得税为:' || v_tax_sal);
       end;
       /

标量(scalar) - 使用%type类型
对于上面的pl/sql块有一个问题:
就是如果员工的姓名超过了5个字符的话,就会有错误,为了降低pl/sql程序的维护工作量,可以使用%type属性定义变量,这样它就会按照数据库列来确定你定义的变量的类型和长度。

如何使用:标示符名 表名.列名%type;
sql>  v_ename emp.ename%type;



复合变量(composite)- 介绍
用于存放多个值的变量。主要包括这几种:
pl/sql记录
pl/sql表
嵌套表
varray

复合类型 - pl/sql记录
类似于高级语言中的结构体(类),需要注意的是:当引用pl/sql记录成员时,必须要加记录变量作为前缀(记录变量.记录成员)如下:
-- pl/sql记录实例
sql>  declare
   -- 定义一个pl/sql记录类型 emp_record_type,类型包含三个数据
分别是:name, salary,title
       type emp_record_type is record(
       name emp.ename%type,
       salary emp.sal%type,
       title emp.job%type);
   -- 定义一个sp_record 变量,变量的类型是 emp_record_type
       sp_record emp_record_type;
       begin
       select ename, sal, job into sp_record from emp where empno = 7788;
       dbms_output.put_line('员工名:' || sp_record.name || '薪水' ||    sp_record.salary);
       end;
       /

复合类型 - pl/sql表
相当于高级语言中的数组。但是需要注意的是在高级语言中数组的下标不能为负数,而pl/sql是可以为负数的,并且表元素的下标没有限制。实例如下:

-- pl/sql表实例
sql>  declare
       -- 定义一个pl/sql表类型 sp_table_type,
   该类型是用于存放emp.ename%type类型的数组
       -- index by binary_integer表示下标是整数
       type sp_table_type is table of emp.ename%type
       index by binary_integer;
       -- 定义了一个 sp_table变量,这个变量的类型是sp_table_type
       sp_table sp_table_type;
       begin
       select ename into sp_table(0) from emp where empno = 7788;
       dbms_output.put_line('员工名:' || sp_table(0));
       end;
       /

说明:
sp_table_type 是pl/sql表类型
emp.ename%type 指定了表的元素的类型和长度
sp_table 为pl/sql表变量
sp_table(0)则表示下标为0的元素

如果把where条件去小会怎样?  应该使用参照变量

参照变量 - 介绍
参照变量是指用于存放数值指针的变量。通过使用参照变量,可以使得应用程序共享相同对象,从而降低占用的空间。在编写pl/sql程序时,可以使用游标变量(ref cursor)和对象类型变量(ref obj_type)两种参照变量类型

参照变量 -ref cursor游标变量
使用游标时,当定义游标时不需要指定相应的select语句,但是当使用游标时(open时)需要指定select语句,这样一个游标就与一个select语句结合了。实例如下:

请使用pl/sql编写一个块,可以输入部门号,并显示该部门所有员工姓名和他的工资。
sql>  declare
       -- 定义一个游标类型sp_emp_cursor
       type sp_emp_cursor is ref cursor;
       -- 定义一个游标变量
       test_cursor sp_emp_cursor;
       -- 定义变量
       v_ename emp.ename%type;
       v_sal emp.sal%type;
       begin
   -- 执行部分
       -- 把test_cursor和一个 select结合
       open test_cursor for select ename, sal from emp where deptno=&no;
       -- 循环取出
       loop
       fetch test_cursor into v_ename, v_sal;
            -- 判断test_cursor是否为空
            exit when test_cursor%notfound;
       dbms_output.put_line('姓名:' || v_ename || ' 薪水:' ||     v_sal);
       end loop;
       -- 关闭游标
       close test_cursor;
       end;
       /

控制结构
在任何计算机语言(c,java,pascal)都有各种控制语句(条件语句,循环结构,顺序控制机构...)在pl/sql中也存在这样的控制结构。

条件分支语句
pl/sql中提供了三种条件分支语句 if -- then, if -- then -- else, if -- then -- elsif -- else



简单的条件判断if - then
?编写一个过程,可以输入一个雇员姓名,如果该雇员的工资低于2000元,就给该雇员工资增加10%;
sql>  create or replace procedure pro1(pName varchar2) is
   -- 定义部分
       v_sal emp.sal%type;
       begin
   -- 执行部分
       select sal into v_sal from emp where ename = pName;
       -- 判断
       if v_sal < 2000 then
       update emp set sal = sal + sal * 10 where ename = pName;
       end if;
       end;
       /

sql>  exec pro('SCOTT');

二重条件分支 if - then - else
?编写一个过程,可以输入一个雇员名,如果该雇员的补助不是0就在原来的基础上增加100;如果为0就把补助设置为200;
sql>  create or replace procedure pro2(pName varchar2) is
   -- 定义部分
       v_comm emp.comm%type;
       begin
   -- 执行部分
       select sal into v_comm from emp where ename = pName;
   -- 判断
       if v_comm <> 0 then
       update emp set comm = comm + 100 where ename = pName;
       else
       update emp set comm = comm + 200 where ename = pName;
       end if;
       end;
       /

sql>  exec pro('SCOTT');

多重条件分支if -- then -- elsif --else
?编写一个过程,可以输入一个雇员编号,如果该雇员的职位是president就给他的工资增加1000,如果该职员的职位是manager就给他的工资增加500,其它职位的雇员工资增加200;
sql>  create or replace procedure pro3(pNo number) is
   -- 定义部分
       v_job emp.job%type;
       begin
   -- 执行部分
       select job into v_job from emp where emptno = pNo;
   -- 判断
       if v_job = 'PRESIDENT' then
       update emp set sal = sal + 1000 where empno = pNo;
       elsif v_job = 'MANAGER' then
       update emp set sal = sal + 500 where empno = pNo;
       else
       update emp set sal = sal + 200 where empno = pNo;
       end if;
       end;
       /

sql>  exec pro(7839);

循环语句 -loop
是pl/sql中最简单的循环语句,这种循环语句以loop开头,以end loop结尾,这种循环至少会被执行一次。
案例:现有一张表users,表结构如下
                                                
用户id 用户名


请编写一个过程,可输入用户名,并循环添加10个用户到users表中,用户编号从1开始增加。
sql>  create table users(userNo number, userName varchar2(40));

sql>  create or replace procedure pro4(pName varchar2) is
   -- 定义部分
       v_num number := 1;
       begin
   -- 执行部分
       loop
       insert into users values(v_num, pName);
           -- 判断是否要退出循环
       exit when v_num = 10;
        -- 自增
       v_num := v_num + 1;
       end loop;
       end;
       /

循环语句 -while循环
基本循环至少要执行循环体一次,而对于while循环来说,只有条件为true时,才会执行循环体语句,while循环以while...loop开始,以end loop 结束。

案例:现有一张users表,表结构如下

用户id 用户名


请编写一个过程,可输入用户名,并循环添加10个用户到users表中,用户编号从11开始增加。

sql>  create or replace procedure pro5(pName varchar2) is
   -- 定义部分
       v_num number := 11;
       begin
   -- 判断
       while v_num <= 20 loop
            -- 执行部分
       insert into users values(v_num, pName);
    -- 自增
       v_num := v_num + 1;
       end loop;
       end;
       /

循环语句 -for循环
基本for循环的基本结构如下
sql>  begin
       for i in reverse 1.. 10 loop
       insert into users values(i, '小白');
end loop;
   end;
   /

我们可以看到控制变量i,在隐含中就在不停的增加



顺序控制语句 -goto, null
goto语句用于跳转到特定标号去执行语句。注意由于使用goto语句会增加程序的复杂性,并使得应用程序可读行变差,所以在做一般应用开发时,建议大家不要使用goto语句。

基本语法如下 goto lable,其中lable是已经定义好的标号名。

goto案例
sql>  declare
       i int := 1;
       begin
       loop
       dbms_output.put_line('输出i =' || i);
   if i=10 then
goto end_loop;
end if;
i := i + 1;
end loop;
<<end_loop>>
dbms_output.put_line('循环结束');
end;
/

null语句
null语句不会执行任何操作,并且会直接将控制传递到下一条语句。使用null语句的主要好处是可以提高pl/sql的可读性。

null语句案例
sql>  declare
   v_sal emp.sal%type;
   v_ename emp.ename%type;
   begin
   select ename, sal into v_ename, v_sal from emp where
   v_sal < 3000 then
       update emp set comm = sal * 0.1 where ename = v_ename;
  else
   null;
       end if;
       end;






编写分页过程

无返回值的存储过程
案例:现有一张表 book,表结构如下:
书号 书名 出版社


请编写一个过程,可以向book表中添加书,要求通过java程序调用该过程。

-- book表
sql>  create table book(bookId number, bookName varchar2(50), publishHouse varchar2(50));

-- 编写过程
-- in:表示这是一个输入参数,默认为in
-- out:表示是一个输出参数
sql>  create or replace procedure pro6(pBookId in number, pBookName    in varchar2, pPublishHouse in varchar2) is
   begin
   insert into book values(pBookId, pBookName, pPublishHouse);
     end;

-- 在java中调用
package com.bo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

// 调用一个无返回值的存储过程
public class Test1 {
private final static
    String URL = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
private final static String USERNAME = "scott";
private final static String USERPASSWORD = "tiger";
private final static String SQL = "{call pro6(?, ?, ?)}";
private static Connection con = null;
private static CallableStatement cs = null;

public static void main(String[] args) {

try {
Class.forName("oracle.jdbc.driver.OracleDriver");
con = DriverManager.getConnection(URL,
            USERNAME, USERPASSWORD);
cs = con.prepareCall(SQL);
cs.setInt(1, 10);
cs.setString(2, "笑傲江湖");
cs.setString(3, "人民出版社");

cs.execute();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (cs != null) {
try {
cs.close();
                    cs = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if(con != null){
try {
con.close();
                    con = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}






有返回值的存储过程(非列表)
-- 有输入和输出的存储过程

案例:编写一个过程,可以输入书的编号,在java中返回该书的书籍名称。
sql>  create or replace procedure pro7 (pBookId in number, pName out     varchar2) is
       begin
   select bookName into pName from book where bookId = pBookId;
       end;

-- 在java中调用
package com.bo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

//调用一个有返回值的存储过程
public class Test2 {
private final static
    String URL = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
private final static String USERNAME = "scott";
private final static String USERPASSWORD = "tiger";
private final static String SQL = "{call pro7(?, ?)}";
private static Connection con = null;
private static CallableStatement cs = null;

public static void main(String[] args) {

try {
Class.forName("oracle.jdbc.driver.OracleDriver");
con = DriverManager.getConnection(URL,
            USERNAME, USERPASSWORD);
cs = con.prepareCall(SQL);
cs.setInt(1, 10);
cs.registerOutParameter(2, oracle.jdbc.OracleTypes.VARCHAR);
cs.execute();
// 取出返回值,要注意?号的顺序
String name = cs.getString(2);
System.out.println(name);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (cs != null) {
try {
cs.close();
                    cs = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if(con != null){
try {
con.close();
                    con = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}



案例扩张:编写一个过程,可以输入雇员的编号,返回该雇员的姓名、工资和岗位。
sql>  create or replace procedure pro8 (pNo in number, pName out     varchar2, pSal out number, pJob out varChar) is
       begin
   select eName, sal, job into pName, pSal, pJob from emp where     empno= pNo;
       end;

-- 在java中调用
package com.bo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

//调用一个有返回值的存储过程
public class Test2 {
private final static
    String URL = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
private final static String USERNAME = "scott";
private final static String USERPASSWORD = "tiger";
private final static String SQL = "{call pro8(?, ?, ?, ?)}";
private static Connection con = null;
private static CallableStatement cs = null;

public static void main(String[] args) {

try {
Class.forName("oracle.jdbc.driver.OracleDriver");
con = DriverManager.getConnection(URL,
            USERNAME, USERPASSWORD);
cs = con.prepareCall(SQL);
cs.setInt(1, 7788);
cs.registerOutParameter(2, oracle.jdbc.OracleTypes.VARCHAR);
cs.registerOutParameter(3, oracle.jdbc.OracleTypes.NUMBER);
cs.registerOutParameter(4, oracle.jdbc.OracleTypes.VARCHAR);
cs.execute();
// 取出返回值,要注意?号的顺序
String ename = cs.getString(2);
float sal = cs.getFloat(3);
String job = cs.getString(4);
System.out.println("雇员姓名:"+ ename + "薪水:" + sal + "岗位:" + job);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (cs != null) {
try {
cs.close();
                    cs = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if(con != null){
try {
con.close();
                    con = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}



有返回值的存储过程(列表[结果集])
案例:编写一个过程,输入部门号,返回该部门所有雇员的信息。对该题分析如下:
由于oracle存储过程没有返回值,它的所有返回值都是通过out参数来替代的,列表同样也不例外,但是由于是集合,所以不能用一般的参数,必须要用package。所以要分为两部分:
建一个包。如下:
    -- 创建一个包,在该包中,定义类型test_cursor,是个游标类型
sql>  create or replace package testPackage as
   type test_cursor is ref cursor;
   end testPackage;

创建存储过程
sql>  create or replace procedure pro9(pNo in number, p_cursor out
       testPackage.test_cursor) is
       begin
       open p_cursor for select * from emp where deptno = pNo;
       end;

-- 在java中调用
package com.bo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Test3 {
private final static
    String URL = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
private final static String USERNAME = "scott";
private final static String USERPASSWORD = "tiger";
private final static String SQL = "{call pro9(?, ?)}";
private static Connection con = null;
private static CallableStatement cs = null;
private static ResultSet rs = null;

public static void main(String[] args) {

try {
Class.forName("oracle.jdbc.driver.OracleDriver");
con = DriverManager.getConnection(URL,
            USERNAME, USERPASSWORD);
cs = con.prepareCall(SQL);
cs.setInt(1, 10);
cs.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);
cs.execute();

// 得到结果集
rs = (ResultSet) cs.getObject(2);
while (rs.next()) {
System.out.println("雇员编号:" + rs.getInt(1) + " " + "姓名:"
+ rs.getString(2) + " " + "岗位:" + rs.getString(3) + " "
+ "上级领导:" + rs.getInt(4) + " " + "入职日期:"
+ rs.getDate(5) + " " + "薪水:" + rs.getDouble(6) + " "
+ " " + "奖金:" + rs.getDouble(7) + " " + "部门编号:"
+ rs.getInt(8));
}

} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
rs = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (cs != null) {
try {
cs.close();
cs = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (con != null) {
try {
con.close();
con = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
}
}


编写分页过程

要求:编写一个存储过程,要求可以输入表名,每页显示记录数、当前页。返回总记录数,总页数和返回的结果集。

-- oracle的分页
sql>  select t1.*, rownum rn from (select * from emp) t1;

sql>  select t1.*, rownum rn from (select * from emp) t1 where rownum <= 10;

sql>  select * from (select t1.*, rownum rn from (select * from emp) t1 where rownum <= 10) where rn >= 6;
-- 开发一个包
sql>  create or replace package fenyePackage as
       type page_cursor is ref cursor;
       end fenyePackage;

-- 开始编写分页过程
sql>  create or replace procedure fenye(
       tableName in varchar2,   -- 表名
       pageSize in number,   -- 每页显示记录数
       nowPage in number,   -- 当前页
       myRows out number,   -- 总记录数
       myPageCount out number,  -- 总页数
       page_cursor out fenyePackage.page_cursor  -- 返回的记录集
       ) is
   -- 定义部分
       -- 定义sql语句 字符串
   v_sql varchar2(1000);
   -- 定义两个整数
     v_begin number := (nowPage - 1) * pageSize + 1;
       v_end number := nowPage * pageSize; 
     begin
   -- 执行部分
       v_sql := 'select * from (select t1.*, rownum rn from (select *    from '|| tableName ||') t1 where rownum <= '|| v_end ||') where    rn >= '|| v_begin;
   -- 打开游标和sql关联
       open page_cursor for v_sql;
       -- 计算myRows和myPageCount
       -- 组织一个sql
     v_sql := 'select count(*) from ' || tableName;
   -- 执行sql语句,并将返回的值赋给myRows;
      execute immediate v_sql into myRows;
       -- 计算myPageCount
   if mod(myRows, pageSize) = 0 then
       myPageCount := myRows / pageSize;
       else
       myPageCount := myRows / pageSize + 1;
       end if;
   end;
       /
-- 在java中调用

package com.bo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;

// 测试分页
public class Test4 {
private final static
    String URL = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
private final static String USERNAME = "scott";
private final static String USERPASSWORD = "tiger";
private final static String SQL = "{call fenye(?, ?, ?, ?, ?, ?)}";
private static Connection con = null;
private static CallableStatement cs = null;
private static ResultSet rs = null;

public static void main(String[] args) {

try {
Class.forName("oracle.jdbc.driver.OracleDriver");
con = DriverManager.getConnection(URL,
            USERNAME, USERPASSWORD);
cs = con.prepareCall(SQL);

cs.setString(1, "emp"); // 表名
cs.setInt(2, 3); // 一夜显示几条数据
cs.setInt(3, 1); // 从第几页开始显示

// 注册总记录数
cs.registerOutParameter(4, oracle.jdbc.OracleTypes.INTEGER);
// 注册总页数
cs.registerOutParameter(5, oracle.jdbc.OracleTypes.INTEGER);
// 注册返回的结果集
cs.registerOutParameter(6, oracle.jdbc.OracleTypes.CURSOR);

// 执行
cs.execute();

// 取出总记录数
int rowNum = cs.getInt(4);
// 取出总页数
int pageCount = cs.getInt(5);
// 取出返回的结果集
rs = (ResultSet) cs.getObject(6);

// 显示结果
System.out.println("rowNum = " + rowNum);
System.out.println("总页数:" + pageCount);
while(rs.next()) {
System.out.println("EMPNO:" + rs.getInt(1) + " ENAME:"
+ rs.getString(2) + " JOB:" + rs.getString(3)
+ " MGR:" + rs.getInt(4) + " HIREDATE:"
+ rs.getDate(5) + " " + " SAL:" + rs.getDouble(6)
    + " COMM:" + rs.getDouble(7) + " " + " DEPTNO:"
+ rs.getInt(8));
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
rs = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (cs != null) {
try {
cs.close();
cs = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (con != null) {
try {
con.close();
con = null;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
}
}


例外的分类
oracle将例外分为预定义例外,非预定义例外和自定义例外三种。
预定义例外用于处理常见的oracle错误
非预定义例外用于处理预定义例外不能处理的例外
自定义例外用于处理与oracle错误无关的其它情况

例外传递
如果不处理例外我们看看会出现什么情况:

案例:编写一个过程,可接收雇员的编号,并显示该雇员的姓名。问题是:如果输入的雇员编号不存在,怎样去处理?

-- 例外案例
sql>  declare
       v_ename emp.ename%type;
       begin
       select ename into v_ename from emp where empno = &no;
       dbms_output.put_line('雇员姓名:' || v_ename);
       exception
      then no_data_found then
  dbms_output.put_line('雇员编号不存在!');
       end;

处理预定义例外
预定义例外是由pl/sql所提供的系统例外。当pl/sql应用程序违反了oracle规定的限制时,则会隐含的触发一个内部例外。pl/sql为开发人员提供了二十多个预定义例外。

常见的例外:

预定义例外case_not_found
在开发pl/sql块中编写case语句时,如果在when子句中没有包含必须的条件分支,就会触发case_not_found的例外。
sql>  create or replace procedure pro10(pNo number) is
       v_sal emp.sal%type;
       begin
select sal into v_sal from emp where empno = pNo;
   case
when v_sal < 1000 then
update emp set sal = sal + 100 where empno = pNo;
        when v_sal < 2000 then
update emp set sal = sal + 200 where empno = pNo;
end case;
   exception
  when case_not_found then
     dbms_output.put_line('case语句没有与' || v_sal || '相匹配   的条件');
       end;


预定义例外cursor_already_open
当重新打开已经打开的游标时,会隐含的触发cursor_already_open的例外。
sql>  declare
       cursor emp_cursor is select ename, sal from emp;
   begin
      open emp_cursor;
     for emp_record1 in emp_cursor loop
   dbms_output.put_line(emp_record1.ename);
   end loop;
      exception
  when cursor_already_open then
dbms_output.put_line('游标已经打开');
       end;


预定义例外 dup_val_on_index
在唯一索引所对应的列上插入重复的值时,会隐含的触发dup_val_on_index的例外。
sql>  begin
   insert into dept values(10, '公安部', '北京');
      exception
  when dup_val_on_index then
dbms_output.put_line('在deptno列上不能出现重复值');
       end;

预定义例外 invalid_cursor
当试图在不合法的游标上执行操作时,会触发invalid_cursor例外。
例如:试图从没有打开的游标提取数据,或是关闭没有打开的游标。则会触发该例外。
sql>  declare
       cursor emp_cursor is select ename, sal from emp;
       emp_record emp_cursor%rowtype;
   begin
      open emp_cursor; --打开游标
     fetch emp_cursor into emp_record;
   dbms_output.put_line(emp_record.ename);
   close emp_cursor;
      exception
  when invalid_cursor then
dbms_output.put_line('请检测游标是否打开');
       end;

预定义例外 invalid_number
当输入的数据有误时,会触发该例外

比如:数字100写成了loo就会触发该例外
sql>  begin
   update emp set sal = sal+ 'loo';
      exception
  when invalid_number then
dbms_output.put_line('输入的数字不正确');
       end;

预定义例外 no_data_found
下面是一个pl/sql块,当执行select into 没有返回行,就会触发该例外
sql>  declare
       v_sal emp.sal%type;
     begin
      select sal into v_sal from emp where ename = '&name';
      exception
  when no_data_found then
dbms_output.put_line('不存在该员工');
       end;

预定义例外 too_many_rows
当执行select into 语句时,如果返回超过了一行,就会触发该例外
sql>  declare
       v_ename emp.ename%type;
     begin
      select ename into v_ename from emp;
      exception
  when too_many_rows then
dbms_output.put_line('返回了多行数据');
       end;

预定义例外 zero_divide
当执行2/0语句时,就会触发该例外

预定义例外 value_error
当在执行赋值操作时,如果变量的长度不足以容纳实际数据,就会触发该例外 value_error
sql>  declare
       v_ename varchar2(5);
     begin
      select ename into v_ename from emp where empno = &no;
             dbms_output.put_line(v_ename);
      exception
  when value_error then
dbms_output.put_line('变量尺寸不足');
       end;


其它预定义例外

logon_denied
当用户非法登陆时,就会触发该例外
not_logged_on
如果用户没有登陆就执行dml操作,就会触发该例外
storage_error
如果超出了内存空间或是内存被损坏,就会触发该例外
timeout_on_resource
如果oracle在等待资源时,出现了超时就触发该例外


非预定义例外
非预定义例外用于处理与预定义例外无关的oracle错误。使用预定义例外只能处理21个oracle错误,而当使用pl/sql开发应用程序时,可能会遇到其它的一些oracle错误。比如在pl/sql块中执行dml语句时,违反了约束规定等等,在这样的情况下,也可以处理oracle的各种例外。


处理自定义例外
预定义例外和自定义例外都是与oracle错误相关的,并且出现的oracle错误会隐含的触发相应的例外;而自定义例外与oracle错误没有任何关联,它是由开发人员为特定情况所定义的例外。

?请编写一个pl/sql块,接收一个雇员的编号,并给该雇员工资增加1000元没如果该雇员不存在,请提示。
-- 自定义例外
sql>  create or replace procedure ex_test(pNo number) is
       -- 定义一个例外
       myex exception;
       begin
   -- 更新用户sal
   update emp set sal = sal + 1000 where empno = &no;
   -- sql%notfound表示没有更新成功
   -- raise myex; 表示触发myex
   if sql%notfound then
       raise myex;
       end if;
       exception
     when myex then
  dbms_output.put_line('没有更新任何用户');
   end;

oracle视图
视图是一张虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的表,并且在引用视图时动态生成。

视图与表的区别
表需要占用磁盘空间,视图不需要
视图不能添加索引
使用视图可以简化复杂查询
视图有利于提高安全性(不同用户查看不同视图)

创建视图
create view 视图名 as select 语句 [with read only] --> 表示该视图为只读

创建或修改视图
create or replace view 视图名 as select 语句 [with read only]

删除视图
drop view 视图名

-- 创建视图,把emp表的sal < 1000的雇员 映射到该视图(view)
sql>  create view myView as select * from emp where sal < 1000;
sql>  select * from myView;
-- 为简化操作,用一个视图解决 显示雇员编号,姓名和部门名称。
sql>  create view myView2 as select emp.empno, emp.ename, emp.dname    from emp, dept where emp.deptno == dept.deptno;
sql>  select * from myView2;
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics