当前位置:   article > 正文

oracle

oracle

一、           Introduction

1.       Oracle的特点

Oracle   Database,又叫做Oracle      RDBMS,即oracle关系型数据管理系统。和mysql一样同属于甲骨文公司的关系数据库管理系统。它提供开放的、全面的、近乎完整的信息管理,是目前世界上流行的数管系统,可移植性好、使用方便、功能强大、使用机器环境广、效率高、可靠性好,使得它在数据库领域一直处于领先地位。Oracle数据库由一个oracle数据库和多个oracle实例组成,其中oracle数据库是硬盘上的文件,oracle实例是读取到内存中的镜像。开发人员通过操作实例来操作数据库。数据库和实例可以是1对1的关系,也可以是1对多的关系,如果是1对多的关系就是数据库的集群。

集群就是指多个内存的镜像,优点是1,load balance,负载均衡,将大量的访问量平均到集群中的多个实例上,缓解处理访问的压力;2,fail over,失败迁移,当某个实例在处理访问需求时,如果出现异常失败,则可以迁移到其他实例继续处理,提高了可靠性。也提高了访问的安全性,只要多个内存镜像中有一个可用,则可以访问数据库。Oracle的集群专业上称为RAC,real application  cluster。

2.       Oracle的高性能受益于oracle的体系结构。

数据库文件的组成,一个数据库文件中,以.dbf为后缀的文件,称为数据文件,是数据库的物理存储单位。表空间,是oracle对物理数据库上相关数据文件的逻辑映射。一个数据库在逻辑上被划分称一到若干个表空间,每个表空间包含了在逻辑上相关联的一组结构,每个数据库至少有一个表空间。数据库的数据存储于表空间,实际上是在一个或者多个数据文件中,一个数据文件只能属于一个表空间。一旦数据文件被加入到某个表空间后,就不能删除这个文件。如果要删除这个数据文件,只能删除其属于的表空间才行。以.ctl为后缀的文件是控制文件。以.log为后缀的文件是日志文件。

操作数据库时,就是将数据库文件读到内存中,形成实例。实例通过操作系统的进程操作数据库文件,比如读和写的进程,还有很多其他进程。一个实例包括一个SGA,system global  area,系统的全局区和若干个PGA,proccess global  area,进程的全局去。每个pga对应一个客户端,也就是说客户端只能操作pga,不能操作sga。当客户端在pga的操作提交后,pga的操作就提交到sga,然后通过操作系统的进程提交到数据库。这种提交方式称为两阶段提交,由于客户端提交的pga统一提交到sga,再一次性提交到数据库,相比分别提交到数据库,就提高了效率。

客户端通过操作实例,来操作数据库。

3.       Oracle的用户

Oracle只有一个庞大的数据库,默认的名字是orcl,这个数据库可以有多个用户。每个用户下面有若个个表。比如scott用户,下面有4张表,SALGRADE(工资级别表),BONUS,DEPT(部门表),EMP(员工表)。

二、           Oracle数据库的安装

从官网下载oracle11g企业版,一路安装,在安装完成时,有一个口令管理页面,点击口令管理,接触scott和hr用户的锁定,并设置口令。安装完成。

在cmd中验证oracle是否安装成功。输入:sqlplus scott/tiger。空格后的是用户名和密码。出现版本信息则安装成功。exit,可以退出oracle数据库。如果使用的虚拟机安装的oracle数据库,则在本机连接虚拟机的oracle时,请输入命令:sqlplus scott/tiger@192.168.137.xxx:1521/orcl。@后面是虚拟机的ip地址,1521是连接orcal的端口号。

安装成功后,可以通过cmd命令查看一些oracle的服务的状态。Lsnrctl status 查看oracle监听器的状态。

三、           Sql和sqlplus命令的区别

在cmd命令行中,sql语句书写在sqlplus中,两者的区别是:

Sql

Sqlplus

一种语言

一种环境

ANSI标准

Oracle的特性之一

关键字不能缩写

关键字可以缩写

使用语句控制数据库中的表的定义信息和表中的数据

命令不能改变数据库中的数据的值

 

集中运行

另外,orcale  10g中有isqlplus,用来描述表结构,编辑和执行sql语句。是将sql保存在文件中并将sql语句执行结果保存在文件中,在保存的文件中执行sql语句,也可以将文本文件装入sqlplus编辑窗口。通过本地地址可以访问isqlplus,如http://localhost:5560/isqlplus/  但是,从11g之后就没有这个isqlplus的服务了。

四、           Oracle的基本查询

(一)基础查询语句

录屏开始:spool  d:\基本查询.txt

录屏结束:spool  off

注意:录屏期间不要退出sql,有结束命令,才生成屏录的文本。

查询当前用户:show  user

当前用户下的表:select  *  from tab;

查看某张表的结构:desc  emp

清屏命令:windows下:host cls;linux下:host clear

(二)基本查询语句

1.       查询所有信息:select *  from  emp;

2.       查询表的行宽:show linesize

3.       设置表的行宽:set linesize  100

4.       设置每页的行数:set pagesize  24

5.       设置表的列宽:如果列的字符为字符串,col ename  for  a8;如果列的字符为数字,col sal  for  9。

6.       执行上一条sql语句:/

7.       按照列名查询:select empno,ename,job,hiredate from  emp;

8.       修改写错的sql语句

1)       使用c命令

使用c命令修改写错的sql语句,适用于cmdlineInterface。

比如:select ename

form emp;

第2行的form写错了。那么首先输入2,然后输入c /form/from,然后输入/,再次执行。

2)       使用ed命令

输入ed,回车出弹出一个编辑页面,在这个页面修改好后直接保存,关掉就可以了。然后在cmd命令中,输入/执行修改的命令。

9.       Sql支持运算:select empo,ename,sal,sal*12  from  emp;

查询是给列取别名:select empno  as  “员工号”, ename “姓名”,sal  月薪  from emp;

关于别名3种方法的区别:as  “员工号”和  “姓名”两者没有区别;这两种方式与没有双引号的方式的区别是:没有引号的方式在字符之间输入空格或者sql的关键字会表示特定的含义,可能会导致sql语句错误。

如:select empno  as  “员工号”, ename “姓名”,sal  月   薪 from emp;或者select empno  as  “员工号”, ename “姓名”,sal  月select薪  from emp;会报错的。而select empno  as  “员工号”, ename “姓select名”,sal 月薪  from emp;并没有错。

10.   过滤重复关键字:distinct。如:select distinct  deptno  from emp;注意:distinct作用与后面所有的列,如:select distinct  deptno,sal  from emp;

11.   连接符:||。

||的作用是合成字符串。Sql语句中用单引号括住的是字符串,sql查询的结果,不论是字符、数字还是日期,也是字符串。另外,在sql语句中,日期或者字符只能在单引号中出现。而双引号中表示的是列的别名。

在mysql中有连接函数concat,oracle中也可以使用concat(),如果要连接的内容不在某个表中,可以使用oracle提供的dual表。Dual表又叫伪表。如:select concat(‘ll’,’aa’)  from  dual;

如果使用||,如:select ‘hh’||’aaaa’  哈哈  from dual;如:select ename||’的薪水是’||sal  薪水  from emp;

(三)条件查询和排序

1.       where关键字表示按照某个或某些条件查询

select  * from  emp  where deptno=10;

在where查询条件中,字符和日期都要包含在单引号中,字符的大小写敏感,日期的格式敏感,日期的默认格式是DD-MON-RR。

如查询名字是tom的:select *  from  emp where  ename=’tom’;

如查询入职日期是17-11月-81,即81年11月17号的员工。select *  from  emp where hiredate=’17-11月-81’;为了满足开发需求,有时需要修改查询的日期的格式,修改方式是:首先可以,显示oracle的一些参数,select *  from  v$nls_parameters;为了便于查看可以设置下行宽,set linesize  200;其中的NLS_DATE_FORMAT,表示日期格式的参数。然后,修改方式是:alter session  set  NLS_DATE_FORMAT=’yyyy-mm-dd’;修改完毕后,可是测试:select  *  from emp  where  deptno=’1981-11-11’;。注意上述修改中alter  session,表示在本次会话中有效,如果退出这次sql查询,即exit后,则修改的内容恢复默认,如果不想只是这次会话有效,可是使用alter system,表示在系统运行期间有效。

2.       where查询可以使用的比较运算符

操作符

含义

比较运算符

 

=

等于(不是==)

大于

>=

大于、等于

小于

<=

小于、等于

<> 

不等于(也可是!=)

:=

赋值

BETEWWN…AND…

在两个值之间(包含边界),注意:小值在前,大值在后。

IN(SET)

等于值列表中的一个或一些

NOT  IN

不在某个集合中

LIKE

模糊查询

IS  NULL

空值

逻辑运算

 

AND

逻辑并且

OR

逻辑或者

NOT

逻辑否定

注意:在模糊查询中,可以使用ESCAPE标识符,选择’%’和’_’符号。%表示一个或零个的任意字符,_表示任意一个字符。

比如:

  1. select  *  from emp  where  sal<=2000;
  2. select  * from  emp  where sal  between  1000  and  2000;
  3. select  * from  emp  where deptno  in(10,20);
  4. select  * from  emp  wehre ename  like  ‘%张%’;

注意:如果查询名字中含有下划线_的名字,则取like ‘%_%’是查不到的,那么,就需要使用转义字符\,要使用escape ‘\’声明转义字符。所以,正确的语句是:select *  from  emp where  ename  like  ‘%\_%’  escape ‘\’;

3.       排序

使用关键字order  by。默认为升序。降序使用desc。order by后面可以跟列,表达式,别名,序号(列的序号)。

1)       按照某列排序,如:select *  from  emp order  by  sal;

2)       按照表达式排序,如:select *  from  emp order  by  sal*12;

3)       按照别名排序,如:select sal*12  年薪  from emp  order  by  年薪  desc;

4)       按照序号排序,如:select ename,sal,sal*12  from  emp order  by  3 desc;

order  by也可以给多个列排序。如:select *  from  emp order  by  deptno,sal;select  * from  emp  order by  deptno,sal  decs;

总结:order  by作用于后面所有的列,先按照第一个列排序,然后后面的列;desc只作用离他最近的列。

有时候,根据前端业务的需求,需要将排序的null的列放到查询结果的后面,特别是在desc的时候,null会出现在最前面,因为在oracle中nul最大。Oracle提供了处理这个问题的语法,如:select *  from  emp order  by  comm  nulls  last;

五、           函数

数学上,函数是一种定义域与值域在对应法则下的关系式。软件中的函数也是指对应法则的关系式,简单的说就是一个计算方法,如求最大值,最小值,平均数等。软件中,使用函数为了简化操作。Sql中根据值域的行数,分为单行函数和多行函数。

(一)单行函数

1.       字符函数

1)       大小写控制函数

LOWER\UPPER\INITCAP

select  lower(‘helSDF’),小写  upper(‘llhsksl’)  大写,initcap(‘aaa’)  首字母大写  fromdual;

2)       字符控制函数

CONCAT:连接字符串。select  cancat(‘hlkj’,’ljs连接’)  连接 from  dual;

SUBSTR:求字符串的子串。

Substr(a,b):从a中的b位置开始取。select substr(‘ljlj sdf’,4)  子串  from dual;

Substr(a,b,c):从a中的b位置开始取c位。select substr(‘ljlj ljsljdf’,4,4)  子串  from dual;

LENGTH/LENGTHB:求字符串的长度。

       Length:字符数。Select length(‘aaa aaa’)  字符,lengthb(‘aaaaaa’)  字节 from  dual;

       Lengthb:字节数。Select length(‘aaa 啊啊啊’)  字符,lengthb(‘aaa啊啊啊’)  字节  from dual;

INSTR:在一个字符串中查找另一个字符串。Select  instr(‘ljlj’,’l’)  from dual;

LPAD/RPAD:左填充/右填充。Select  lpad(‘abc’,10,’abcd’),rpad(‘abc’,12,’dlgc’)  from dual;

TRIM:去掉前后指定的字符。Select  trim(‘a’  from  ‘aljslkdjfa’)  from dual;

REPLACE:替换为。Select  replace(‘abdcdsg’,’a’,’*’)  from dual;

2.       数值函数

ROUND:四舍五入。Select  round(22.222,2),round(22.343,1),round(22.343,0),round(22.343,-1),round(22.343,-2)  from dual;

TRUNC:截断。Select  trunk(22.232,2), trunk(22.232,1),trunk(22.232,0), trunk(22.232,-1), trunk(22.232,-2)  from dual;

MOD:求余。Select  mod(1400,300)  from dual;

3.       日期函数

关键字:sysdate,当前时间。Select sysdate  from  dual;

额外介绍,关于格式转换函数,将某个转换为一定的格式:select to_char(sysdate,’yyyy-mm-dd  hh24:mi:ss’)  from dual;

日期的+/-运算:select (sysdate-1)  昨天,sysdate  今天,(sysdate+1)  明天 from  dual;

利用日期的减法运算计算工龄:

       Select ename,hiredate,(sysdate-hiredate) 天,(sysdate-hiredate)/7 星期,(sysdate-hiredate)/30 月,(sysdate-hiredate)/365  年 from  emp;

MONTHS_BETWEEN:两个日期相差的月数。

       Select ename,hiredate,(sysdate-hiredate)/30 估计值,months_between(sysdate,hiredate)  准确值 from  emp;

ADD_MONTHS:向指定日期中加上若干月数。

       Select add_months(sysdate,111)  fromdual;

NEXT_DAY:指定日期的下一个日期。

       Select next_day(sysdate,’星期四’) from  dual;

       Next_day应用于oracle数据库的异地容灾部署。比如每周五将数据备份到异地的数据库。这里涉及到分布式数据库和触发器、快照的知识。

LAST_DAY:本月的最后一天。

       Select last_day(sysdate)  from  dual;

ROUND:日期四舍五入。Select round(systdate,’month’)  fromdual;select  round(sysdate,’year’)  from dual;

TRUNC:日期截断。Select  trunc(sysdate,’month’)  from dual;select  trunc(sysdate,’year’)  from dual;

4.       转换函数

在不同的数值类型之间完成转换。包括隐式和显式两种。隐式数据类型是oralce数据库自动转换的。

显式类型转换函数:

1)       TO_CHAR:将对象按照格式进行转换。

日期的格式列表:

格式

说明

例子

YYYY

年的全部数字

2011

YEAR

年的英文全称

Twenty ten

MM

月份以两位数字表示

04

MONTH

月的全称

4月

DY

星期几

星期二

DAY

星期几的全程

星期二

DD

月中的某天

02

将日期转换为字符串:Select to_char(sysdate,’yyyy-mm-dd hh24:mi:ss “今天是”day’) from  dual;

数字的格式列表:

9

数字

0

$

美元符

L

本地货币符号

.

小数点

,

千位符

查询员工的薪水:要求格式为两位小数、千位符、本地货币。Select to_char(sal,’L9,999.99’) from  emp;

2)       TO_NUMBER

将字符串转换成数字。To_number(char,’format_model’)

Select  to_number(‘¥234.234.23’,’L9,999.99’)  from dual;

3)       TO_DATE

将字符串转换成日期。To_date(char,’format_model’)

Select  to_date(‘1981-02-21’,’yyyy-mm-dd’)  from dual;

5.       通用函数

通用函数是适用与任何数据类型,也适用于空值的函数。

NVL:滤空函数。Select  nvl(comm,0)  from emp;

NVL2:带2的nvl是没有待nvl的增强。Nvl2(a,b,c),当a=null的时候,返回c;否则返回b。select nvl2(comm,comm,0)  from  emp;

NULLIF:nullif(a,b),判断参数是否相等,当a=b的时候,返回null;否则返回a。select nullif(‘ab’,’ab’),nullif(‘ab’,’ac’) from  dual;

COALESCE:coalesce(a,b,c,d,…,x),从左往右找到第一个不为空的值。Select coalesce(comm,sal)  from  emp;

6.       条件表达式

根据条件的不同,执行不同的操作,就形成了条件表达式。比如根据员工的职位涨不同的工资,列出涨前与涨后的报表。

两种方法,

一种是sql99的语法,case表达式,相对繁琐。

Case  expr when  expr1  then expr11  when  expr2 then  expr21  when expr3  then  expr31 else  exprx  end;

Select  ename,job,sal 涨前,case  job  when  ‘PRESIDENT’  then sal+1000  when  ‘MANAGER’ then  sal+600  else sal+100  end  涨后 from  emp;

另一种是使用oracle自己的语法,DECODE函数。

Decode(cod|expression,search1,result1,search2,result2,…,default)

Select  ename,job,sal 涨前,decode(job,’PRESIDENT’,sal+10,’MANAGER’,sal+5,sal+1)  涨后 from  emp;

(二)多行函数

多行函数,又叫做组函数或者分组函数,就是作用域一组数据,并返回一组数据的函数。一般,组函数会忽略空值,nvl函数可以保持组函数不忽略空值。

1.       常用的多行函数。AVG;COUNT;MAX;MIN;SUM。

1)       求员工总人数:select count(*)  from  emp;

2)       求员工的平均工资:select sum(sal)/count(*),avg(sal) from  emp;

3)       当函数的参数中含有null值时,

比如求员工的平均奖金:select sum(comm)/count(*),sum(comm)/count(comm),avg(comm)  from emp;其中正确算法是sum(comm)/count(*),其他两种修正后是:sum(comm)/count(nvl(comm,0);avg(nvl(comm,0))。

4)       在函数中使用distinct关键字,将distince写到括号里面。如:select count(distinct  deptno)  from emp;

2.       不常用的多行函数。WM_CONCAT(column)

行转列。Var_concat(column)

比如:select  deptno,wm_concat(ename)  namelist from  emp  group by  deptno;

3.       分组函数

使用group  by表达式。

总结查询中sql表达式的顺序,在一条sql中可能出现where条件表达式,group by表达式,order  by表达式,这些式子有一定的顺序。

即:select column,funciont()  from  table where  condition  group gy  expression  order by  column;

1)       求每个部门的平均工资,人数。Select count(ename),avg(sal)  from  emp gourp  by  deptno;

注意:在select列表中所有未包含在组函数中的列都应该包含在group by字句中。但是包含在group  by字句中的列不必包含在select列表中。如:select a,b,c,func(x)  from  emp group  by  a,b,c;select a,b,c,func(x)  from  emp group  by  a,b,c,d,e;

2)       按照多个列分组。按照部门中不同的职位求总工资。Select deptno,job,sum(sal)  from  emp group  by  deptno,job order  by  deptno;

多个列分组,先按照第一列分,如果相同,再按照第二个列分,以此类推。

4.       过滤分组

1)       过滤分组,是过滤分组后的结果,使用having子句。Having子句放在group by之后,order  by之前。如下格式:

select  column,funciont()  from table  where  condition group  gy  expression having  group_condition  order by  column;

2)       求每个部门的平均工资大于2000的。Select deptno,avg(sal)  from  emp group  by  deptno having  avg(sal)>2000;

3)       Having子句有点类似where子句,它们的区别是:where子句后不能使用多行函数。

某些查询下,where和having可以通用。查询20号部门的平均工资:select avg(sal)  from  emp where  deptno=20;select  avg(sal) from  emp  group by  deptno  having deptno=20;二者相比,使用where子句的性能更高。

4)       Group  by子句的增强。

有如下3条select语句。

  1. Select  deptno,sum(sal)  from emp  group  by deptno,job;
  2. Select  deptno,sum(sal)  from emp  group  by deptno;
  3. Select  sum(sal) from  emp;

使用group  by增强将上述3条sql语句加起来:

Select  deptno,job,sum(sal)  from emp  group  by rollup(deptno,job);

Group  by rollup(a,b)的作用相当于group by  a,b+group  by  a+没有group  by。

使用group  by的增强做出来的效果类似报表,也是说sqlplus支持报表查询。对于上述rollup语句的查询结果,使用break on  deptno  skip  2,可以使输出的格式更优化。其中,break  on表示相同的参数显示依次,skip  2,不同的deptno之间空2行。Break null,可以取消break  on的设置。

六、           多表查询

多表查询,是在多表中进行的查询。多表查询建立在笛卡尔积的基础上,通俗的说,笛卡尔积就是将两个表或多个表的所有行数相乘,列数相加得到一个新的表。然而,通过笛卡尔积得到的表并不能直接使用。多表查询以笛卡尔积表为基础,根据连接条件,从中筛选出需要的数据记录。多表查询的连接条件至少需要n-1个,n为表的个数。Oracle的多表查询有:等值连接,不等值连接,外连接和自连接4中类型。

(一)等值连接

凡是连接条件中间是=的,就是等值连接。如select *  from  emp  e,dept  d where  e.deptno=d.deptno;

(二)不等值连接

凡是连接条件中间不是=的,就是不等值连接,如select e.ename,e.sal,s.grade  from  emp  e,salgrade  s where  e.sal  between s.losal  and  s.hisal;

(三)外连接

举个例子:按部门统计员工信息,要求查出部门号、部门名称、人数。Select d.deptno  部门号,d.dname  部门名称,count(e.empno)  人数  from emp  e,dept  d where  e.deptno=d.deptno  group by  d.deptno,  d.dname;这个是使用等值连接,查询到的结果,这个结果是不对的,因为它不能显示出没有员工的部门的人数。所以,为了解决这个问题,要使用外连接。

外连接,意义是将某些连接条件下不成立的记录也包含在最后的结果中。外连接包含左连接和右连接。

左外连接,当连接条件where e.deptno=d.deptno不成立时,等号左边的表仍然包含在结果中。写法是:where e.deptno=d.deptno(+)

左外连接,当连接条件where e.deptno=d.deptno不成立时,等号右边的表仍然包含在结果中。写法是:where e.deptno(+)=d.deptno

使用外连接再次查询上述例子:Select d.deptno  部门号,d.dname  部门名称,count(e.empno)  人数  from  emp  e,dept d  where  e.deptno(+)=d.deptno  group by  d.deptno,  d.dname;

(四)自连接

自连接,就是本表连接本表。例子:查询员工姓名以及经理姓名。Select e.ename  员工,b.ename  经理 from  emp  e,emp b  where  e.mgr=b.empno;

通过上述例子可见,自连接可以认为是通过表的别名,将同一张表视为多张表。

自连接的笛卡尔积基础是表的行数的平方,如果表很大,那么,自连接的查询基础将相当大,所以,从效率的角度讲,自连接不适合做大表查询。

(五)层次查询

层次查询,可以解决自连接不适合做大表查询的问题。自连接的弊端在于多表查询依赖于笛卡尔积,而层次查询避免这个问题的关键之一就在于层次查询是单表查询。层次查询依据的是分叉树原理。

层次查询使用connect  by  …  start  with语法。同时使用oracle提供的伪列level,即表示层次的深度的列,如:select level,empno,ename,mgr  from  emp connect  by  prior empno=mgr  start  with mgr  is  null order  by  1;

层次查询也是一种优化的解决方案。然后,层次查询不能显示上述自连接那样直观的显示员工名和经理名的结果。

小结,每种查询都有优缺点,需要根据实际需求权衡使用。

七、           子查询

(一)子查询,就是以一个查询的结果为查询的范围的查询。

举例子如:查询工资比scott高的员工信息。首先需要查询到scott的工资是多少,通过select sal  from  emp where  ename=’SCOTT’;得到其工资3000,然后查询工资比3000高的工资,select *  from  emp where  sal>3000;这就需要两步来完成一个查询。Sql的查询规范允许将两步合为一条sql语句,就是子查询,语法是select list  from  table where  expr  (select list  from  table where  expr);。通过子查询实现上述查询:select  *  from emp  where  sal>(select  sal from  emp  where ename=’SCOTT’);。

子查询,也叫内查询,在主查询之前一次执行完成。子查询的结果被主查询使用。

(二)使用子查询时,请注意

1)、使用括号,2)、合理的书写格式(清晰的换行),3)、使用子查询的位置(where\having\select\from等后面),4)、特别注意不可以在group  by后面使用子查询,5)、主查询和子查询可以不是同一张表:只要子查询返回的结果,主查询可以使用即可,6)、一般不在子查询中排序,但在top-n分析中,必须对子查询排序,7)、注意from后面的子查询,8)、一般先执行子查询,再执行主查询,但相关子查询例外,9)、单行子查询只能使用单行操作符;多行子查询只能使用多行操作符,10)、子查询中的null。

(三)分别在where\having\select\from后面使用子查询

1.       Where

查询工资比scott高的员工信息:select *  from  emp where  sal>(select  sal from  emp  where ename=’SCOTT’);

2.       Select

Select后面使用子查询必须是单行子查询,不能是多行子查询。因为oracle是关系型数据库,采用二维表也就是行和列的形式保存数据,如果在select后面是多行子查询,也就是返回多行结果,则这个多行结果需要与其他每列的一行存在于一行中,则是不可行的,所以不能使用多行子查询。

Select  empno,ename,sal,(select  job from  emp  where empno=7839)  子查询  from  emp;

3.       Having

查询最低工资的部门比10号部门高的部门号及其最低工资。

Select  deptno,min(sal)  from emp  group  by deptno  having  min(sal)>(select  min(sal) from  emp  where deptno=20);

4.       From

查询员工号、姓名、月薪:Select  *  from (select  empno,ename,sal  from emp);

(四)主查询和子查询可以不是同一张表,只要子查询的返回结果,主查询可以使用即可

查询部门名称是sales的员工。Select ename  from  emp where  deptno=(select  deptno from  dept  where dname=’SALES’);

上述需求可以使用多表查询得到同样的结果。Select e.*   from  emp  e,dept d  where  e.deptno=d.deptno  and d.dname=’SALES’;

(五)一般不对子查询排序,但是在top-n分析中,必须对子查询排序

Top-n就是查出符合条件的前n个。如果不对子查询排序,就没有意义求的前n个了。

(六)一般先执行子查询,再执行主查询,但相关子查询例外

(七)单行子查询只能使用单行操作符,多行子查询只能使用多行操作符

根据返回结果,可以分为单行子查询和多行子查询,返回一行结果是单行子查询,返回多行结果是多行子查询。

1.       单行子查询

单行操作符

操作符

含义

=

等于

大于

>=

大于、等于

小于

<=

小于等于

<> 

不等于

例子如:

select ename,job,sal  from  emp where  job=(select  job from  emp  where empno=7566)  and  sal>(select  sal from  emp  where empno=7782);其中job后的=就是单行操作符,=后面的子查询只能是单行子查询。

错误使用单行子查询的例子:select empno,ename  from  emp where  sal=(select  min(sal) from  emp  group by  deptno);错误原因是单行子查询返回不止一行结果。

2.       多行子查询

多行操作符

操作符

含义

In

等于列表中的任何一个

Any

和子查询返回的任意一个值比较

all

和子查询返回的所有值比较

1)       使用in:查询部门名称是SALES和ACCOUNTING的员工。

Select  * from  emp  where deptno  in(select  deptno from  dept  where dname=’SALES’  or  dname=’ACCOUNTING’);这个需求可以通过多表查询解决。Select  *  from emp  e,dept  d where  e.deptno=d.deptno  and (d.dname=’SALES’  or  d.dname=’ACCOUNTING’);

2)       使用any:查询工资比30号部门任意一个员工高的员工信息

Select  * from  emp  where sal>any(select  sal  from emp  where  deptno=30);

可以使用min()实现上述需求。Select *  from  emp where  sal>(select  min(sal) from  emp  where deptno=30);

3)       使用all:查询工资比30号部门所有员工都高的员工信息

Select  * from  emp  where sal>all(select  sal  from emp  where  deptno=30);

可以使用max()实现上述需求。Select *  from  emp where  sal>(select  max(sal) from  emp  where deptno=30);

4)       多行子查询中的null

查询不是老板的员工信息。Select  *  from emp  where  empno not  in(select  mgr from  emp);实际上有结果,但是查询的没有结果。因为not  in后面的集合中有null值,not  in的运算规则是并且运算,即如果a  not  in(10,20,null)运算的过程是a!=10 and  a!=20  and a!=null,因为只有null!=null才为真,其他一些非null!=null都是假的,所以,a not  in(10,20,null)是假的,所以,上述多行子查询本应该有结果但是由于这个原因而没有结果。通过过滤掉not in中集合的null可以修改这条sql查询。Select *  from  emp where  empno  not in(select  mgr  from emp  where  mgr is  not  null);

查询是老板的员工信息。Select  *  from emp  where  empno in(select  mgr  from emp);有正确的结果。同样的,in后面跟集合也有它的运算规则,即如果a in(10,20,null)的运算过程是a=10 or  a=20  or a=null,显然这个运算会有结果的。

(八)集合运算

集合元算,实际上就是数学上的交集、并集、差集运算。

集合运算的语法字。

1.       并集:Union/union all

Select  * from  emp  where deptno=10  union  select *  from  emp where  deptno=20;

2.       集合运算的语法

Select  deptno,job,sum(sal)  from emp  group  by  deptno,job  union select  deptno,sum(sal)  from emp  group  by deptno  union  select sum(sal)  from  emp;

这条集合语句是错误的。这就涉及到集合运算需要注意的问题:

1)       参与运算的各个集合必须列数相同且类型一致。

2)       采用第一个集合作为最后的表头。

3)       Order  by永远在最后

4)       使用括号改变顺序。

根据集合运算的语法,可将上述错误的sql改为:select deptno,job,sum(sal)  from  emp group  by  deptno,job union  select  deptno,to_char(null),sum(sal)  from emp  group  by deptno  union  select to_number(null),to_char(null),sum(sal) from  emp;

3.       交集

Intersect。查询薪水位于级别1(700-1300)和级别2(1201-1400)的员工的信息。

Select  ename,sal from  emp  where sal  between  700 and  1300  intersect select  ename,sal  from emp  where  sal between  1201  and 1400;

4.       差集

Minus。查询薪水同时位于级别1(700-1300),但不属于级别2(1201-1400)的员工信息。

Select  ename,sal from  emp  where sal  between  700 and  1300  minus select  ename,sal  from emp  where  sal  between 1201  and  1400;

八、           Sqlplus中打开sql执行的时间

当两条或者多条sql语句都可以完成某个需求时,可以通过打开sqlplus中的sql执行时间查看效率。语法是:set timing  on。

比如对于这两条sql语句:

Select  deptno,job,sum(sal)  from emp  group  by rollup(deptno,job);执行用时00.01s。

select  deptno,job,sum(sal)  from emp  group  by deptno,job  union  select deptno,to_char(null),sum(sal) from  emp  group by  deptno  union select to_number(null),to_char(null),sum(sal) from  emp;同一台电脑执行用时00.02s。

可见使用group  by增强用时更短,效率更高。

通过set  timing  off可以关闭sql执行时间的显示。

九、           Sql语句解决null值问题

1.       包含null的表达式都为null

select  empno,ename,sal,sal*12,comm,sal*12+comm  from emp;

比如某部门的员工有月薪和奖金两列,有的员工有奖金,有的没有奖金(奖金列为null),如果计算年薪时加上奖金,则奖金列为null的员工的年薪为null。这是不合理的。

可以使用oracle的虑空函数。nvl和nvl2。

nvl(a,b),当a为null,返回b,否则返回a。

使用nvl后,select empno,ename,sal,sal*12,comm,sal*12+nvl(comm,0)  from emp;

2.       null永远!=null

select  * from  emp  where  comm=null;

比如上述查询奖金为空的员工,是查不到的,因为null值永远不等于null。

如果判断等于null,请使用is。

select  * from  emp  where comm  is  null;

3.       如果集合中含有null,不能使用not in,但可以使用in。

比如:select  *  from emp  where  deptno not  in(10,20,null);这是查不到任何结果的。

但是select  *  from emp  where  deptno in(10,20,null);

4.       null的排序

有时候,根据前端业务的需求,需要将排序的null的列放到查询结果的后面,特别是在desc的时候,null会出现在最前面,因为在oracle中nul最大。Oracle提供了处理这个问题的语法,如:select *  from  emp order  by  comm  nulls  last;

十、           sql的执行计划

查询sql执行计划的语法:explain plan  for  sqlsentence。

比如:解释计划,explain plan  for  select  * from  emp  where deptno=10;查看计划,select *  from  table(dbms_xplan.display);

十一、   sql的数据处理操作

按照操作的类型,sql语言包括:DML,

(一)DML,data  manipulation laguage  数据库操作语言,select insert  update  delete。

1.       Select

见前述。

2.       Insert

语法1:insert into  table(column,column,…)  values (value,value,…);

比如:insert  into  emp(empno,ename,sal)  values(100,’LKL’,200);

语法2:使用地址符&。Insert into  table(column,column,…)  values (&column,&column,…);

比如:insert into  emp(empno,ename,sal)  values(&empno,&ename,&sal);当执行后会需要依次输入相关列的值。

地址符&的用法不局限与insert,其他关键字中也可以使用,比如:select ename,sal,&t  from  emp;执行后,会要求输入列的值。

地址符&也可以用于表名。如:select *  from  &t;执行后,会要求输入表的值。

语法3:一次插入多条记录。

使用create  table  emp5 as  select  * from  emp  where 1=2;

需求:一次性将emp中,所有10号部门的员工插入emp5中。

可以在insert语句中使用子查询。语法:insert into  table(column,column,…)  select subtable  ….;或者insert  into table  select…;

上述需求的语句:insert into  emp5  select *  from  emp where  deptno=10;

如果需要海量插入数据,可以使用以下方法

1)       Oracle提供了数据泵(PLSQL程序)

Dbms_datapump(程序包)

2)       SQL*loader

3)       外部表

3.       Update

全部修改:update table  set  column=value,column=value,column=value,…。

局部修改:update table  set  column=value,column=value,…where…。

Update中也可以使用子查询。

Update  emp set  job=(select  job from  emp  where empno=7782),sal=(select  sal  from emp  where  empno=7782) where  empno=7934;

更新中可能会发生数据的完整性错误。数据的完整性指的是创建在表上的约束。

4.       Delete

语法:delete from  table  where  …。

在删除中如果不指定删除条件的话就会删除所有的数据。

Truncate  table删除数据。

Truncate和delete删除的区别:

1),delete是dml语句,truncate是ddl语句。Dml可以rollback,ddl不可以rollback。delete删除的数据可以rollback;

2),delete删除可能产生碎片,碎片会影响查询的速度,truncate不会产生碎片;

去掉碎片的方法:alter table  表名  move;数据的导入和导出,导出:exp/expdp,导入:imp/impdp。

3),truncate是先摧毁表结构,再重构表结构。

4),delete不释放空间,truncate会释放空间。

5),delete可以闪回(flashback),truncate不可以闪回。比如使用delete删除的数据提交了,不能rollback了,oracle中几乎所有的操作都是可逆的。

6),delete的删除速度比truncate快。原因是oracle的非常重要的功能undo数据(还原数据)。

特别注意:插入、更新和删除会引起数据的变化,必须考虑数据的完整性。

Set  feedback off:关闭回显。

5.       事务

1)       事务的概念略。Oracle中事务的提交是:commit,回滚是:rollback。

2)       Oracle中事务的起始的标识是第一条dml语句。结束的标识是:提交,显式:commit;隐式:正常退出,ddl语句或者dcl语句。回滚的标识,显式:rollback,隐式:非正常退出,掉电,宕机。

3)       Oracle的保存点:使用savepoint。

比如:

  1. create  table testsavepoint(id  number,name  varchar(20));
  2. insert  into testsavepoint  values(1,’haha’);
  3. insert  into testsavepoint  values(2,’hlkj’);
  4. savepoint  a;
  5. insert  into testsavepoint  values(3,’lkjl’);
  6. rollback  to savepoint  a;

4)       事务的隔离级别

在oracle中只支持3种隔离级别,即READ COMMITED、SERIALIZABLE和READ ONLY。默认的隔离级别是READ  COMMITED。

设置隔离级别的语法:set transaction  read  only;设置隔离级别前需要先提交到没有提交的语句。

(二)DDL,data  definition language  数据定义语言,create  table,alter  table,truncate table,drop  table。Create\drop,view,sequence,index,synonyn(同义词)。

(三)DCL,data  control language  数据控制语言,grant(授权),revoke(撤销权限)

十二、   Sql优化的原则

1.       尽量使用列名,而不是*。因为如果是*,则数据库需要根据表来查询*号代表的是哪些列,这降低了效率。

2.       where语句解析顺序,从右往左。

如果是and连接时,根据从右往左判断的原则,尽量把可能为假的放在右边,当右边为假则省略往左的判断;如果是or连接时,同样根据这个原则,尽量把可能为真的放在右边,如果为真则省略往做的判断。

3.       如果where语句和having语句都可以使用的话,尽量使用where。

在sql语句中,where子句需要写在group by之前,having子句需要写在group by之后,这使得:Having是在分组的基础上过滤分组,它的逻辑是先分组再过滤;where语句正好相反,是先过滤再分组。假如数据库的数据很庞大,比如100万条,此时查询部门号为10的平均工资,如果先分组则需要先将这100万条数据分组,然后过滤出部门为10的;如果先过滤出部门为10的,则不需要对100万条进行分组,二则都是要基本相同的过滤工作的,但是分组的工作却因为过滤的前后执行有所不同。显然使用where先过滤,有助于提高性能。

4.       子查询和多表查询都可以使用的时候,尽量使用多表查询。

5.       尽量不要使用集合运算,因为集合运算会随着参与集合运算的集合变多而运算变多。

十三、   Sql练习题

1.       查询表中工资最高的前三名

获取前n名,需要使用到oracle里面的rownum伪列。Select rownum,ename,sal  from  emp where  rownum<=3  order by  sal;这条sql是错误的。

注意rownum的语法规则:1),rownum永远按照默认的顺序生成。就是这个表原来的顺序,即使排序后也是按照这个顺序。2),rownum只能使用<和<=,不能使用>和>=。

这是一个top-n分析的需求。首先需要对薪水进行排序,select *  from  emp order  by  sal desc;然后,将这个排好序的表放到from后面,作为排序的表。如:select  rownum,empno,ename,sal  from(select *  from  emp order  by  sal desc)  where  rownum<=3;

关于rownum只能使用<和<=,不能使用>和>=的例子,分页查询。比如查询每页4条,第3页就是行号从9到12,select rownum,empno,ename,sal  from  emp where  rownum>=9  and rownum<=12;因为oracle是行式数据库,取了第1行才能取第2行,数据库永远从1开始。所以,对于行号>或者>=某个数的永远不成立,所以查不到结果。那么,oracle的分页的方法是:select *  from  (select rownum  r,e1.*  from (select  *  from emp  order  by sal)  e1  where rownum<=8)  where  r>=5;实际上相当于,将最里面的行用外面的表套嵌住,从而使得伪劣称为套嵌表的实际列,然后就可以使用>或者>=来运算行。

Oracle中的表分为标准表、临时表和索引表。

关于临时表,有两种创建方式:1,create global  temporary  table temp;2,自动创建,比如排序的时候,这个时候就产生了行号的问题,当某个表排好序后就产生一个临时表,如果单独的只有一表,我们看到的是临时表的结果,然后临时表使用的行号依然是原表的行号而不是从1开始新排的,如果我们需要使用排好序的表,但是行号有不符合要求,那么,就可以套嵌一层表包裹住临时表,并显示行,这个时候就从1开始重新排行号。

临时表的作用是:当事务或者会话结束的时候,表中的数据自动删除。比如:

当事务结束的时候,会删除表的例子。

  1. Create  global temporary  table  temp1(id number,name  varchar(20));创建完成后oracle自动开启事务。
  2. Insert  into temp1  values(1,’haha’);
  3. Insert  into temp1  values(1,’sbsd’);
  4. Select  * from  temp1;
  5. Commit;
  6. Select  * from  temp1;

执行到这里,发现数据没有了。

2.       找到员工表中薪水大于本部门平均薪水的员工

Select  e.empno,e.ename,e.sal,t.avgsal  from  emp e,(select  deptno,avg(sal)  avgsal  from emp  group  by  deptno)  t  where e.sal>t.avgsal and  e.deptno=t.deptno;

这个查询,也可以使用相关子查询。

相关子查询,将主查询中的值作为参数传递给子查询。使用相关子查询的方法:select empno,ename,sal,(select avg(sal)  from  emp where  deptno=e.deptno)  avgsal from  emp  e where  sal>(select  avg(sal) from  emp  where deptno=e.deptno);

3.       统计每年入职的员工人数

不使用子查询的解答:

Select  count(*) total,sum(decode(to_char(hiredate,’yyyy’),’1980’,1,0))  “1980”, sum(decode(to_char(hiredate,’yyyy’),’1981’,1,0))  “1981”, sum(decode(to_char(hiredate,’yyyy’),’1982’,1,0))   “1982”,  sum(decode(to_char(hiredate,’yyyy’),’1987’,1,0))  “1987” from  emp;

使用子查询的解答:

Select  (select count(*from  emp)  total,(select  count(*from  emp  where to_char(hiredate,’yyyy’)=1980)  “1980”,(select  count(*from emp  where  to_char(hiredate,’yyyy’)=1981)  “1981”, (select  count(*from  emp  where to_char(hiredate,’yyyy’)=1982)  “1982”,(select  count(*from emp  where  to_char(hiredate,’yyyy’)=1987)  “1987”  from  dual;

十四、   使用DDL语句管理表

DDL语句是管理数据库的对象的语言。常用的数据库对象有:表、视图、序列、索引、同义词、存储过程、存储函数、触发器、包和包体。

(一)表

表是一种比较重要的数据库对象。

表空间,是Oracle数据库的逻辑单元。表空间由多个数据文件组成。表空间中可以创建表。表由系统分配存放在数据文件上。表和其他的数据对象都是存储在用户下的,这也是oracle数据库与其他数据库的区别。

创建表空间的语法:create tablespace  haha  datafile ‘d:\space1.dbf’  size  100m autoextend  on  next 10m;datafile指表空间对应的数据文件,size定义表空间的初始大小,autoextend on  自动增长,当表空间存储都占满时,自增,next指定一次自动增长的大小。创建表空间需要权限。

创建用户:create user  aa  identified by  bb  default tablespace  space1;identified  by  后边是用户的密码,default tablespace  后边是表空间。

1.       创建表

1)       语法:create table  [schema,]table  (column datatype  [default  expr],column datatype  [default  expr],…);default表示默认值。

例子:create table  test1  (id number,name  varchar(20));

2)       创建表的时候可以指定的列的类型。

数据类型

描述

Varchar2(size)

可变长字符数据

Char(size)

定长字符数据

Number(p,s)

可变长数值数据

Date

日期型数据

Long

可变长字符数据,最大可达到2G

clob

字符数据,最大可达到4G,比如文本

Raw  and  long  raw

原始的二进制数据

Blob

二进制数据,最大可达到4G,比如图片,视频

beile

存储外部文件的二进制数据,最大可达到4G

Rowid

行地址,伪列

关于rowid。Rowid是伪列,比如:select rowid,empno,sal  from  emp;通过行地址是可以找到对应的记录的。比如:select *  from  emp where  rowid=’xxx’;

3)       使用子查询创建表的语法:create table  tt(column,column,…)  as subquery;

create  table emp10  as  select *  from  emp where  1=2;上述的例子,条件为假,那么就只是复制了表结构,不复制表数据。

Create  table empinfo  as  select e.empno,e.ename,e.sal,e.sal*12 annsal,d.dname  from  emp e,dept  d  where e.deptno=d.deptno;

2.       修改表

1)       增减新列

Alter  table test1  add  photo blob;

2)       修改列

Alter table  test1  modify name  varchar2(40);

3)       删除列

Alter table  test1  drop column  photo;

4)       重命名列

Alter table  test1  rename column  name  to usern;

5)       重命名表

Rename  test1 to  test2;

3.       删除表

1)       语法:Drop table  t;

查询当前用户已经存在的表。Select  *  from tab;

Drop并没有真正的将表删除掉,而是放到数据库的回收站了。查看回收站show recyclebin;清空回收站purge recyclebin;

Purge即不经过回收站清除表,如drop  table  t purge;

可以通过回收站中的被删除的表的名字,将原来表中的数据查询出。查询的表的名字加上双引号,如:select *  from  “BIN$ZErsyYZkTWGd/vBsdk30fg==$0”;

注意:使用回收站时,要知道,在oracle中管理员没有回收站。回收站只针对普通用户。

Oracle获取管理员的命令:sqlplus sys/1234  as  sysdba;sys是数据库名,1234是密码。这个用户名和密码可以是任意的。提供正确的用户名和密码(就是安装oracle时设置的密码)叫做密码认证。没有提供正确的用户名和密码的方式叫做主机认证,或者外部认证。主机认证的方式是通过设置计算机的本地用户组中的ORA_DBA的用户来修改。

Show  user,显示当前用户。

2)       使用闪回可以将oracle回收站中的数据恢复。

10g中闪回操作有6种,11g中闪回操作共有7种。

闪回删除。语法:flashback table  t  to before  drop;

闪回数据归档。语法:

闪回表。

闪回查询。

闪回版本查询。

闪回事务查询。

闪回数据库。

4.       约束

Oracle中约束的级别有两种,一个是列级约束,一个是表级约束。一般是指列级约束。表级约束的例子比如联合组件。常用的列级约束有主键约束(primary key),非空约束(not  null),唯一约束(unique),外键约束(foreign  key),检查性约束(check)。

1)       检查性约束

即检查约束的的列只能是某些情况,如性别只能是男或者女,使用英文的话,只能是male或者female。比如:create table  test2(id  number,name varchar2(20),gender  varchar2(2)  check  (gender  in(‘男’,’女’)),sal number  check(sal>0));插入数据检验:insert  into  test2 values(1,’haha’,’来’,-1);

2)       主键约束

主键约束在id上使用,本身默认不为空,在建表的时候指定。

如:create table  p(id  number(10) primary  key,name  varchar2(20)); 

注意:主键不可以重复,是系统自动分配的约束的名字。而且通过主键查询数据最快,因为主键是一个唯一性索引。

3)       非空约束

使用非空约束,可以指定字段不可以为空。

Create  table p2(id  number(10)  not null,name  varchar2(20)  not null);

4)       唯一约束

表中的一个字段的内容是唯一的。

Create  table p3(id number(10),name varchar(20)  unique);

5)       外键约束

外键是两张表的约束,可以保证关联数据的完整性。

Foreign  key:在子表中,定义了一个表级的约束。

References:指定表和父表中的列。

On  delete cascade:当删除父表时,级联删除子表记录。

On  delete set  null:将子表的相关依赖记录的外键置为null。

Create  table order2(id  number(10)  primary key,price  number(10,2));

Create  table order_detail(did  number(10),name  varchar(20),foreign  key  (did)  references order2(id));

注意:主表的外键必须是副表的主键。

创建约束的时候使用关键字:constraint emp_sal_min  …。Constraint的中文意思即约束。使用constraint创建约束后为约束定义名称,比如:create table  p4(id  number(10),gender  varchar2(2) check  (gender  in(‘男’,’女’)),sal number(10,2),constraint  p4_sal_min  check(sal>0));使用insert  into p4  (1,’非’,-11);测试使用约束关键字命名的效果。

使用所有约束的例子:

Create  table stu(sid  number  constraint stu_pk  primary  key,sname varchar2(20constraint  stu_name_notnull  not null,gender  varchar2(2constraint stu_gender  check(gender  in(‘男’,’女’)),email varchar2(40constraint  stu_email_uni unique  constraint  stu_email_notnull  not null,deptno  number  constraint stu_fk  references  dept(deptno) on  delete  set null);

使用insert插入数据检验:insert into  stu  values(1,’hah’,’男’,’hah@hah.com’,10);insert  into stu  values(2,’mil’, ’男’,’hah@hah.com’,10);

(二)视图

1)       视图就是封装了一条复杂查询的语句。视图是一个虚表。最大的优点就是简化复杂的查询。

语法:create  [or  replace] [force[noforce]  view  v [(alias,alias,…)]  as  subquery [with  check  option[constraint  cons]] [with  read  only[constraint  cons]];with read  only表示只读。

With  check option,表示操作视图中有的表的数据,而不能操作视图中没有的表的数。比如:create view  view10  as select  *  from emp  where  deptno=10 with  check  option;对于insert into  view10  (empno,deptno)  values(22,10);是正确的,但是insert into  view10  (empno,deptno)  values(23,20);是错误的。

创建使用视图需要管理员,即sys管理员授权。语句是:grant create  view  to scott;

比如:create  view  empinfoview as  select  e.empno,e.ename,e.sal,e.sal*12  annsal,d.dname  from emp  e,dept  d where  e.deptno=d.deptno;

创建视图后,查看视图:desc  empinfoview;查看视图中的数据:select *  from  empinfoview;

2)       不建议通过视图对表中的数据进行修改,因为会受到很多限制。

这些限制有:当视图定义中包含组函数、group by、distinct、rowmun时不能使用delete;当视图定义中包含组函数、group by、distinct、rowmun、列的定义为表达式时不能使用update;当视图定义中包含组函数、group by、distinct、rowmun、列的定义为表达式、表中非空的列在视图定义中未包括时不能使用insert;所以,尽量不要使用视图做这些修改操作。可以使用with read  only屏蔽对视图的dml操作。

3)       删除视图

删除视图只能删除视图的定义,并不会删除基表的数据。Drop view  v1;

(三)序列

序列可供多个用户用来产生唯一数值的数据库对象。作用:自动提供唯一的数值,共享对象,主要用于提供主键值,将序列值装入内存可以提高访问效率。效率方面,因为序列实际是一个数组,而且是存放在内存中的,访问内存比访问硬盘要快,所以效率高。

在很多数据库中都存在一个自动增长的列,比如mysql中的auto_increment,如果现在要想在oracle中完成自动增长的功能,则只能依靠序列完成,需要用户手工完成处理。并且oracle将序列值装入内存可以提高访问效率。

语法:create sequence  sequence  [increment by  n]  [start with  n]  [{maxvlues n|nomaxvalue}] [{cycle|nocyle}]  [{cache   n|nocache}];

比如:create sequence  dept_id  increment by  2  start with  122  maxvalue 300  nocache  nocycle;

创建一张表测试序列。Create table  testseq(id  number,name varchar(20));

序列提供了一下两个操作:nextval,取得序列的下一个内容;currval,取得序列的当前内容。在插入数据时需要自增的主键可以这样使用:insert into  testseq  values(dept_id.nextval,’哈哈’);

序列可能产生裂缝(断续的序号)的原因:回滚,系统异常,多个表共用一个序列。系统异常,比如停电,因为序列在内存中,如果停电,序列就没了。

(四)索引

索引是用于加速数据存取的数据对象。类似于书的目录。合理的使用索引可以大大降低i/o次数,从而提高数据访问性能。索引分为单列索引和多列索引。索引表中放的是rowid,行地址。

1.       单列索引

单列索引就是基于单个列建立的索引。语法:create index  in1  on table1(column1,…);

比如:create index  myindex  on  emp(deptno);

2.       复合索引

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

适合创建索引的情况:列中数据值分布范围很广,列经常在where子句或连接条件中出现,表经常被访问而且数据量很大,访问的数据大概占数据总量的2%到4%。

不适合创建索引的情况:表很小,列不经常在where子句或连接条件中出现,查询的数据大于 2%到4%,表经常更新。

(五)同义词

同义词就是别名的意思。

语法:Create [public]  synonym  synonym for  object;使用public,表示这个同义词是共有同义词,就是所有人都可以用的。

比如:作为scott用户是不能直接就访问hr用户里面的表的,但是sys授权后就可以了。语法:grant select  on  hr.employees to  scott;

授权之后,scott就可以访问hr的表了。Select count(*)  from  hr.employees;那么,给hr.employees起的别名叫做同义词。Create synonym  hrem  for hr.employees;同样创建同义词的时候需要管理员授权,语法:grant create  synonym  to scott;授权成功后就可以创建同义词。同义词的意义就是用户透明,提高用户数据的安全性。

十五、   PL/SQL编程语言

(一)简介

Procedure  language/sql,plsql是oracle对sql语言的过程化扩展,指在sql命令语言中增加了过程处理语句(如分支、循环等),是sql语言具有过程处理能力。把sql语言的数据操作能力与过程语言的数据处理能力结合起来,使得plsql面向过程但比过程语言简单、高效、灵活和实用。

举例说明:根据职位给员工涨工资。

如果使用jdbc操作:

  1. ResultSet  rs=select empno,job  from  emp”;
  2. While(rs.next()){
  3.        Int eno=rs.getInt(“empno”);
  4.        String job=rs.getString(“job”);
  5.        If(“PRESIDENT”.equals(job){
  6.               Update  emp set  sal=sal+1000  where empno=eno;
  7.        }else if(“MANAGER”.equals(job){
  8.               Update  emp set  sal=sal+500  where empno=eno;
  9.        }else{
  10.               Update  emp set  sal=sal+100  where empno=eno;
  11.        }
  12. }

但是我们要使用plsql来完成这些使用sql难以完成的操作,因为plsql是对sql的扩展形成的程序,它比java要快,效率要高;plsql关系到其他数据库对象存储过程等。

Plsql例子:

  1. Declare
  2.        --说明部分
  3. Begin
  4.        --程序主体
  5.        Dbms_output.put_line(‘hhhll’);
  6. End;
  7. /

默认情况下oracle的输出开关是关闭的,打开语法是:set serveroutput  on。

在命令行可以查看plsql中的程序包的简介。语法:desc dbms_output。另外也可以从oracle官网下载相关的学习资料。

为了方便和生产,需要使用plsql  developer之类的ide进行plsql的开发操作。

请安装pl/sql develeper。

(二)语法

1.       语法结构

  1. Declare
  2.        --说明部分(变量说明,光标申明,例外说明)
  3. Begin
  4.        --语句序列(dml语句)
  5. Exception
  6.        --例外处理语句
  7. End;
  8. /

2.       常量和变量的定义

变量类型:char、varchar2、date、number、boolean、long。

比如:var1 char(10);married  Boolean:=true;psal  number(7,2);

注意:plsql中的:=表示赋值,相当于java中的=,plsql中的=表示相等,相当于java中的==。

另外还以使用引用型变量和记录型变量。

引用型变量例子:my_name emp.ename%type;表示my_name的类型与emp表中ename列的类型一样。

变量的赋值可以通过:=,也是通过select 列名或表达式  into  变量。

  1. Declare
  2.        --定义变量保存姓名和薪水
  3.        Pname emp.ename%type;
  4.        Psal emp.sal%type;
  5. Begin
  6.        Select ename,sal  into  pname,psal from  emp  where empno=7839;
  7.        --注意into表示赋值,后面变量顺序与前面顺序一致。
  8.        Dbms_output.put_line(pname||’的薪水是’||psal);
  9. End;

记录型变量例子:emp_rec emp%rowtype;rowtype表示得到一行。可以通过变量名.列名,引用行中的列。如:emp_rec.ename。

  1. Declare
  2.        --定义记录型变量
  3.        Emp_rec emp%rowtype;
  4. Begin
  5.        Select *  into  emp_rec from  emp  where empno=7839;
  6.        Dbms_output.put_line(emp_rec.ename||’的薪水是’||emp_rec.sal);
  7. End;

3.       If语句

语法:

1)        

If  条件 then  语句1;

语句2;

End  if;

2)        

       If 条件  then  语句序列1;

       Else 语句序列2;

       End if;

3)        

If  条件  then  语句;

Elsif  语句  then  语句;

Else  语句;

End  if;

例子:判断用户从键盘输的数字。

  1. --接收键盘输入
  2. --变量num:是一个地址值,在该地址上保存了输入的变量的值
  3. Accept  num prompt  ‘请输入一个数字’;
  4. Declare
  5.        --定义变量的保存输入的数字
  6.        Pnum number  :=&num;
  7. Begin
  8.        If pmun  =  0 then  dbms_output.put_line(‘输入的是0’);
  9.               Elsif  pmun  =  1 then  dbms_output.put_line(‘输入的是1’);
  10.               Elsif  pmun =  2  then dbms_output.put_line(‘输入的是2’);
  11.               Else  dbms_output.put_line(‘其他’);
  12.        End if;
  13. End;

4.       循环

语法:

1)        

  1. While total<=2500
  2. Loop
  3. Total:=total+sal;
  4. End  loop;

2)        

  1. Loop
  2. Exit[when  条件];
  3. End  loop;

3)        

  1. For  I  in 1..3
  2. Loop
  3. 语句序列;
  4. End  loop;

其中1..3表示从1到3,一共3次。

例子:打印输出1到10。

  1. Declare
  2.        Pnum number:=1;
  3. Begin
  4.        Loop
  5.               --退出条件
  6.               Exit  when pnum>10;
  7.               Dbms_output.put_line(pnum);
  8.               Pnum:=pnum+1;
  9.        End loop;
  10. End;

(三)光标

也叫游标,可以存储查询返回的多条数据。

语法:cursor  游标名[(参数名  数据类型,参数名  数据类型,…)]  is  select …;

例如:cursor  c1  is select  ename  from emp;

光标的属性:1,%isopen 是否打开;2,%rowcount 影响的行数;3,%found 是否取到值;4,%found 是否没有取到值。

游标的使用步骤:1,打开游标:open c1;2,取一行游标的值:fetch  c1  into pjob;3,关闭游标:close  c1;4,游标的结束方式:exit  when  c1%notfound。注意:获取游标的值时,存放值的标量如pjob的数据类型必须和job的类型一致。如:pjob emp.empjob%type;

例子:查询和打印员工的姓名和薪水。

  1. Declare
  2.        --定义光标
  3.        Cursor cemp  is  select ename,sal  from  emp;
  4.        Pname emp.ename%type;
  5.        Psal emp.sal%type;
  6. Begin
  7.        Open cemp;
  8.        Loop
  9.               Fetch  cemp into  pname,psal;
  10.               Exit  when  cemp%notfound;
  11.               Dbms_output.put_line(pname||’的薪水是’||psal);
  12.        End loop;
  13.        Close cemp;
  14. End;

综合使用if,循环解决根据职位给员工涨工资的问题。

  1. Declare
  2.        Cursor cemp  is  select empno,job  from  emp;
  3.        Pempno emp.empno%type;
  4.        Pjob  emp.job%type;
  5. Begin
  6.        Open cemp;
  7.               Loop
  8.                      Fetch  cemp into  pempno,pjob;
  9.                      Exit  when cemp%notfound;
  10.                      If  pjob= ‘PRESIDENT’  then update  emp  set sal=sal+1000  where  empno=pempno;
  11.                      Elsif  pjob=’MANAGER’  then update  emp  set sal=sal+500  where empno=pempno;
  12.                      Else  update emp  set  sal=sal+100 where  empno=pempno;
  13.                      End  if;
  14.               End  loop;
  15.        Close cemp;
  16.        Commit;
  17.        Dbms_output.put_line(‘完成’);
  18. End;

Orcale的默认隔离级别是read  committed。读提交,表示如果当下的事务不提交,另外的事务是不能读取到当下事务的结果的。所以,这里要提交后才能查询到涨后的结果。上面的事务要提交。

关于使用光标的参数的例子。

查询某个部门的员工姓名。

  1. Declare
  2.        --形参
  3.        Cursor  cemp(dno numberis  select ename  from  emp where  deptno=dno;
  4.        Pname emp.ename%type;
  5. Begin
  6.        --实参
  7.        Open cemp(10);
  8.        Loop
  9.               Fetch  cemp into  pename;
  10.               Exit  when cemp%notfound;
  11.       
  12.               Dbms_output.put_line(pname);
  13.        End loop;
  14.        Close cemp;
  15. End;

(四)例外

例外是程序提供的一种功能,用来增强程序的健壮性和容错性。

1.       系统定义的异常有如下:

No_date_found

没有找到数据

Too_many_rows

Select…into  语句匹配多个行

Zero_divide

被零除

Value_error

算数或转换错误

Timeout_on_resource

在等待资源时发生超时

注意:timeout_on_resource这个异常比较典型的例子就是在分布式数据库中。分布式数据库数据物理上被存放在网络的多个节点上,逻辑上是一个整体。

例子:被0除的异常。

  1. Declare
  2.        Pnum number;
  3. Begin
  4.        Pnum:=1/0;
  5. Exception
  6.        When zero_divide  then  dbms_output.put_line(‘10不能做分母’);
  7.                                                         Dbms_output.put_line(‘20不能做分母’);
  8.        When value_error  then  dbms_output.put_line(‘算数或者转换错误’);
  9.        When others  then  dbms_output.put_line(‘其他例外’);
  10. End;

2.       用户也可以自定义异常,在声明中来定义异常。以变量的形式定义异常。

语法:

  1. Delare
  2.        My_job char(10);
  3.        V_sal emp.sal%type;
  4.        No_data exception;
  5.        Cursor c1  is  select distinct  job  from emp  order  by job;

如果遇到异常就抛出raise  no_data;raise相当与java中的throw,抛出异常。

例子:查询50号部门的员工。

显然,没有50号这个部门,所有查询的结果是没有的。

  1. Declare
  2.        Cursor cemp  is  select ename  from  emp where  deptno=50;
  3.        Pname emp.ename%type;
  4.        No_emp_found  exception;
  5. Begin
  6.        Open cemp;
  7.        Fetch cemp  into  pname;
  8.        If cemp%notfound  then     Raise no_emp_found;
  9.        End if;
  10.        Close cemp;
  11. Exception
  12.        When no_emp_found  then  dbms_output.put_line(‘没有找到员工’);
  13.        When others  then  dbms_output.put_line(‘其他例外’);
  14. End;

(五)实例

解决问题的基本思路:需求分析>>概要设计、详细设计>>编码>>测试>>上线>>维护。通常称这个模型为瀑布模型。

1.       统计每年入职的员工人数

思路:需要什么sql语句?需要哪些变量,变量的初始值,变量最终的值如何得到?

Sql:所有员工入职的年份,select to_char(hiredate,’yyyy’) from  emp。

那么,得到一个集合,这就需要光标,然后就需要循环,退出条件是notfound。

变量:count80 number:=0;然后在循环中判断光标的值。其他年份类似。

解答:

  1. Declare
  2.        Cursor cemp  is  select to_char(hiredate,’yyyy’) from  emp;
  3.        Phiredate varchar2(4);
  4.        Count80 number:=0;
  5.        Count81 number:=0;
  6.        Count82 number:=0;
  7.        Count87 number:=0;
  8. Begin
  9.        Open cemp;
  10.        Loop
  11.               Fetch  cemp into  phiredate;
  12.               Exit  when cemp%notfound;
  13.               If phiredate=1980’  then  count80:=count80+1;
  14.               Elsif  phiredate=1981’  then count81:=count81+1;
  15.               Elsif  phiredate=1982’  then count82:=count82+1;
  16.               Else  count87:=count87+1;
  17.               End  if;
  18.        End loop;
  19.        Close cemp;
  20.        Dbms_output.put_line(‘total:’||(count80+count81+count82+count87));
  21.        Dbms_output.put_line(‘1980:’||count80);
  22.        Dbms_output.put_line(‘1981:’||count81);
  23.        Dbms_output.put_line(‘1982:’||count82);
  24.        Dbms_output.put_line(‘1987:’||count87);
  25. End;

2.       为员工涨工资。从最低工资调起没人涨10%,但是工资总额不能超过5万员,请计算涨工资的人数和涨工资后的工资总额,并输出涨工资人数及工资总额。

Sql:select  empno,sal from  order  by sal;

光标,循环,退出:总额>5万或者涨完了。

变量:涨工资的人数:countemp number:=0;涨后的工资总额:saltotal number:=两种方法(1)select sum(sal)  into  salTotal from  emp;(2)涨后=涨前+sal*0.1。在对比多个方法中,哪个方法好的时候,遵循的原则就是效率和安全,在这里,显然不访问数据库是更高效和安全的,所以(2)方法更好。

解答:

  1. Declare
  2.        Cursor cemp  is  select empno,sal  from  emp order  by  sal;
  3.        Pempno emp.empno%type;
  4.        Psal emp.sal%type;
  5.        Countemp number:=0;
  6.        Saltotal number;
  7. Begin
  8.        Select sum(sal)  into  saltotal from  emp;
  9.        Open cemp;
  10.        Loop
  11.               Exit  when saltotal>50000;
  12.               Fetch  cemp  into pempno,psal;
  13.               Exit  when cemp%notfound;
  14.               If (saltotal+psal*0.1)>50000  then
  15.               Update  emp set  sal=sal*1.1  where empno=pempno;
  16.               Countemp:=countemp+1;
  17.               Saltotal:=saltotal+psal*0.1;
  18.               Else  exit;
  19.               End  if;
  20.        End loop;
  21.        Close cemp;
  22.        Commit;
  23.        Dbms_output.put_line(‘人数是:’||countemp||’总额是:’||saltotal);
  24. End;

3.       用pl/sql语言编写一个程序,实现按部门分段(6000以上,(6000,3000)、3000元一下)统计各工资段的职工人数、以及各部门的工资总额(工资总额中不包括奖金)。

为了得到一定的结果格式的持久化,需要先创建一张表。Create table  empsal(deptno  number,count1 number,count2  number,count3  number ,saltotal  number);

Sql:得到部门:select  deptno  from dept;光标,一层循环。

部门中员工中的薪水:select  sal  from emp  where  deptno=?;带参数的光标,内层循环。

变量:count1 number;count2  number;count3  number;

部门的工资总额:saltotal number:=0;最终值有两种方法:(1)select sum(sal)  into  saltotal from  emp  where deptno=?(2)累加。

解答:

  1. Declare
  2.        Cursor cdept  is  select deptno  from  dept;
  3.        Pdeptno dept.deptno%type;
  4.       Cursor  cemp(dno numberis  select sal  from  emp where  deptno=dno;
  5.        Psal emp.sal%type;
  6.        Count1 number;count2  number;count3  number;
  7.        Saltotal number:=0;
  8. Begin
  9.        Open cdept;
  10.        Loop
  11.               Fetch  cdept into  pdeptno;
  12.               Exit  when cdept%notfound;
  13.               Count1:=0;count2:=0;count3:=0;
  14.               Select  sum(sal) into  saltotal  from emp  where  deptno=pdeptno;
  15.               Open  cemp(pdeptno);
  16.               Loop
  17.                      Fetch  cemp into  psal;
  18.                      Exit  when cemp%notfound;
  19.                      If  psal<3000 then  count1:=count1+1;
  20.                             Elsif  psal>=3000 and  psal<6000  then count2:=count2+1;
  21.                             Else  count3:=count3+1;
  22.                      End  if;
  23.               End  loop;
  24.               Close  cemp;
  25.               Insert  into empsal  values(pdeptno,count1,count2,count3,nvl(saltotal,0));
  26.        End loop;
  27.        Close cdept;
  28.        Dbms_output.put_line(‘完成’);
  29. End;

十六、   建立在plsql基础上的数据库对象

(一)存储过程

是指存储在数据库中供所有用户程序调用的子程序。存储过程不能返回值。

1.       语法:

Create  [or replace]  procedure  过程名(参数列表)

       As

              Plsql子程序体;

其中as也可以是is。相当于plsql中的declare,用于定义变量或者光标等。

例子:

  1. Create  or replace  procedure  hahaha
  2. As
  3.        --说明部分
  4. Begin
  5.        Dbms_output.put_line(‘hahha’);
  6. End;

2.       调用存储过程

1)       使用excuate命令,缩写为exec,如exec hahaha();

2)       在plsql中调用存储过程,如begin hahaha();hahaha();  end;

3.       带参数的存储过程

例如:给指定的员工涨100元工资,并打印涨前和涨后的薪水。

  1. Create  or replace  procedure  raisesal(eno in  number
  2. As
  3.        Psal emp.sal%type;
  4. Begin
  5.        Select sal  into  psal from  emp  where  empno=eno;
  6.        Update emp  set  sal=sal+100 where  empno=eno;
  7.        Dbms_output.put_line(‘涨前是:’||psal||’涨后是:’||(psal+100));
  8. End  raisesal;

注意:参数表中的in,表示是输入参数。提交和回滚最好在调用中执行。

测试:使用plsql。Begin raisesal(7839);  raise(21);  end;  /

4.       练习

给指定的员工增加指定额度的工资(注意:传递多个参数)

  1. Create  or replace  procedure  raisesal2(eno in  number,rate  in number)
  2. Is
  3.        Psal emp.sal%type;
  4. Begin
  5.        Select sal  into  psal from  emp  where empno=eno;
  6.        Update emp  set  sal=sal+rate where  empno=eno;
  7.        Dbms_output.put_line(‘员工号是:’||emo||’涨前是:’||psal||’涨后是:’||(psal+rate));
  8. End;

(二)存储函数

函数是一个命名的存储过程,可带参数,并返回一个计算值。函数和过程的结构类似,但必须有一个returned子句。函数说明要指定函数名、结果值的类型,以及参数型等。

语法:

Create  [or replace]  function  函数名(参数列表)

Return  函数值类型

As

       Plsql子程序体;

例子:查询某个员工的年收入

  1. Create  or  replace  function annu(eno  in  number)
  2. Return  number
  3. Is
  4.        Psal emp.sal%type;
  5.        Pcomm emp.comm%type;
  6. Begin
  7.        Select sal,comm  Into  psal,pcomm from  emp  where empno=eno;
  8.        Return psal*12+nvl(pcomm,0);
  9. End  annu;

(三)过程和函数中的in和out

过程和函数都可以通过out指定一个或多个输出参数。使用out参数,在过程和函数中实现返回多个值。

例子:查询某个员工的姓名、薪水和职位。

  1. Create  or replace  procedure  empinfo(eno in  number,pname  out varchar2,psal  out  number,pjob out  varchar2)
  2. Is
  3. Begin
  4.        Select ename,sal,job  into  pname,psal,pjob  from emp  where  empno=eno;
  5. End  empinfo;

测试:

  1. Begin
  2.        Empinfo(eno =>21,
  3.                      Pname=>:panme,
  4.                      Psal=>:psal,
  5.                      Pjob=>:pjob);
  6. End;

注意:在plsql中=>也表示赋值。

Out参数使得过程和函数没有区别,但是为了能够兼容老的版本,函数依然被延用。

(四)在java中调用存储过程和存储函数

在jdbc中调用存储过程,需要使用的接口是CallableStatement。

java.sql
接口 CallableStatement

所有超级接口:

PreparedStatement, Statement, Wrapper

public interface CallableStatement
extends PreparedStatement

用于执行 SQL 存储过程的接口。JDBC API 提供了一个存储过程 SQL 转义语法,该语法允许对所有 RDBMS 使用标准方式调用存储过程。此转义语法有一个包含结果参数的形式和一个不包含结果参数的形式。如果使用结果参数,则必须将其注册为 OUT 参数。其他参数可用于输入、输出或同时用于二者。参数是根据编号按顺序引用的,第一个参数的编号是 1。

   {?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
   {call <procedure-name>[(<arg1>,<arg2>, ...)]}

IN 参数值是使用继承自PreparedStatementset 方法设置的。在执行存储过程之前,必须注册所有 OUT 参数的类型;它们的值是在执行后通过此类提供的 get 方法获取的。

CallableStatement 可以返回一个 ResultSet 对象或多个 ResultSet 对象。多个 ResultSet 对象是使用继承自 Statement 的操作处理的。

为了获得最大的可移植性,某一调用的 ResultSet 对象和更新计数应该在获得输出参数的值之前处理。

1.       Java中使用oracle存储过程和函数基本步骤:

1)       创建java项目,拷贝jar包到lib。Oracle的jdbc的支持jar包在安装oracle的目录里面。

2)       编写一个jdbcutils

  1. public class JDBCUtils {
  2.        private static Stringdriver="oracle.jdbc.OracleDriver";
  3.        private static Stringurl="jdbc:oracle:thin:@localhost:1521/orcl";
  4.        private static Stringuser="scott";
  5.        private static Stringpassword="tiger";
  6.        static{
  7.               try {
  8.                      Class.forName(driver);
  9.               } catch(ClassNotFoundException e) {
  10.                      e.printStackTrace();
  11.               }
  12.        }
  13.        public static ConnectiongetConnection(){
  14.               try {
  15.                      returnDriverManager.getConnection(url, user,password);
  16.               } catch(SQLException e) {
  17.                      e.printStackTrace();
  18.               }
  19.               return null;
  20.        }
  21.        public static voidrelease(Connection conn,Statement st,ResultSet rs){
  22.               if(rs!=null){
  23.                      try {
  24.                             rs.close();
  25.                      } catch(SQLException e) {
  26.                             e.printStackTrace();
  27.                      }finally{
  28.                             rs=null;
  29.                             //置空的原因是为了使得垃圾回收机制回收它。
  30.                             //java的gc不受代码的控制,不能通过java代码干预gc。
  31.                      }                   
  32.               }
  33.               if(st!=null){
  34.                      try {
  35.                             st.close();
  36.                      } catch(SQLException e) {
  37.                             e.printStackTrace();
  38.                      }finally{
  39.                             st=null;
  40.                      }                   
  41.               }
  42.               if(conn!=null){
  43.                      try {
  44.                             conn.close();
  45.                      } catch(SQLException e) {
  46.                             e.printStackTrace();
  47.                      }finally{
  48.                             conn=null;
  49.                      }                   
  50.               }
  51.        }
  52. }

3)       编写测试代码

  1. public class TestOracle {
  2.    @Test
  3.    public voidtestProcedure(){
  4.       //{call<procedure-name>[(<arg1>,<arg2>, ...)]}
  5.       String sql="{callempinfo2(?,?,?,?)";
  6.       Connection conn=null;
  7.       CallableStatement cs=null;
  8.       try{
  9.          conn=JDBCUtils.getConnection();
  10.          cs=conn.prepareCall(sql);
  11.          //对于输入参数赋值
  12.          cs.setInt(1, 7839);
  13.          //对于输出参数,需要申明
  14.          cs.registerOutParameter(2, OracleTypes.VARCHAR);
  15.          cs.registerOutParameter(3, OracleTypes.NUMBER);
  16.          cs.registerOutParameter(4, OracleTypes.VARCHAR);
  17.          cs.execute();
  18.          String name=cs.getString(2);
  19.          double sal=cs.getDouble(3);
  20.          String job=cs.getString(4);
  21.          System.out.println(name+"--"+sal+"--"+job);
  22.       }catch(Exception e){
  23.          e.printStackTrace();
  24.       }finally{
  25.          JDBCUtils.release(conn, cs, null);
  26.       }
  27.    }
  28.   
  29.    @Test
  30.    public voidtestFunction(){
  31.       //{?= call<procedure-name>[(<arg1>,<arg2>, ...)]}
  32.       String sql="{?=callannu(?)}";
  33.       Connection conn=null;
  34.       CallableStatement cs=null;
  35.       try{
  36.          conn=JDBCUtils.getConnection();
  37.          cs=conn.prepareCall(sql);
  38.          cs.registerOutParameter(1, OracleTypes.NUMBER);
  39.          cs.setInt(2, 7839);
  40.          cs.execute();
  41.          double annu=cs.getDouble(1);
  42.          System.out.println(annu);
  43.       }catch(Exception e){
  44.          e.printStackTrace();
  45.       }finally{
  46.          JDBCUtils.release(conn, cs, null);
  47.       }
  48.    }
  49. }

(五)包结构、包体

包结构,将光标、过程或者函数等对象打包,用来简化操作。包结构分为包头和包体。包结构和包体是另外两个常用的oracle对象。

使用关键子type可以自定义一个类型。

语法例子:

--包头

  1. Create  or replace  package  mypackage
  2. as
  3.        type empcursor  is  ref cursor;
  4.        procedure queryemplist(dno  in  number,emplist  out empcursor);
  5. end  mypackage;

--包体

  1. Create  or replace  package  body mypackage
  2. As
  3. Procedure  queryemplist(dno  in number,emplist  out  empcursor)
  4.        As
  5.        Begin
  6.               Open  emplist for  select  * from  emp  where deptno=dno;
  7.        End  queryemplist;
  8. End  mypackage;

通过例子说明。

1.       查询某个员工的所有信息

2.       查询某个部门中的所有员工信息

查询所有信息,结果是一个集合。可以在out参数中使用光标。在out中使用光标可以通过申明包结构来简化操作。

  1. Create or  replace  package mypackage
  2. Is
  3.        Type empcursor  is ref  cursor;
  4.        Procedure queryemplist  (dno in  number,emplist  out empcursor);
  5.       
  6. End  mypackage;
  7. Create  or  replace  package body  mypackage  is
  8.        Procedure  queryemplist(dno  in number,emplist  out  empcursor)
  9.        As
  10.        Begin
  11.               Open  emplist for  select  * from  emp  where deptno=dno;
  12.        End;
  13. End  mypackage;

通过desc  mypackage可以查看程序包。

在java中调用程序包中的存储过程或者函数。

  1.    @Test
  2.    public voidtestpackage(){
  3.       String sql = "{callmypackage.queryemplist(?,?)";
  4.       Connection conn=null;
  5.       CallableStatement cs=null;
  6.       ResultSet rs=null;
  7.       try{
  8.          conn=JDBCUtils.getConnection();
  9.          cs=conn.prepareCall(sql);
  10.          cs.setInt(1, 10);
  11.          cs.registerOutParameter(2, OracleTypes.CURSOR);
  12.          cs.execute();
  13.          rs=((OracleCallableStatement)cs).getCursor(2);
  14.          while(rs.next()){
  15.             String name=rs.getString("ename");
  16.             double sal=rs.getDouble("sal");
  17.             System.out.println(name+"--"+sal);
  18.          }
  19.       }catch(Exception e){
  20.          e.printStackTrace();
  21.       }finally{
  22.          JDBCUtils.release(conn, cs, rs);
  23.       }
  24.    }

(六)触发器

1.       定义

数据库触发器是一个与表相关联的、存储的pl/sql程序。每当一个特定的数据操作语句(insert、update、delete)在指定的表上发出时,oracle自动地执行触发器中定义的语句序列。

例子:

每当插入新员工后,自动打印“成功插入新员工”。

  1. Create  trigger ftrigger
  2. After  insert
  3. On  emp 
  4. Declare
  5. Begin
  6.        Dbms_output.put_line(‘成功插入’);
  7. End;
  8. /

测试:insert into emp(empno,ename,sal,deptno) values(22,’hll’,200,10);

2.       触发器的类型。

语句级触发器,在指定的操作语句操作之前或之后执行一次,不管这条语句影响了多少行。

行级触发器(for  each  row),触发语句作用的一条记录都被触发。在行级触发器中使用:old和:new伪记录变量,识别值的状态。

3.       触发器的作用

数据确认、实施复杂的安全性检查,做审计、跟踪表上所做的数据操作等,数据的备份和同步。

审计的种类:强制审计、标准审计、基于值的审计(使用触发器的方式)、细粒度审计、管理员审计。

数据备份的例子:next_day函数的应用,每个星期一自动备份表中的数据,使用到分布式数据库,触发器或者快照。

4.       语法

Create  [or replace]  trigger  触发器名

{before|after}

{delete|insert|update[of  列名]}

On  表名

[for  each row  [where]]

Plsql子程序

5.       例子

1)       关于实施安全性检查的例子:禁止在非工作时间插入新员工。

非工作时间包括:周末,to_char(sysdate,’day’) in  (‘星期六’,’星期日’),to_number(to_char(sysdate,’hh24’))  not between  9  and  17。

  1. Create  or replace  trigger  securityemp
  2. Before  insert
  3. On  emp
  4. Begin
  5.        If  to_char(sysdate,’day’)  in  (‘星期六’,’星期三’)  or  to_number(to_char(sysdate,’hh24’))  not between  9  and  17  then
  6.        Raise_application_error(-20001,’不能在非工作时间插入新员工’);
  7.        End  if;
  8. End  securityemp;

创建触发器后,通过insert进行测试。

2)       关于数据确认的例子。

涨工资,要求涨后的薪水不能少于涨前的薪水。

这是一个行级触发器。

要使用:old和:new。:old表示操作某行前的值,:new表示操作某行后的值。

  1. Create  or replace  trigger  checksal
  2. Before  update
  3. On  emp
  4. For  each row
  5. Begin
  6.        If  :new.sal<:old.sal  then    
  7.               Raise_application_error(-20002,’涨后的薪水不能少于涨前的薪水。涨前:’||:old.sal||’涨后:’||:new.sal);
  8.        End  if;
  9. End  checksal;

测试,使用update。

十七、   典型面试题

1.       提供表,有姓名和工资,得出一个查询表,显示3列,姓名,工资,他前一行的人的工资。

关于提供的表:create  table  test1(id number(10),name varchar2(20),money  number);

考点,相关子查询。

Select  id,name,money,(select  money  from  test1  where id=t.id-1)  money1  from test1  t;

2.       提供两张表,一张是课程id和选了这个课的学生id,一张是学生id和学生姓名。要求查询出课程id和班级里的学生的姓名。

  1. create table pm_ci
  2.  (ci_id varchar(20) primary key,
  3.   stu_ids varchar(100));
  4. insert into pm_civalues('1','1,2,3,4');
  5. insert into pm_civalues('2','1,4');
  6.  create table pm_stu
  7.  (stu_id varchar(20) primary key,
  8.   stu_name varchar(20));
  9. insert into pm_stuvalues('1','张三');
  10. insert into pm_stuvalues('2','李四');
  11. insert into pm_stuvalues('3','王五');
  12. insert into pm_stuvalues('4','赵六');
  13. commit;

考点,多表查询。

解答:

  1. Select  ci_id,wm_concat(stu_name)  namelist 
  2. from(select  c.ci_id,s.stu_name
  3. from  pm_ci  c,pm_stu s
  4. where instr(c.stu_ids,stu_id)>0)
  5. group  by ci_id;
  6. wm_concat(),作用将n列数据放入1行中。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/584775
推荐阅读
相关标签
  

闽ICP备14008679号