PLSQL(二)
储存过程/存储函数 --procedure#什么是存储过程:#存储过程就是一段已经封装好了的 plsql代码片段, 这个代码片段提前已经编译好了,用户在调用的时候就不用再编译大大提高了 数据库的吞吐效率存储过程不编译是不能被调用的!!!语法:#create or replace procedure 存储过程的名(参数1 in|out 参数类型,.......)
is | as
-- 声明部分
begin
-- 业务逻辑
end;in 表示: 该参数是输入参数out 表示: 该参数是输出参数例子: 使用存储过程给员工涨工资,create or replace procedure pro_update_sal(vempno in number , vsal in number)
is
-- 声明部分
-- 存储涨工资之前的 工资
v_sal number ;
begin
-- 存储涨工资之前的工资
select sal into v_sal from emp where EMPNO=vempno;
-- 涨工资
update emp set SAL=v_sal+vsal where EMPNO=vempno;
-- 提交事务, 我们的存储过程里面的事务通常都是在里面直接提交的
commit;
exception
when others then rollback ;
end;一般存储过程中加上 事务的提交,和出现异常时回滚如何调用-- 调用方式一:
call pro_update_sal(7369,100);
-- 调用方式二:
declare
begin
pro_update_sal(7369,100);
end;
-- 方式三:
begin
pro_update_sal(7369,100);
end;带返回值的存储过程#create or replace procedure get_pro (v_no in number , total_sal out number)
is
begin
select SAL*12+ nvl(comm,0) into total_sal from EMP where EMPNO = v_no;
commit;
exception
when others then
rollback ;
end;不用显示的声明,声明返回值,把查询的结果赋值给 入参位置的out类型的参数就行,她自动的返回调用declare
t_sal number;
begin
get_pro(7369,t_sal);
dbms_output.put_line(t_sal);
end;传递进去两个参数,不需要我们写 t_sal = get_pro(7369,t_sal)(错误的写法) 她会自动的把数据传递进t_sal ,什么是存储函数#同样是一段封装在oracle 服务器中的一段plsql 代码片段, 他是已经编译好了的代码片段语法:create [or replace] function 存储函数的名称 (参数名 in|out 参数类型,参数名 in|out 参数类型.参数名 in|out 参数类型) return 参数类型
is | as
begin
end;例: 使用存储函数 查询指定员工的年薪create or replace function fun_get_empsal(v_empno number) return number
is
-- 声明变量 保存年薪
total_sal number;
begin
select sal*12 + nvl(comm,0) into total_sal from EMP where EMPNO=v_empno; -- nvl(p1,p2) 第一个字段为空,那么就取第二个字段的值
return total_sal;
end;调用declare
v_cal number;
begin
v_cal := fun_get_empsal(number);
dbms_output.put_line(v_cal);
end;标准sql调用 存储函数select ename, fun_get_empsal(EMPNO) from EMP;存储过程和存储函数的区别#其实他们本质上是没有区别的,两者都可以实现相互的功能语法上的区别3. 存储函数参数的小括号后面紧跟着 return语句4. 关键字 一个数 procedure 一个是 function存储函数大多数情况下是给存储过程调用的存储函数可以给标准的sql语句调用,但是存储过程不行触发器#什么是触发器 --trigger#Oracle 和 Mysql 中都有触发器,他是一段代码,用来监听用户对数据表的具体某个操作,可以在这个操作前后进行拦截,执行我们添加进去的操作分类#语句级触发器(表级别)操作无论影响多好行, 触发器执行一次行级触发器 -- 关键字 for each row操作影响多少行,触发多少次简单的触发器 语法#create or replace trigger -- 创建触发器
before | after -- 指定触发的时机
insert | delete | update -- 监听的操作
on 表名 -- 针对哪张表
[for each row] 时候针对每一行
declare
begin
end;例: 插入员工之后,输出一句话 XXX -- 每次有inset语句, 都会先执行我们的 输出XXXcreate or replace trigger trrigger_one
after
insert
on EMP
declare
begin
dbms_output.put_line('XXX');
end;行级触发器: for each row行级触发器有两个内置的对象:new 记录了表中某个字段更新前的值:old 记录表中的某个字段更新后的值在 on 表名 和 declare 之间添加 for each row , 使触发器升级成 行级触发器触发器的练习--- 模拟mysql的 id 自增长思路: 触发器监听用户对表的insert操作,每当监听到有插入操作的时候,就会使用我们的 序列自增1,给它补全-- 创建表
create table person(
id number primary key ,
name varchar2(10)
);
-- 创建序列--并编译执行
create sequence id_auto_increament;
-- 创建监听对person表 的insert操作的 触发器 -- 并编译执行
create or replace trigger trigger_insert
before
insert
on person
for each row
declare
begin
select id_auto_increament.nextval into :NEW.id from dual;
end;
插入数据
insert into person values(null,'长沙'); -- 成功插入注意点: :NEW这个内置对象只属于行级触发器-- create 关键字 :-- 创建表-- 创建view-- 创建index-- 创建synonym-- 创建存储过程-- 创建用户-- 创建触发器-- 创建序列
Oracle中如何写存储过程
Oracle中如何写存储过程
存储过程是什么? 存储过程是一种命名的PL/SQL程序块,输入、输出参数数量【0个或多个】,通常没有返回值【也有特殊的情况】。
运行SQL语句时,Oracle是要先编译再执行的;而存储过程是已经编译好的代码,所以被调用或引用时,执行效率非常高。
存储过程被保存在数据库中,但是不能被SQL直接执行或调用,只能通过EXECUT命令执行或在PL/SQL程序块内部被调用。
创建存储过程的语法格式:
123456CREATE [OR REPLACE] procedure pro_name[;number] [(parameter1[,parameter2]...] is|asbegin
plsql_sentences;
[exception]
[dowith_sentences;]
end [pro_name]; -->pro_name:存储过程的名称,如果数据库中已经存在了此名称,则可以指定"OR PLACE"关键字来覆盖原来的存储过程;如果不想覆盖,可在名称后面添加";number",number为随机数,作为区分存储过程的标签;例如:proc_tent;1 proctent;2 这样就可以区分开了
-->parameter1: 存储过程被调用或执行时用到的参数。注意这个值不是存储过程内定义的内部变量,内部变量应该在is|as之后定义
若是输入参数,则需要在后面指定IN关键字;若是输出参数,则需要在后面指定OUT关键字,在IN和OUT后面加的是参数的数据类型,不需要指定长度;【不明白不要紧,后面会举例说明;】
-->plsql_sentences:PL/SQL语句,存储过程功能实现的主体
-->dowith_sentences:异常处理语句,是可选的plsql语句
创建和运行简单的存储过程举个栗子----【创建一个存储过程,该存储过程实现向dept表中插入一条记录】(首先记得自己建一张表,这里不再演示了)
create or replace procedure pro_insertDept isbegininsert into dept values(77,'市场拓展部','8');commit;dbms_output.put_line('插入新纪录成功!');end pro_insertDept;
执行Execute之后,
从上面的结果看出,我们已经顺利的创建出了存储过程pro_insertDept。【如果没有创建成功的话,可以使用show error命令查看错误信息。】
》》如果在我们创建之前,就已经有这个同名的存储过程了,那么应该怎么处理呢?【两种方法】
①修改现有的存储过程名称,重新创建
②使用字段中“OR Replcae"关键字创建一个同名的存储过程覆盖已经存在的存储过程。实际使用不加中括号,这里只是表示可有可无;
表示已经存在的话覆盖创建存储过程;没有的话,直接创建存储过程。
-->如果现在使用Sql*PLUS去查询插入的记录,一定是不存在的
原因是,我们只是创建了存储过程而并没有执行,若想要执行的话,使用Execute关键字来执行存储过程;也可以简写”EXEC“
-->接下里我们尝试在PL/SQL块中调用此存储过程,之前先将上一条记录删除,因为字段ID为主键;
然后按照下面的方式调用即可;
注:set serverout on 意思是在窗口里显示服务器输出信息。
带参数的存储过程
①IN模式参数-------->“输入类型参数,参数右调用者传入,只能被存储过程读取,是默认的参数模式,也是最常用的”
举个栗子----【创建一个存储过程,并定义3个IN模式的变量,然后将这3个变量的值插入到dept表中,代码及运行结果如下。】
create or replace procedure insert_dept(
id in number,
dname in varchar2,
deptno in number) isbegininsert into deptvalues(id,dname,deptno);commit;end insert_dept;
创建存储过程成功! 【注意点:参数类型不能指定长度。】
在调用或执行IN模式的存储过程时,用户需要自己传参数,你传什么,它就使用什么去执行。
传参数的方式有三种:
1)指定名称传递-->参数名称在左,参数值在右,中间使用赋值符号"=>"连接:
1pro_name(parameter1=>value1[,parameter2=>value2]...) 还是用栗子比较容易懂,不太明白的,请看这里:(因为标注字段名称,所以,赋值时位置可以颠倒)
【在PL/SQL块中调用存储过程insert_dept,然后使用'指定名称'的方式向其传入参数数值,最后执行当前的PL/SQL块】
2)按位置传递(这种方式不用写字段名称,所以赋值顺序必须与字段标准顺序一致)
##因为有的时候参数过多,或者参数名称太长不好记,这种方法只要记住顺序就可以了,实在记不住,使用DESC查看;
3)混合方式传递(顾名思义:这是将前两者结合使用的)
执行方法,还可以使用Execute关键字执行,和上面使用的方法结果是一样的;
特别注意:因为在中间使用了"指名方式"传值,所以后面的参数都要使用指名方式;因为指名方式可能已经破坏了参数原始的定义顺序了.
②OUT模式参数----->"输出类型参数,表示这个参数在存储过程中已经被赋值,并且参数值可以传递到当前存储过程以外的环境中"
老规矩--看栗子(栗子说:"为什么老是看我 ~_~!!")
【创建一个存储过程,要求定义两个OUT模式的字符类型的参数,然后在dept表中检索到的一行部门信息存储到这两个参数中】
12345678910111213create or replace procedure select_dept_out(
m_id in number, --定义IN模式变量,要求输入人员编号
m_dname out dept.dname%type, --定义OUT模式变量,可以存储部门名称并输出
m_deptno out dept.deptno%type) is
begin
select dname,deptno
into m_dname,m_deptno
from dept
where id=m_id; --检索某个员工号的员工信息
exception when no_data_found then --如果select语句没有结果返回
dbms_output.put_line('该员工编号不存在'); --输出信息
end select_dept_out; 当调用或者执行以上存储过程时,都需要定义变量来保存这两个out参数,下面对OUT模式如何调用或执行分别举例子说明:
一》在PL/SQL块中调用OUT模式的存储过程:在PL/SQL块的DECLARE部分定义与存储过程中out参数兼容的若干变量
【首先在PL/SQL块中声明若干变量,然后调用select_dept_out存储过程,并将定义的变量传入该存储过程,以便接收out参数的返回值】
具体过程:执行上述代码时,声明的两个变量会被传入到存储过程中,但存储过程执行时,其中的out参数会被赋值,存储过程执行完毕后,OUT参数的值会在调用处(begin)返回,之后定义的两个变量(declare)就能得到传回来的值,就可以在存储过程之外任意使用了。
二》使用Exec执行OUT模式的存储过程:使用Exec命令需要在SQL*Plus环境中使用variable关键字声明两个变量,用来存储out参数的返回值
【使用variable关键字声明两个变量,分别用来存储部门名称和部门编号,然后使用exec命令执行存储过程,并传入声明的两个变量来接收out参数的返回值】
Exec执行的方式是无法直接看到结果的,可以通过两种方式输出变量的值: ①print命令 ②select语句
#>print命令
#>select语句
③IN OUT模式参数
开始之前咱们现总结一下IN和OUT的特性:
在执行存储过程时,
IN参数只能根据调用者传入的值去执行存储过程,不能被修改;
OUT参数只能等待存储过程执行完毕为其赋值再供外界使用,不能像IN一样为存储过程提供数据;
到這里,大家想一想:如果我要是想【计算一个数的平方或者平方根】,这种存储过程怎么写呢?
岂不是要是用IN传入一个数,再用OUT定义一个变量来接收了?不过大家仔细想一下,我们想要计算的值传进去后,就没用了,如果再原路将计算结果返还回来,那该多好,就不用单独定义OUT参数了,结果就有了IN OUT模式参数
IN OUT就是解决这个问题的;兼顾了IN和OUT的参数特性调用存储过程时,上面的分析如果看懂了,这里就不详细解释定义了。就是给定一个参数,在存储过程执行过程中,发生了改变,之后再将该参数原路返还给调用者;
创建存储过程,计算一个数的平方或平方根:
1234567891011create or replace procedure pro_square(
num in out number,
flag in boolean) is
i int:=2; --表示计算平方
begin
if flag then --if语句,如果为true
num:=power(num,i); --计算平方
else --否则
num:=sqrt(num); --计算平方根
end if;
end pro_square; 执行上述存储过程:
--->IN参数的默认值(IN类型是可以设定默认值的,上面忘了说了,放在最后吧,重新建了表dept2)
IN模式的参数是可以设定默认值的,当执行时,调用者没有传值的话,可以使用默认值赋值:看栗子
【创建一个存储过程,定义3个IN参数,并将其中的两个参数设置为初始默认值;然后将这三个IN参数的值插入到dept中】
1234567create or replace procedure insert_dept2_default(
num_deptno in number,
var_dname in varchar2 default '综合部', --定义部门名称并设定默认值
var_loc in varchar2 default '北京') is
begin insert into dept2 values(num_deptno,var_dname,var_loc); --插入一条记录end insert_dept2_default; 执行执行过程:
边学习边总结了存储过程的基本写法规则也就这些,希望能给大家一些启发~~~~
以上代码都是个人亲自敲的,执行过后都是没有问题的。如果哪位大牛觉得代码有不妥的地方,希望不吝赐教,万分感谢!原文地址https://www.cnblogs.com/SpaceKiller/p/10615599.html
《jQuery EasyUI开发指南》——1.3 建立数据库
本节书摘来自异步社区《jQuery EasyUI开发指南》一书中的第1章,第1.3节,作者:王波著,更多章节内容可以访问云栖社区“异步社区”公众号查看
1.3 建立数据库
打开Oracle10的“Database Configuration Assistant”功能,可以看到欢迎界面,单击“下一步”。这时,就正式进入了Oracle创建数据库的过程,具体创建数据库的过程是下面这样的。
(1)创建数据库,单击“下一步”。
(2)选择“一般用途”,单击“下一步”。
(3)“全局数据库名”输入manage。注意,“SID”一般需要和“全局数据库名”保持一致。这里,同样输入manage,然后单击“下一步”。
(4)数据库的“管理方式”可以保持默认配置,直接单击“下一步”。
(5)“数据库身份证明”,选择“所有账户使用同一口令”,输入manage,单击“下一步”。
(6)“存储选项”,选择“文件系统”,单击“下一步”。
(7)“数据库文件所在位置”保持默认配置,单击“下一步”。
(8)“恢复配置”,取消“制定快速恢复区”,单击“下一步”。
(9)“数据库内容”保持默认配置,单击“下一步”。
(10)“初始化参数”选择“典型”,“百分比”输入框中输入11,单击“下一步”。
(11)“数据库存储”保持默认配置,单击“下一步”。
(12)“创建选项”,单击“完成”,弹出对话框,列出了manage数据库的参数,单击“确定”,开始创建数据库。
(13)当数据库创建完成时,会弹出数据库账户锁定的提示信息对话框,单击“口令管理”,弹出对话框,可以看到,Oracle系统默认提供了很多账户,只有SYS、SYSTEM账户是没有被锁定的,其他的都已经被锁定。在这里,可以再次设定密码。因为之前已经设置过,所以此处不再设置,单击“确定”,关闭对话框,再单击“退出”。
建立Oracle数据库的过程中的第2步如图1-2所示。
此时,Oracle的manage数据库已经被创建好了。因为manage只是单纯地用来为项目存储数据,初期不用太过于考虑性能和安全问题(项目上线之前会做压力测试)。所以,在很多设置上面都采用了默认配置和最低配置。这样的设置有一个好处,就是可以最大限度地减少数据库对系统资源的占用,做到轻量级。毕竟,本书的主要内容是讲开发的,而不是讲数据库维护的。再者,采用这种方式创建数据库,在以后的维护中,也可以通过实践来检测数据库哪方面不足,然后对数据库进行有针对性的优化,对比优化前和优化后的效果,这也不失为一种学习数据库的好方法。
建好了数据库实例,接下来,需要建立监听服务。打开Oracle的“Net Manager”功能,选择“监听程序”,单击左边的加号,在弹出的对话框中输入监听程序名LISTENER,单击“确定”。一般,配置监听位置就可以了,单击“添加地址”,“协议”选择“TCP/IP”,“主机”填127.0.0.1,“端口”填1521。因为manage数据库和项目程序都在本机上,所以在配置监听程序的时候,主机要填127.0.0.1,也就是代表本机的IP,或者填localhost,填计算机名称也可以。
当监听服务配置好后,就可以通过PLSQL来操作Oracle数据库了。一般来说,Oracle的服务只需要启动OracleOraDb10g_home1TNSListener监听程序,还有OracleServiceMANAGE就可以了,这样就具备进入PLSQL的条件了。最好把这些服务的启动方式设置为手动。如果是专门有一台计算机作为服务器的话,就需要设置为自动。简而言之,这里涉及计算机优化的问题,一般在开发环境中,也就是我们的本地计算机中,设置为手动更方便我们做到控制管理。在程序发布的正式环境中,一般是服务器上,也就是面对客户的版本,就需要设置为自动,保证24小时运行。
以下是Oracle的服务介绍,可以根据需要来设置它们的启动方式,以起到节省系统资源的作用。
(SID)均指数据库安装时所指定的SID名称,(HOME_NAME)指安装时ORACLE_HOME指定值的最后Oracle安装名。Oracle在一台机器上可以有多个实例存在,如果使用多实例,关于SID和HOME_NAME的服务会成倍数存在,对系统的资源占用很大。
在建立一个数据库时,这些服务只会出现一次。可以根据具体的情况,选择开启和停止,并设置合适的启动方式。这样,把数据库的活动监控和管理起来远比什么都自动化好。
OracleDBConsole(SID)是采用浏览器方式的Oracle企业管理器,即访问https://localhost:1158/em要用到的服务。
Oracle(HOME_NAME) iSQLPlus是iSQLPlus的服务,要使用iSQL*Plus,应该开启该服务。
OracleService(SID)是数据库主服务,该服务决定着数据库的启动和停止。服务进程为ORACLE.EXE,也就是Windows任务管理器进程中的运行项。
Oracle(HOME_NAME)TNSListener是监听器服务,要使用PLSQL管理数据库,必须开启此服务。一般来说,数据库需要远程访问时都需要此服务(不管是通过另一台机器还是在本地通过SQL*Net网络协议)。
一般来说,只需要特别关注OracleService(SID)和Oracle(HOME_NAME)TNSListener就可以了。Service服务保证数据库启动,TNS保证PLSQL可以顺利连接。其他的服务可以酌情关闭,以节省系统资源。
在这里,有一个地方需要注意,如果采用手动方式来启动服务,可能会出现以下问题:如果长时间开启服务,或者不使用数据库,在某天需要使用的时候会发现实例开启了不起作用。通过Database Configuration Assistant来管理数据库的时候,竟然会发现原来建好的数据库都不存在了。其实,造成这个问题的原因是,现在的电脑都装有一些系统优化软件,这些软件在做优化的时候,容易把Oracle的一些注册表信息更改了,造成找不到相应的数据库。在这里,可以通过以下命令来重新建立数据库实例,然后,重启服务,就可以恢复正常。在命令行模式下直接执行:
oradim -NEW -SID manage -PFILE D:\oracle\product\10.2.0\admin\manage\pfile\init.ora此时,按理说,重启了服务,即可正常运行。但是,如果还是不能运行的话,就需要启动Database Configuration Assistant,单击“下一步”。这时候,因为之前已经重新建立过实例,程序会自动识别数据库,选择“配置数据库”选项,单击“下一步”。这时可以看到,数据库选项栏中出现了manage,选中它,单击下一步,当页面中出现了数据库组件页签时,就说明Oracle已经读取到了manage。此时,只要再次重启服务即可恢复正常。
本书并不会讲解太多数据库维护方面的知识,读者可以参考专业数据库方面的书籍。一般来说,软件工程师很少会兼顾数据库管理员(DBA)的角色,基本上每个公司都会有专门负责数据库维护的人员。这些人的工作就是做日常维护,包括数据库的调优、备份等。
数据库面试题(开发者必看)
数据库常见面试题(开发者篇)
什么是存储过程?有哪些优缺点?
什么是存储过程?有哪些优缺点?
存储过程就像我们编程语言中的函数一样,封装了我们的代码(PLSQL、T-SQL)。
存储过程的优点:
能够将代码封装起来
保存在数据库之中
让编程语言进行调用
存储过程是一个预编译的代码块,执行效率比较高
一个存储过程替代大量T_SQL语句 ,可以降低网络通信量,提高通信速率
存储过程的缺点:
每个数据库的存储过程语法几乎都不一样,十分难以维护(不通用)
业务逻辑放在数据库上,难以迭代
三个范式是什么
三个范式是什么
第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。
第二范式(2NF):数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。
第三范式(3NF):在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系: 关键字段 → 非关键字段x → 非关键字段y
上面的文字我们肯定是看不懂的,也不愿意看下去的。接下来我就总结一下:
首先要明确的是:满足着第三范式,那么就一定满足第二范式、满足着第二范式就一定满足第一范式
第一范式:字段是最小的的单元不可再分
学生信息组成学生信息表,有年龄、性别、学号等信息组成。这些字段都不可再分,所以它是满足第一范式的
第二范式:满足第一范式,表中的字段必须完全依赖于全部主键而非部分主键。
其他字段组成的这行记录和主键表示的是同一个东西,而主键是唯一的,它们只需要依赖于主键,也就成了唯一的
学号为1024的同学,姓名为Java3y,年龄是22岁。姓名和年龄字段都依赖着学号主键。
第三范式:满足第二范式,非主键外的所有字段必须互不依赖
就是数据只在一个地方存储,不重复出现在多张表中,可以认为就是消除传递依赖
比如,我们大学分了很多系(中文系、英语系、计算机系……),这个系别管理表信息有以下字段组成:系编号,系主任,系简介,系架构。那我们能不能在学生信息表添加系编号,系主任,系简介,系架构字段呢?不行的,因为这样就冗余了,非主键外的字段形成了依赖关系(依赖到学生信息表了)!正确的做法是:学生表就只能增加一个系编号字段。
参考链接:
https://www.zhihu.com/question/24696366
http://www.cnblogs.com/CareySon/archive/2010/02/16/1668803.html
什么是视图?以及视图的使用场景有哪些?
什么是视图?以及视图的使用场景有哪些?
视图是一种基于数据表的一种虚表
(1)视图是一种虚表
(2)视图建立在已有表的基础上, 视图赖以建立的这些表称为基表
(3)向视图提供数据内容的语句为 SELECT 语句,可以将视图理解为存储起来的 SELECT 语句
(4)视图向用户提供基表数据的另一种表现形式
(5)视图没有存储真正的数据,真正的数据还是存储在基表中
(6)程序员虽然操作的是视图,但最终视图还会转成操作基表
(7)一个基表可以有0个或多个视图
有的时候,我们可能只关系一张数据表中的某些字段,而另外的一些人只关系同一张数据表的某些字段...
那么把全部的字段都都显示给他们看,这是不合理的。
我们应该做到:他们想看到什么样的数据,我们就给他们什么样的数据...一方面就能够让他们只关注自己的数据,另一方面,我们也保证数据表一些保密的数据不会泄露出来...
我们在查询数据的时候,常常需要编写非常长的SQL语句,几乎每次都要写很长很长....上面已经说了,视图就是基于查询的一种虚表,也就是说,视图可以将查询出来的数据进行封装。。。那么我们在使用的时候就会变得非常方便...
值得注意的是:使用视图可以让我们专注与逻辑,但不提高查询效率
drop、delete与truncate分别在什么场景之下使用?
drop、delete与truncate分别在什么场景之下使用?
我们来对比一下他们的区别:
drop table
1)属于DDL
2)不可回滚
3)不可带where
4)表内容和结构删除
5)删除速度快
truncate table
1)属于DDL
2)不可回滚
3)不可带where
4)表内容删除
5)删除速度快
delete from
1)属于DML
2)可回滚
3)可带where
4)表结构在,表内容要看where执行的情况
5)删除速度慢,需要逐行删除
不再需要一张表的时候,用drop
想删除部分数据行时候,用delete,并且带上where子句
保留表而删除所有数据的时候用truncate
索引是什么?有什么作用以及优缺点?
索引是什么?有什么作用以及优缺点?
什么是索引【Index】
(1)是一种快速查询表中内容的机制,类似于新华字典的目录
(2)运用在表中某个些字段上,但存储时,独立于表之外
索引表把数据变成是有序的....
快速定位到硬盘中的数据文件...
rowid特点
rowid的特点
(1)位于每个表中,但表面上看不见,例如:desc emp是看不见的
(2)只有在select中,显示写出rowid,方可看见
(3)它与每个表绑定在一起,表亡,该表的rowid亡,二张表rownum可以相同,但rowid必须是唯一的
(4)rowid是18位大小写加数字混杂体,唯一表代该条记录在DBF文件中的位置
(5)rowid可以参与=/like比较时,用''单引号将rowid的值包起来,且区分大小写
(6)rowid是联系表与DBF文件的桥梁
索引特点
索引的特点
(1)索引一旦建立,** Oracle管理系统会对其进行自动维护**, 而且由Oracle管理系统决定何时使用索引
(2)用户不用在查询语句中指定使用哪个索引
(3)在定义primary key或unique约束后系统自动在相应的列上创建索引
(4)用户也能按自己的需求,对指定单个字段或多个字段,添加索引
需要注意的是:Oracle是自动帮我们管理索引的,并且如果我们指定了primary key或者unique约束,系统会自动在对应的列上创建索引..
什么时候【要】创建索引
(1)表经常进行 SELECT 操作
(2)表很大(记录超多),记录内容分布范围很广
(3)列名经常在 WHERE 子句或连接条件中出现
什么时候【不要】创建索引
(1)表经常进行 INSERT/UPDATE/DELETE 操作
(2)表很小(记录超少)
(3)列名不经常作为连接条件或出现在 WHERE 子句中
索引优缺点:
索引加快数据库的检索速度
索引降低了插入、删除、修改等维护任务的速度(虽然索引可以提高查询速度,但是它们也会导致数据库系统更新数据的性能下降,因为大部分数据更新需要同时更新索引)
唯一索引可以确保每一行数据的唯一性,通过使用索引,可以在查询的过程中使用优化隐藏器,提高系统的性能
索引需要占物理和数据空间
索引分类:
唯一索引:唯一索引不允许两行具有相同的索引值
主键索引:为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型。主键索引要求主键中的每个值是唯一的,并且不能为空
聚集索引(Clustered):表中各行的物理顺序与键值的逻辑(索引)顺序相同,每个表只能有一个
非聚集索引(Non-clustered):非聚集索引指定表的逻辑顺序。数据存储在一个位置,索引存储在另一个位置,索引中包含指向数据存储位置的指针。可以有多个,小于249个
深入理解索引可参考:
https://kb.cnblogs.com/page/45712/
https://www.cnblogs.com/drizzlewithwind/p/5707058.html
什么是事务?
什么是事务?
事务简单来说:一个Session中所进行所有的操作,要么同时成功,要么同时失败
ACID — 数据库事务正确执行的四个基本要素
包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
一个支持事务(Transaction)中的数据库系统,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易。
举个例子:A向B转账,转账这个流程中如果出现问题,事务可以让数据恢复成原来一样【A账户的钱没变,B账户的钱也没变】。
事例说明:
/*
* 我们来模拟A向B账号转账的场景
* A和B账户都有1000块,现在我让A账户向B账号转500块钱
*
* */
//JDBC默认的情况下是关闭事务的,下面我们看看关闭事务去操作转账操作有什么问题
//A账户减去500块
String sql = "UPDATE a SET money=money-500 ";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
//B账户多了500块
String sql2 = "UPDATE b SET money=money+500";
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();
从上面看,我们的确可以发现A向B转账,成功了。可是如果A向B转账的过程中出现了问题呢?下面模拟一下
//A账户减去500块
String sql = "UPDATE a SET money=money-500 ";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
//这里模拟出现问题
int a = 3 / 0;
String sql2 = "UPDATE b SET money=money+500";
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();
显然,上面代码是会抛出异常的,我们再来查询一下数据。A账户少了500块钱,B账户的钱没有增加。这明显是不合理的。
我们可以通过事务来解决上面出现的问题
//开启事务,对数据的操作就不会立即生效。
connection.setAutoCommit(false);
//A账户减去500块
String sql = "UPDATE a SET money=money-500 ";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
//在转账过程中出现问题
int a = 3 / 0;
//B账户多500块
String sql2 = "UPDATE b SET money=money+500";
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();
//如果程序能执行到这里,没有抛出异常,我们就提交数据
connection.commit();
//关闭事务【自动提交】
connection.setAutoCommit(true);
} catch (SQLException e) {
try {
//如果出现了异常,就会进到这里来,我们就把事务回滚【将数据变成原来那样】
connection.rollback();
//关闭事务【自动提交】
connection.setAutoCommit(true);
} catch (SQLException e1) {
e1.printStackTrace();
}
上面的程序也一样抛出了异常,A账户钱没有减少,B账户的钱也没有增加。
注意:当Connection遇到一个未处理的SQLException时,系统会非正常退出,事务也会自动回滚,但如果程序捕获到了异常,是需要在catch中显式回滚事务的。
事务隔离级别
数据库定义了4个隔离级别:
Serializable【可避免脏读,不可重复读,虚读】
Repeatable read【可避免脏读,不可重复读】
Read committed【可避免脏读】
Read uncommitted【级别最低,什么都避免不了】
分别对应Connection类中的4个常量
TRANSACTION_READ_UNCOMMITTED
TRANSACTION_READ_COMMITTED
TRANSACTION_REPEATABLE_READ
TRANSACTION_SERIALIZABLE
脏读:一个事务读取到另外一个事务未提交的数据
例子:A向B转账,A执行了转账语句,但A还没有提交事务,B读取数据,发现自己账户钱变多了!B跟A说,我已经收到钱了。A回滚事务【rollback】,等B再查看账户的钱时,发现钱并没有多。
不可重复读:一个事务读取到另外一个事务已经提交的数据,也就是说一个事务可以看到其他事务所做的修改
注:A查询数据库得到数据,B去修改数据库的数据,导致A多次查询数据库的结果都不一样【危害:A每次查询的结果都是受B的影响的,那么A查询出来的信息就没有意思了】
虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
注:和不可重复读类似,但虚读(幻读)会读到其他事务的插入的数据,导致前后读取不一致
简单总结:脏读是不可容忍的,不可重复读和虚读在一定的情况下是可以的【做统计的肯定就不行】。
数据库的乐观锁和悲观锁是什么?
数据库的乐观锁和悲观锁是什么?
确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性,乐观锁和悲观锁是并发控制主要采用的技术手段。
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作
在查询完数据的时候就把事务锁起来,直到提交事务
实现方式:使用数据库中的锁机制
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
在修改数据的时候把事务锁起来,通过version的方式来进行锁定
实现方式:使用version版本或者时间戳
悲观锁:
乐观锁:
参考资料:
http://www.open-open.com/lib/view/open1452046967245.html
超键、候选键、主键、外键分别是什么?
超键、候选键、主键、外键分别是什么?
超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。
候选键(候选码):是最小超键,即没有冗余元素的超键。
主键(主码):数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)。
外键:在一个表中存在的另一个表的主键称此表的外键。
候选码和主码:
例子:邮寄地址(城市名,街道名,邮政编码,单位名,收件人)
它有两个候选键:{城市名,街道名} 和 {街道名,邮政编码}
如果我选取{城市名,街道名}作为唯一标识实体的属性,那么{城市名,街道名} 就是主码(主键)
SQL 约束有哪几种?
SQL 约束有哪几种?
NOT NULL: 用于控制字段的内容一定不能为空(NULL)。
UNIQUE: 控件字段内容不能重复,一个表允许有多个 Unique 约束。
PRIMARY KEY: 也是用于控件字段内容不能重复,但它在一个表只允许出现一个。
FOREIGN KEY: 用于预防破坏表之间连接的动作,也能防止非法数据插入外键列,因为它必须是它指向的那个表中的值之一。
CHECK: 用于控制字段的值范围。
数据库运行于哪种状态下可以防止数据的丢失?
数据库运行于哪种状态下可以防止数据的丢失?
在archivelog mode(归档模式)只要其归档日志文件不丢失,就可以有效地防止数据丢失。
Mysql存储引擎
Mysql的存储引擎有以下几种:
我的是5.7.15版本,默认使用的是Innodb版本!
常用的存储引擎有以下:
Innodb引擎,Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。
MyIASM引擎(原本Mysql的默认引擎),不提供事务的支持,也不支持行级锁和外键。
MEMORY引擎:所有的数据都在内存中,数据的处理速度快,但是安全性不高。
同一个数据库也可以使用多种存储引擎的表。如果一个表修改要求比较高的事务处理,可以选择InnoDB。这个数据库中可以将查询要求比较高的表选择MyISAM存储。如果该数据库需要一个用于查询的临时表,可以选择MEMORY存储引擎。
参考资料:
https://www.cnblogs.com/xiaohaillong/p/6079551.html
http://blog.csdn.net/ls5718/article/details/52248040
http://blog.csdn.net/t146lla128xx0x/article/details/78737290
MyIASM和Innodb两种引擎所使用的索引的数据结构是什么?
MyIASM和Innodb两种引擎所使用的索引的数据结构是什么?
答案:都是B+树!
MyIASM引擎,B+树的数据结构中存储的内容实际上是实际数据的地址值。也就是说它的索引和实际数据是分开的,只不过使用索引指向了实际数据。这种索引的模式被称为非聚集索引。
Innodb引擎的索引的数据结构也是B+树,只不过数据结构中存储的都是实际的数据,这种索引有被称为聚集索引。
varchar和char的区别
varchar和char的区别
Char是一种固定长度的类型,varchar是一种可变长度的类型
mysql有关权限的表都有哪几个
mysql有关权限的表都有哪几个
MySQL服务器通过权限表来控制用户对数据库的访问,权限表存放在mysql数据库里,由mysql_install_db脚本初始化。这些权限表分别user,db,table_priv,columns_priv和host。下面分别介绍一下这些表的结构和内容:
user权限表:记录允许连接到服务器的用户帐号信息,里面的权限是全局级的。
db权限表:记录各个帐号在各个数据库上的操作权限。
table_priv权限表:记录数据表级的操作权限。
columns_priv权限表:记录数据列级的操作权限。
host权限表:配合db权限表对给定主机上数据库级操作权限作更细致的控制。这个权限表不受GRANT和REVOKE语句的影响。
数据表损坏的修复方式有哪些?
数据表损坏的修复方式有哪些?
使用 myisamchk 来修复,具体步骤:
1)修复前将mysql服务停止。
2)打开命令行方式,然后进入到mysql的/bin目录。
3)执行myisamchk –recover 数据库所在路径/*.MYI
使用repair table 或者 OPTIMIZE table命令来修复,REPAIR TABLE table_name 修复表 OPTIMIZE TABLE table_name 优化表 REPAIR TABLE 用于修复被破坏的表。
OPTIMIZE TABLE 用于回收闲置的数据库空间,当表上的数据行被删除时,所占据的磁盘空间并没有立即被回收,使用了OPTIMIZE TABLE命令后这些空间将被回收,并且对磁盘上的数据行进行重排(注意:是磁盘上,而非数据库)
MySQL中InnoDB引擎的行锁是通过加在什么上完成
MySQL中InnoDB引擎的行锁是通过加在什么上完成
InnoDB是基于索引来完成行锁
例: select * from tab_with_index where id = 1 for update;
for update 可以根据条件来完成行锁锁定,并且 id 是有索引键的列,
如果 id 不是索引键那么InnoDB将完成表锁,,并发将无从谈起
数据库优化的思路
SQL优化
在我们书写SQL语句的时候,其实书写的顺序、策略会影响到SQL的性能,虽然实现的功能是一样的,但是它们的性能会有些许差别。
因此,下面就讲解在书写SQL的时候,怎么写比较好。
①选择最有效率的表名顺序
数据库的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表将被最先处理
在FROM子句中包含多个表的情况下:
如果三个表是完全无关系的话,将记录和列名最少的表,写在最后,然后依次类推
也就是说:选择记录条数最少的表放在最后
如果有3个以上的表连接查询:
如果三个表是有关系的话,将引用最多的表,放在最后,然后依次类推。
也就是说:被其他表所引用的表放在最后
例如:查询员工的编号,姓名,工资,工资等级,部门名
emp表被引用得最多,记录数也是最多,因此放在form字句的最后面
select emp.empno,emp.ename,emp.sal,salgrade.grade,dept.dname
from salgrade,dept,emp
where (emp.deptno = dept.deptno) and (emp.sal between salgrade.losal and salgrade.hisal)
②WHERE子句中的连接顺序
数据库采用自右而左的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之左,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的之右。
emp.sal可以过滤多条记录,写在WHERE字句的最右边
select emp.empno,emp.ename,emp.sal,dept.dname
from dept,emp
where (emp.deptno = dept.deptno) and (emp.sal > 1500)
③SELECT子句中避免使用*号 ####
我们当时学习的时候,“*”号是可以获取表中全部的字段数据的。
但是它要通过查询数据字典完成的,这意味着将耗费更多的时间
使用*号写出来的SQL语句也不够直观。
④用TRUNCATE替代DELETE
这里仅仅是:删除表的全部记录,除了表结构才这样做。
DELETE是一条一条记录的删除,而Truncate是将整个表删除,保留表结构,这样比DELETE快
⑤多使用内部函数提高SQL效率
例如使用mysql的concat()函数会比使用||来进行拼接快,因为concat()函数已经被mysql优化过了。
⑥使用表或列的别名
如果表或列的名称太长了,使用一些简短的别名也能稍微提高一些SQL的性能。毕竟要扫描的字符长度就变少了。。。
⑦多使用commit
comiit会释放回滚点...
⑧善用索引
索引就是为了提高我们的查询数据的,当表的记录量非常大的时候,我们就可以使用索引了。
⑨SQL写大写
我们在编写SQL 的时候,官方推荐的是使用大写来写关键字,因为Oracle服务器总是先将小写字母转成大写后,才执行
⑩避免在索引列上使用NOT
因为Oracle服务器遇到NOT后,他就会停止目前的工作,转而执行全表扫描
①①避免在索引列上使用计算
WHERE子句中,如果索引列是函数的一部分,优化器将不使用索引而使用全表扫描,这样会变得变慢
①②用 >= 替代 >
低效:
SELECT * FROM EMP WHERE DEPTNO > 3
首先定位到DEPTNO=3的记录并且扫描到第一个DEPT大于3的记录
高效:
SELECT * FROM EMP WHERE DEPTNO >= 4
直接跳到第一个DEPT等于4的记录
①③用IN替代OR
select * from emp where sal = 1500 or sal = 3000 or sal = 800;
select * from emp where sal in (1500,3000,800);
①④总是使用索引的第一个列
如果索引是建立在多个列上,只有在它的第一个列被WHERE子句引用时,优化器才会选择使用该索引。 当只引用索引的第二个列时,不引用索引的第一个列时,优化器使用了全表扫描而忽略了索引
create index emp_sal_job_idex
on emp(sal,job);
----------------------------------
select *
from emp
where job != 'SALES';
上边就不使用索引了。
数据库结构优化
1)范式优化: 比如消除冗余(节省空间。。)
2)反范式优化:比如适当加冗余等(减少join)
3)拆分表: 垂直拆分和水平拆分
服务器硬件优化
这个么多花钱咯!
SQL练习题
下列练习题参考自公众号Java知音:
https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247483693&idx=1&sn=9fa301b0076778cd854a924e96cc356e&chksm=ebd63e01dca1b71745dca1f7e1c2aa2b7c80a393185db690b4fdfba22bb10ca87ea2cd6fa774&scene=21#wechat_redirect
https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247483696&idx=1&sn=5f472ce7720aede89e2e15ea64bed1bc&chksm=ebd63e1cdca1b70ad18dec268c9903b2cbe11f9ce7b0633980c78a28bd5b1b57c4efbe7a3411&scene=21#wechat_redirect
基本表结构:
student(sno,sname,sage,ssex)学生表
course(cno,cname,tno) 课程表
sc(sno,cno,score) 成绩表
teacher(tno,tname) 教师表
题目:
101,查询课程1的成绩比课程2的成绩高的所有学生的学号
select a.sno from
(select sno,score from sc where cno=1) a,
(select sno,score from sc where cno=2) b
where a.score>b.score and a.sno=b.sno
102,查询平均成绩大于60分的同学的学号和平均成绩
select a.sno as "学号", avg(a.score) as "平均成绩"
from
(select sno,score from sc) a
group by sno having avg(a.score)>60
103,查询所有同学的学号、姓名、选课数、总成绩
select a.sno as 学号, b.sname as 姓名,
count(a.cno) as 选课数, sum(a.score) as 总成绩
from sc a, student b
where a.sno = b.sno
group by a.sno, b.sname
或者:
selectstudent.sno as 学号, student.sname as 姓名,
count(sc.cno) as 选课数, sum(score) as 总成绩
from student left Outer join sc on student.sno = sc.sno
group by student.sno, sname
104,查询姓“张”的老师的个数
selectcount(distinct(tname)) from teacher where tname like '张%‘
或者:
select tname as "姓名", count(distinct(tname)) as "人数"
from teacher
where tname like'张%'
group by tname
105,查询没学过“张三”老师课的同学的学号、姓名
select student.sno,student.sname from student
where sno not in (select distinct(sc.sno) from sc,course,teacher
where sc.cno=course.cno and teacher.tno=course.tno and teacher.tname='张三')
106,查询同时学过课程1和课程2的同学的学号、姓名
select sno, sname from student
where sno in (select sno from sc where sc.cno = 1)
and sno in (select sno from sc where sc.cno = 2)
或者:
selectc.sno, c.sname from
(select sno from sc where sc.cno = 1) a,
(select sno from sc where sc.cno = 2) b,
student c
where a.sno = b.sno and a.sno = c.sno
或者:
select student.sno,student.sname from student,sc where student.sno=sc.sno and sc.cno=1
and exists( select * from sc as sc_2 where sc_2.sno=sc.sno and sc_2.cno=2)
107,查询学过“李四”老师所教所有课程的所有同学的学号、姓名
select a.sno, a.sname from student a, sc b
where a.sno = b.sno and b.cno in
(select c.cno from course c, teacher d where c.tno = d.tno and d.tname = '李四')
或者:
select a.sno, a.sname from student a, sc b,
(select c.cno from course c, teacher d where c.tno = d.tno and d.tname = '李四') e
where a.sno = b.sno and b.cno = e.cno
108,查询课程编号1的成绩比课程编号2的成绩高的所有同学的学号、姓名
select a.sno, a.sname from student a,
(select sno, score from sc where cno = 1) b,
(select sno, score from sc where cno = 2) c
where b.score > c.score and b.sno = c.sno and a.sno = b.sno
109,查询所有课程成绩小于60分的同学的学号、姓名
select sno,sname from student
where sno not in (select distinct sno from sc where score > 60)
110,查询至少有一门课程与学号为1的同学所学课程相同的同学的学号和姓名
select distinct a.sno, a.sname
from student a, sc b
where a.sno <> 1 and a.sno=b.sno and
b.cno in (select cno from sc where sno = 1)
或者:
select s.sno,s.sname
from student s,
(select sc.sno
from sc
where sc.cno in (select sc1.cno from sc sc1 where sc1.sno=1)and sc.sno<>1
group by sc.sno)r1
where r1.sno=s.sno
111、把“sc”表中“王五”所教课的成绩都更改为此课程的平均成绩
update sc set score = (select avg(sc_2.score) from sc sc_2 wheresc_2.cno=sc.cno)
from course,teacher where course.cno=sc.cno and course.tno=teacher.tno andteacher.tname='王五'
112、查询和编号为2的同学学习的课程完全相同的其他同学学号和姓名
这一题分两步查:
1,
select sno
from sc
where sno <> 2
group by sno
having sum(cno) = (select sum(cno) from sc where sno = 2)
2,
select b.sno, b.sname
from sc a, student b
where b.sno <> 2 and a.sno = b.sno
group by b.sno, b.sname
having sum(cno) = (select sum(cno) from sc where sno = 2)
113、删除学习“王五”老师课的sc表记录
delete sc from course, teacher
where course.cno = sc.cno and course.tno = teacher.tno and tname = '王五'
114、向sc表中插入一些记录,这些记录要求符合以下条件:
将没有课程3成绩同学的该成绩补齐, 其成绩取所有学生的课程2的平均成绩
insert sc select sno, 3, (select avg(score) from sc where cno = 2)
from student
where sno not in (select sno from sc where cno = 3)
115、按平平均分从高到低显示所有学生的如下统计报表:
-- 学号,企业管理,马克思,UML,数据库,物理,课程数,平均分
select sno as 学号
,max(case when cno = 1 then score end) AS 企业管理
,max(case when cno = 2 then score end) AS 马克思
,max(case when cno = 3 then score end) AS UML
,max(case when cno = 4 then score end) AS 数据库
,max(case when cno = 5 then score end) AS 物理
,count(cno) AS 课程数
,avg(score) AS 平均分
FROM sc
GROUP by sno
ORDER by avg(score) DESC
116、查询各科成绩最高分和最低分:
以如下形式显示:课程号,最高分,最低分
select cno as 课程号, max(score) as 最高分, min(score) 最低分
from sc group by cno
select course.cno as '课程号'
,MAX(score) as '最高分'
,MIN(score) as '最低分'
from sc,course
where sc.cno=course.cno
group by course.cno
117、按各科平均成绩从低到高和及格率的百分数从高到低顺序
SELECT t.cno AS 课程号,
max(course.cname)AS 课程名,
isnull(AVG(score),0) AS 平均成绩,
100 * SUM(CASE WHEN isnull(score,0)>=60 THEN 1 ELSE 0 END)/count(1) AS 及格率
FROM sc t, course
where t.cno = course.cno
GROUP BY t.cno
ORDER BY 及格率 desc
118、查询如下课程平均成绩和及格率的百分数(用"1行"显示):
企业管理(001),马克思(002),UML (003),数据库(004)
select
avg(case when cno = 1 then score end) as 平均分1,
avg(case when cno = 2 then score end) as 平均分2,
avg(case when cno = 3 then score end) as 平均分3,
avg(case when cno = 4 then score end) as 平均分4,
100 * sum(case when cno = 1 and score > 60 then 1 else 0 end) / sum(casewhen cno = 1 then 1 else 0 end) as 及格率1,
100 * sum(case when cno = 2 and score > 60 then 1 else 0 end) / sum(casewhen cno = 2 then 1 else 0 end) as 及格率2,
100 * sum(case when cno = 3 and score > 60 then 1 else 0 end) / sum(casewhen cno = 3 then 1 else 0 end) as 及格率3,
100 * sum(case when cno = 4 and score > 60 then 1 else 0 end) / sum(casewhen cno = 4 then 1 else 0 end) as 及格率4
from sc
119、查询不同老师所教不同课程平均分, 从高到低显示
select max(c.tname) as 教师, max(b.cname) 课程, avg(a.score) 平均分
from sc a, course b, teacher c
where a.cno = b.cno and b.tno = c.tno
group by a.cno
order by 平均分 desc
或者:
select r.tname as '教师',r.rname as '课程' , AVG(score) as '平均分'
from sc,
(select t.tname,c.cno as rcso,c.cname as rname
from teacher t ,course c
where t.tno=c.tno)r
where sc.cno=r.rcso
group by sc.cno,r.tname,r.rname
order by AVG(score) desc
120、查询如下课程成绩均在第3名到第6名之间的学生的成绩:
-- [学生ID],[学生姓名],企业管理,马克思,UML,数据库,平均成绩
select top 6 max(a.sno) 学号, max(b.sname) 姓名,
max(case when cno = 1 then score end) as 企业管理,
max(case when cno = 2 then score end) as 马克思,
max(case when cno = 3 then score end) as UML,
max(case when cno = 4 then score end) as 数据库,
avg(score) as 平均分
from sc a, student b
where a.sno not in
(select top 2 sno from sc where cno = 1 order by score desc)
and a.sno not in (select top 2 sno from sc where cno = 2 order by scoredesc)
and a.sno not in (select top 2 sno from sc where cno = 3 order by scoredesc)
and a.sno not in (select top 2 sno from sc where cno = 4 order by scoredesc)
and a.sno = b.sno
group by a.sno
Oracle和Mysql的区别
在Mysql中,一个用户下可以创建多个库:
而在Oracle中,Oracle服务器是由两部分组成
数据库实例【理解为对象,看不见的】
数据库【理解为类,看得见的】
一个数据库实例可拥有多个用户,一个用户默认拥有一个表空间。
表空间是存储我们数据库表的地方,表空间内可以有多个文件。
当我们使用Oracle作为我们数据库时,我们需要指定用户、表空间来存储我们所需要的数据!
最后
参考资料:
http://blog.csdn.net/xlgen157387/article/details/46899031
http://blog.csdn.net/beauty_1991/article/details/51209107
https://zhuanlan.zhihu.com/p/23713529
http://blog.csdn.net/wickedvalley/article/details/51527551
http://blog.csdn.net/zhugewendu/article/details/73550414
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y
更多的文章可往:文章的目录导航
PLSQL面向对象编程
面向对象编程简称OOP编程,实际上是对现实世界事物的一种抽象的过程。它的核心是把对象的定义和实现进行区分,让定义部分对象所具有的结构,让实现部分根据定义部分定义的具体结构进行具体的实现。
用于生产玩具的模具叫做类,通常设计类的过程也可以称为建模,当然这个模不是模具的模,而是对类的模型进行建模。所生产的玩具可以叫做对象,类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模版。
对类的定义是一组具有相同数据结构和相同操作的对象的集合,类的定义包括一组代表其特性的属性。以及一组表示其执行行为的方法,类定义可以看作是一个具有相似特性与共同行为的对象模版,可以用来产生对象,而每个对象都是类的实例,都可以使用类中提供的方法,对象的具体状态包含在对象实例变量中。
封装:也叫做信息封装,确保对象不会以不可预期的方式改变其他对象的内部状态。只有在那些提供了内部状态改变方法的对象中,才可以访问其他内部状态。每类对象都提供一个与其他对象联系的接口,并规定了其他对象进行调用的方法
多态性:对象的引用和类会涉及其他许多不同类型的对象,而且引用对象所产生的结果将依据实际调用的类型
继承性:允许在现存的对象基础上创建子类对象,统一并增强了多态性和封装性。典型地来说就是用类来对对象进行分组,而且还可以定义新类为现存的类的扩展,这样就可以将类组织成树型或网状结构,这体现了对象的通用性。
对象类型实际上就是在上一小节中介绍的类型,类的实例就是对象。对象的类型封装了数据结构和用于操纵这些数据结构的过程和函数,这使得通过定义对象类型就可以封装一些较复杂的代码,提高应用开发的效率和速度。可以在一个对象类型说明部分声明属性和方法,属性不能是常数,异常,游标或类型,最大的属性声明数量是1000个,但是必须至少声明一个属性,而方法是可选的。属性描述了对象的特性,方法则是对象类型具有的功能。在PLSQL中,方法就是一些子程序,可以是函数,也可以是过程,方法名称不能和它的对象类型名称和属性名称一样,方法在实例级别或对象类型级别被调用。
PLSQL中对象的组成结构
PLSQL中的对象类型是一种自定义的符合类型,它的定义与包的定义非常相似
对象类型规范:是对象与应用的接口,它用于定义对象的公用属性和方法
对象类型体:用于实现对象类型规范所定义的公用方法.
例如在定义员工对象类型时,先在对象类型规范中定义好了对象的所有属性,以及对象可被调用的方法声明,这些方法并没有具体的实现部分,仅可供外部调用的方法签名。而具体的方法体代码实现则定义在对象类型体中。
在定义对象类型的属性时,不能指定对象属性的默认值,也不能指定NOT NULL选项。
PLSQL中可以定义的几种类型的方法
1.构造方法:该方法类似于JAVA等语言中的构造函数,用来初始化一个对象类型并返回对象的实例
2.MEMBER方法:该方法允许对象的实例进行调用,在MEMBER方法中可以访问对象实例的数据,通常称为实例方法或成员方法
3.STATIC方法:该方法可以直接在对象类型上进行调用,它用于在对象类型上执行全局操作,通常称为静态方法
4.MAP方法:用于在多个对象间排序的映射方法。
5.ORDER方法:用于在两个对象实例间排序的排序方法。
定义对象类型
对象类型包含对象类型规范和对象类型体两大部分,因此在定义时必须先定义对象类型规范,然后再定义对象类型体.
定义employee_obj对象规范
create or replace type employee_obj as object(
empno number(4),
ename varchar2(20),
job varchar2(20),
sal number(10,2),
comm number(10,2),
deptno number(4),
MEMBER procedure change_sal(p_empno number,p_sal number),
MEMBER procedure change_comm(p_empno number,p_comm number),
MEMBER procedure change_deptno(p_empno number,p_deptno number),
MEMBER procedure get_sal(p_empno number) return number,
MEMBER procedure get_comm(p_empno number) return number,
MEMBER procedure get_deptno(p_empno number) return integer
)NOT FINAL; --指定该类可以被继承,如果指定FINAL,表示该类无法被继承
定义对象体
create or replace type body employee_obj
as
member procedure chang_sal(p_empno number,p_sal number)
is
begin
update emp set sal=p_sal where empno=p_empno;
end;
member procedure chang_comm(p_empno number,p_sal number)
is
begin
update emp set comm=p_comm where empno=p_empno;
end;
member procedure chang_deptno(p_empno number,p_sal number)
is
begin
update emp set deptno=p_deptno where empno=p_empno;
end;
member function get_sal (p_empno number)
return number
is
v_sal number(10,2);
begin
select sal into v_sal from emp where empno=p_empno;
return v_sal;
end;
member function get_comm(p_empno number)
return number
is
v_comm number(10,2);
begin
select comm into v_comm from emp where empno=p_empno;
RETURN v_comm;
end;
member function get_deptno(p_empno number)
return integer
is
v_deptno int;
begin
select deptno into v_deptno from emp where empno=p_empno;
return v_deptno;
end;
end;
定义属性
属性是对象类型特性的定义,属性声明是一个对象必需的,也就是说一个对象类型至少要定义一个属性。属性的定义与变量的定义相似,也具有名称和数据类型,在整个对象类型中,属性的名称必须是唯一的,但是在不同的对象类型之间,属性的命名是可以重复的。
1.属性的声明必须在方法的声明以前,也就是说在对象规范中create type下面的声明必须最先是属性的定义。
2.属性的数据类型必须是oracle数据库类型,不能是任何PLSQL类型或PLSQL自定义类型,但是排除了oracle中的rowid,urowid,long,log raw,nchar,nclob,nvarchar2类型。
3.在定义属性时不能对属性应用NOT NULL约束或使用default指定默认值。
4.在一个对象类型中至少要定义一个属性,但是不能大于1000个属性.
属性的类型既可以是简单的数据类型,也可以是对象类型的引用.
定义对象的属性
create or replace type employee as object(
empno number(4),
ename varchar2(20),
job varchar2(20),
sal number(10,2),
comm number(10,2),
deptno number(4)
)NOT FINAL;
在定义了该对象类型之后,就可以在PLSQL语句块中通过实例化对象类型,读取或写入对象的属性值了.
declare
v_emp employee_property;
v_sal v_emp.sal%TYPE;
begin
v_emp:=employee_property(7890,'赵五','销售人员',5000,200,20);
v_sal:=v_emp.sal;
dbms_output.put_line(v_emp.ename||'的薪资是:'|| v_sal);
end;
定义方法
对象方法是在对象规范定义中使用MEMBER或STATIC声明在对象说明部分的子程序,它们是在属性声明之后进行的,
MEMBER方法:成员方法是基于对象实例而不是基于对象类型调用的
STATIC方法:静态方法独立于对象实例,也不能在对象类型主体中引用这个对象的属性
使用MEMBER和STATIC成员方法
create or replace type employee_method as object(
empno number(4),
sal number(10,2),
comm number(10,2),
deptno number(4),
MEMBER procedure change_sal, --实例方法,可以访问对象本身的属性
MEMBER function get_sal return number,
--静态方法,不能访问对象本身的属性,只能访问静态数据
STATIC procedure change_deptno(p_empno number,p_deptno number),
STATIC function get_sal(p_empno number) return number
)NOT FINAL; --指定该类可以被继承,如果指定FINAL,表示该类无法被继承
create or replace type body employee_method
as
MEMBER procedure change_sal
is
begin
self.sal:=self.sal*1.12;
end;
MEMBER function get_sal
return number
is
begin
return sal;
end;
STATIC procedure change_deptno(p_empno number,p_deptno number)
is
begin
update emp set deptno=p_deptno where empno=p_empno;
end;
STATIC function get_sal(p_empno number)
return number
is
v_sal number(10,2);
begin
select sal into v_sal from emp where empno=p_empno;
return v_sal;
end;
end;
MEMBER和STATIC方法使用示例
declare
v_emp employee_method;
begin
v_emp:=employee_method(7999,5000,200,20); --实例化employee_method对象,现在v_emp是对象实例
v_emp.change_sal; --调用对象实例方法,即MEMBER方法
dbms_output.put_line('员工编号为:'||v_emp.empno||'的薪资为:'||v_emp.get_sal);
--下面的代码调用STATIC方法更新emp表中员工编号为7369的部门编号为20
employee_method.change_deptno(7369,20);
--下面的代码获取emp表中员工编号为7369的员工薪资
dbms_output.put_line('员工编号为7369的薪资为:'||employee_method.get_sal(7369));
end;
使用SELF关键字
每一个MEMBER类型方法都隐式地声明了一个内联参数SELF,它代表了对象类型的一个实例,总是被传递给成员方法的第一个参数,实际上的方法体内,也可以不用SELF。
访问对象类型的属性
create or replace type employee_salobj as object(
empno number(4),
sal number(10,2),
comm number(10,2),
deptno number(4),
MEMBER procedure change_sal,
MEMBER procedure change_comm,
MEMBER procedure change_deptno,
MEMBER function get_sal return number,
MEMBER function get_comm return number,
MEMBER function get_deptno return INTEGER
)NOT FINAL;
create or replace type body employee_salobj
as
MEMBER procedure change_sal
is
begin
self.sal:=self.sal*1.12;
end;
MEMBER procedure change_comm
is
begin
comm:=comm * 1.12;
end;
MEMBER procedure change_deptno
is
begin
self.deptno:=20;
end;
MEMBER function get_sal
return number
is
begin
return sal;
end;
MEMBER function get_comm
return self.comm;
end;
MEMBER function get_deptno
return integer
is
begin
return self.deptno
end;
end;
在employee_salobj对象类型体的成员实现中,显式地使用self进行对象属性的访问,当没有显式使用self关键字时,实际上也是隐式地使用了这个关键字。
由于STATIC属于静态方法级别,因此它不能接受或引用self关键字
定义构造函数
当定义了一个对象类型之后,系统会提供一个接收与每个属性相对应的参数的构造函数。
一般出于如下目的来自定义构造函数:
1.为对象提供初始化功能,以避免许多具有特别用途的过程只初始化对象的不同部分,可以通过构造函数进行统一初始化
2.可以在构造函数中为某些属性提供默认值,这样就能确保属性值的正确性,而不必依赖于调用者所提供的每一个属性值。
3.出于维护性的思考,在新的属性添加到对象中时,避免要更改调用构造函数的应用程序中的代码,这样可以使已经存在的构造函数调用继续工作.
构造函数的定义是一个与对象类型名称具有相同名称的函数,用于初始化对象,并能返回一个对象类型的新实例。在自定义构造函数时,要么就覆盖由oracle为每一个对象生成的默认构造函数,要么就定义一个有着不同方法签名的新构造函数。自定义构造函数使用constructor关键字进行声明.
自定义构造函数示例
create or replace type salary_obj as object(
percent number(10,4), --定义对象类型
sal number(10,2),
--自定义构造函数
constructor function salary_obj(p_sal number) return self as result)
instantiable
final;
/
--定义对象类型体
create or replace type body salary_obj
as
--实现重载的构造函数
constructor function salary_obj(p_sal number)
return self as result
as
begin
self.sal:=p_sal;
self.percent:=1.12;
return;
end;
end;
/
调用
declare
v_salobj1 salary_obj;
v_salobj2 salary_obj;
begin
v_salobj1:=salary_obj(1.12,3000); --使用默认构造函数
v_salobj2:=salary_obj(2000); --使用自定义构造函数
end;
定义MAP和ORDER方法
MAP方法:该函数会将对象实例根据一定的调用规则返回FATE,NUMBER,varchar2类型的标量类型,在映射对象类型为标量函数后,就可以通过对标量函数的比较来得到结果了。
ORDER方法:order方法只能对两个对象之间进行比较,必须是返回数值型结果的函数,根据结果返回正数,负数或零。该方法只有两个参数,SELF和另外一个要比较的对象类型,如果传递该参数为NULL,则返回NULL。
由于MAP方法一次调用时就将所有的对象映射为一个标量值,因此通常用在排序或合并很多对象时。而order方法一次仅能比较两个对象,因此在比较多个对象时需要被重复调用,效率会低一些。
定义MAP函数示例
create or replace type employee_map as object(
empno number(4),
sal number(10,2),
comm number(10,2),
deptno number(4),
MAP MEMBER FUNCTION convert return real --定义一个MAP方法
)NOT FINAL;
create or replace type body employee_emp as
MAP MEMBER FUNCTION convert return real is --定义一个MAP方法
begin
return sal+comm;
end;
end;
在定义了MAP函数后,PLSQL会隐式地通过调用MAP函数在多个对象间进行排序或比较。例如下面创建了一个emp_map_tab的对象表,向这个对象表插入多个对象,然后就可以对这个对象表进行对象的排序
create table emp_map_tab of employee_map;
insert into emp_map_tab values(7123,3000,200,20);
col val format a60;
select value(r) val,r.sal+r.comm from emp_emp_tab r order by 1;
定义order函数示例
create or replace type employee_order as object(
empno number(4),
sal number(10,2),
comm number(10,2),
deptno number(4),
ORDER MEMBER FUNCTION match(r employee_order) return integer --定义一个ORDER方法
)NOT FINAL;
create or replace type body employee_order as
ORDER MEMBER FUNCTION match(r employee_order) return integer is
begin
if ((SELF.sal+SELF.comm)<(r.sal+r.comm)) then
return -1;
elsif((SELF.sal+SELF.comm)>(r.sal+r.comm)) then
return 1;
else
return 0;
end if;
end match;
end;
定义了order函数后,就可以对两个对象进行比较。
declare
emp1 employee_order:=employee_order(7112,3000,200,20);
emp2 employee_order:=employee_order(7113,3800,100,20);
begin
if emp1>emp2 then
dbms_output.put_line('员工1的薪资加提成比员工2大');
elsif emp1<emp2 then
dbms_output.put_line('员工1的薪资加提成比员工2小');
else
dbms_output.put_line('员工1的薪资加提成与员工2相等');
end if;
end;
使用对象类型
对象类型一旦被创建成功,就被保存到了oracle数据字典中,用户可以再任何的PLSQL块,子程序或包中使用它来声明对象
1.声明对象
声明的对象必须是已经在oracle数据字典中存在的对象类型。在声明时可以直接使用对象的构造函数进行初始化,如果没有初始化,那么对象实例初始化为NULL。
declare
o_emp employee_order;
begin
o_emp:=employee_order(7123,3000,200,20);
dbms_output.put_line('员工编号为:'
||o_emp.empno
||'的薪资和提成为:'
||(o_emp.sal+o_emp.comm)
);
end;
在语句块的定义区定义了对象类型的变量,o_emp在定义区的初始化状态下为NULL,在语句块的执行部分,使用对象类型的构造函数实例化了对象类型,然后通过访问对象类型的属性来获取对象实例的信息.
也可以将对象类型作为存储过程和存储函数的形式参数,将对象实例从一个子程序传递到另一个子程序。
在子程序中使用对象类型
create or replace procedure changesalary(p_emp in employee_order)
as
begin
if p_emp is not null then --如果对象类型已经实例化,更新emp表
update emp set sal=p_emp.sal,comm=p_emp.comm where empno=p_emp.empno;
end if;
end changesalary;
--使用对象类型作为函数的传出参数
create or replace function getsalary(p_emp in out employee_order) return number
as
begin
if p_emp is not null then --如果没有实例化,那就实例化对象类型
p_emp:=employee_order(7125,5000,800,20);
end if;
return p_emp.sal+p_emp.comm;
end;
2.初始化对象
declare
o_emp employee_order:=employee_order(NULL,NULL,NULL,NULL);
begin
o_emp.empno:=7301;
o_emp.sal:=5000;
o_emp.comm:=300;
o_emp.deptno:=20;
end;
3.调用对象方法
在对象类型中,方法分为静态方法和实例方法,静态方法在定义时使用STATIC前缀,实例方法在定义时使用MEMBER关键字进行定义。这两类方法的调用方式也不同,实例方法在实例级别进行调用,而静态方法在对象类型的级别,也就是类级别进行调用。在一些高级程序设计语言比如JAVA,静态方法又称为类方法。
调用静态方法与实例方法实例
create or replace type employee_method as object(
empno number(4),
sal number(10,2),
comm number(10,2),
deptno number(4),
MEMBER PROCEDURE change_sal,
MEMBER FUNCTION get_sal return number,
STATIC PROCEDURE change_deptno(empno number,deptno number),
STATIC FUNCTION get_sal(empno number) return number
)not final;
调用
declare
o_emp employee_method:=employee_method(7369,5000,800,20);
v_sal number(10,2);
begin
v_sal:=o_emp.get_sal; --调用对象实例级别的方法
dbms_output.put_line('对象实例级别的工资为:'||v_sal);
v_sal:=employee_method.get_sal(o_emp.empno); --调用对象级别的方法
dbms_output.put_line('对象类型级别的工资为:'||v_sal);
end;
使用嵌套对象类型
嵌套对象类型是指一个对象中嵌入另一个对象类型。在为对象类型定义属性时,除了可以使用标量类型之外,还可以通过自定义的对象类型来提升整个对象类型的灵活性。
定义地址对象类型
create or replace type address_type
as object
(street_addr1 varchar2(25),
street_addr2 varchar2(25),
city varchar2(30),
state varchar2(2),
zip_code number,
--成员方法,返回地址字符串
MEMBER FUNCTION tostring return varchar2,
--MAP方法提供地址比较函数
MAP MEMBER FUNCTION mapping_function return varchar2
)
--定义地址对象类型体,实现成员方法与MAP函数
create or replace type body address_type
as
MEMBER FUNCTION tostring
return varchar2
is
begin
if (street_addr2 is not null)
then
return street_addr1
||CHR(10)
||street_addr2
||CHR(10)
||city
||','
||state
||' '
||zip_code;
else
return street_addr1 ||CHR(10)||city||','||state||' '||zip_code;
end if;
end;
MAP MEMBER FUNCTION mapping_function
return varchar2
is
begin
return to_char(NVL(zip_code,0),'fm00000')
||LPAD (NVL(city,''),30)
||LPAD (NVL(street_addr1,''),25)
||LPAD (NVL(street_addr2,''),25);
end;
end;
定义包含其他对象类型的类型
create or replace type employee_addr as object(
empno number(4),
sal number(10,2),
comm number(10,2),
deptno number(4),
addr address_type,
MEMBER FUNCTION get_emp_info return varchar2
)NOT FINAL;
--定义对象类型体,实现get_emp_info方法
create or replace type body employee_addr
as
MEMBER FUNCTION get_emp_info
return varchar2
is
begin
return '员工'||SELF.empno||'的地址为:'||SELF.addr.tostring;
end;
end;
为了使用employee_addr对象类型,在构造对象的实例时,必须要同时构造address_type的嵌套对象类型。
declare
o_address address_type;
o_emp employee_addr;
begin
--实例化地址对象类型
o_address:=address_type('玉兰一街','二巷','深圳','DG',523343);
--实例化员工对象类型
o_emp:=employee_addr(7369,5000,800,20,o_address);
--输出员工信息
dbms_output.put_line('员工信息为'||o_emp.get_emp_info);
end;
对象继承
继承是指在已存在的对象类型的基础上建立新对象类型的一种技术,新定义的类可包含现有类所声明的数据,定义及包含新定义的对象类型所增加的声明的组合。
对象类型继承由父类型和子类型组成,其中父类型用于定义可供子类型使用的公共的属性和方法,而子类型不但可以使用这些公共的属性和方法,还可以具有自己私有的属性和方法。
例如,在对现实世界的实体进行抽象时,可以发现企业中的员工都有一些共性,因此将其抽象出一个父类型,用来定义一个基类,比如员工都包含了姓名,性别,出生日期等,可以基于这些特性定义一个person_obj的对象类型,然后在该类型的基础上继承一个子类employee_personobj类型,除了具有person_obj的特性外,还具有员工证件号码,工资,提成和职位信息,类继承结构。
实现person_obj父对象
create or replace type person_obj as object(
person_name varchar(20),
gender varchar2(2),
birthdate DATE,
address varchar2(50),
MEMBER FUNCTION get_info return varchar2
)NOT FINAL;
create or replace type body person_obj
as
MEMBER FUNCTION get_info return varchar2
is
begin
return '姓名:' || person_name||',家庭住址:'||address;
end;
end;
子对象employee_personpbj的实现
create or replace type employee_personobj under person_obj(
empno number(6),
sal number(10,2),
job varchar2(10),
MEMBER FUNCTION get_emp_info return varchar2
);
create or replace type body employee_personobj as
MEMBER FUNCTION get_emp_info return varchar2 is
begin
return '员工编号:' || SELF.empno ||'员工名称:' || SELF.person_name||'职位:'||SELF.job;
end;
end;
为了从一个父对象中继承,在子对象中使用UNDER关键字,指定一个父对象名称,该父对象必须使用NOT FINAL关键字定义的对象。在子对象中新增了属性和方法后,就被合并到父对象中去了,因此可以看到在对象体实现时,可以直接使用SELF关键字访问父对象中的person_name属性。
使用方法:
declare
o_emp employee_personobj;
begin
--使用构造函数实例化员工对象
o_emp:=employee_personobj('张小五','F',TO_DATE('1983-01-01','YYYY-MM-DD'),'中信',7981,5000,'Programmer');
dbms_output.put_line(o_emp.get_info);
dbms_output.put_line(o_emp.get_emp_info);
end;
方法重载
所谓重载就是定义一个或多个具有同名的函数或过程,但是参数类型名个数不同,由编译器根据调用参数确定执行哪一个子程序。这种重载方式有时候也称为静态多态。在使用对象继承时,也可以使用方法重载。但是这种方法重载不同于过程或包中的重载,这种重载使用了动态方法调用的能力,也称为动态多态或运行时多态。也就是说具体的调用方法不是在编译时确定的,而是在代码实际执行时才确定的重载。
对象方法的重载使用OVERRIDING关键字,不是根据参数的个数来决定调用哪一个方法,而是根据优先级进行调用,也就是总是先调用子类的方法。
实现对象方法重载
create or replace type employee_personobj under person_obj(
empno number(6),
sal number(10,2),
job varchar2(10),
MEMBER FUNCTION get_emp_info return varchar2,
--定义重载方法
OVERRIDING MEMBER FUNCTION get_info return varchar2
);
create or replace type body employee_personobj as
MEMBER FUNCTION get_emp_info return varchar2 is
begin
return '员工编号:' || SELF.empno||'员工名称:'||SELF.person_name||'职位:'||SELF.job;
end;
--实现重载方法
OVERRIDING MEMBER FUNCTION get_info return varchar2 as
begin
return '员工编号:' ||SELF.empno||'员工名称:'||SELF.person_name||'职位':||SELF.job;
end;
end;
调用employee_personobj对象实例的get_info方法时,将看到的是来自重载方法中的消息
declare
o_emp employee_personobj;
begin
--使用构造函数实例化员工对象
o_emp:=employee_personobj('张小五','F',TO_DATE('1983-01-01','YYYY-MM-DD'),'中信',7981,5000,'Programmer');
dbms_output.put_line(o_emp.get_info);
end;
管理对象表
对象表就像普通的表一样,只是存储的事对象类型,该表中的每一个字段与对象的一个属性相对应,然后使用对象表的每一行或者称为每一条记录存储一个对象类型的实例。
create table emp_obj_table of employee_personobj;
创建对象表使用的是create table of 语句。
可以使用desc 查看到结构,和对象中包含的属性类型。
创建的对象表一旦引用了特定的对象类型,就不能使用drop type语句对对象类型进行删除,如果非要这样做,oracle会报错。
如果对象类型中包含嵌套的对象类型,那么嵌套的对象表类型会被作为一列存储到对象表中,比如employee_addr的addr属性是一个嵌套了address_type对象类型的属性,因此当创建一个employee_addr对象类型的数据表,会将addr作为一个单独的列。
create table emp_addr_table of employee_addr;
使用set desc depth all linenum on 语句。
插入对象表
插入数据的语法与向普通表插入数据一样,使用insert命令。
除了直接插入列值外,在向对象类型的表中插入数据时,可以先实例化一个对象类型,然后向insert语句的values子句传入构建的对象类型。
declare
o_emp employee_personobj;
begin
--使用构造函数实例化员工对象
o_emp:=employee_personobj('张小五','F',TO_DATE('1983-01-01','YYYY-MM-DD'),'中信',7981,5000,'Programmer');
insert into emp_obj_table values(o_emp);
end;
检索对象表
对象表的查询与关系表的查询一样。
1.value函数
在查询语句中使用value函数将返回存储在对象表中的对象实例,因此对于查询的单行记录,可以使用select into 语句将查询出来的值插入到预定义的对象实例中;对于查询返回的多行记录,可以使用游标来获取查询出来的对象实例。
select value(e) from emp_obj_table e;
该查询返回了存储在emp_obj_table表中所有的对象实例,每一行一个对象实例。
下面演示使用select into 将查询的结果赋值给一个对象类型的变量:
declare
o_emp employee_personobj;
begin
--使用select into 语句将value函数返回的对象实例插入到对象类型的变量
select value(e) into o_emp from emp_obj_table e where e.person_name='张小五';
--输出对象类型的属性值
dbms_output.put_line(o_emp.person_name||'的职位是:'||o_emp.job);
end;
使用游标和value函数查询多行数据结果
declare
o_emp employee_personobj;
cursor all_emp
is
select value(e) as emp from emp_obj_table e;
begin
for each_emp in all_emp
loop
o_emp:=each_emp.emp;
--输出对象实例信息
dbms_output.put_line(o_emp.person_name||'的职位是:'||o_emp.job);
end loop;
end;
2.REF函数
与value相对应的ref函数也可以用来检索对象表中的数据,但是由其名可知,它返回的事一个对象的引用。二者之间的主要区别在于引用类型返回的只是指向对象实际位置的一个指针,而value类型会把对象副本从一个子程序传递到另一个子程序,程序执行时的效率可能会降低。
使用共享对象类型,可以避免数据不必要的重复,而且在共享的内容更新时,任何引用所指向的内容也会被立即更新。
create type address as object( --创建地址类型
street varchar2(35),
city varchar2(15),
state char(2),
zip_code integer
);
create table addresses of address; --创建地址对象表
create type person as object( --创建人员对象类型
person_name varchar2(15),
birthday DATE,
home_address REF address, --使用ref关键字,指定属性为指向另一个对象表的对象
phone_number varchar2(15)
);
create table persons of person; --创建人员对象表
插入数据
insert into addresses values(address ('玉兰','深圳','GD','321413'));
insert into persons values(person ('王小五',TO_DATE('1983-01-01','YYYY-MM-DD'),(select ref(a) from addresses a where street='玉兰'),'1312332'));
使用PL/SQL语句块向对象表中插入引用类型的对象
declare
addref ref address; --定义一个引用类型的对象
begin
select ref (a) into addref from addresses a where street='玉兰';
insert into persons values(person('武大郎',TO_DATE('1983-01-01','YYYY-MM-DD'),addref,'1312312'));
end;
在语句块中使用ref关键字定义了一个指向address对象类型的引用类型,然后使用ref函数从address表中返回匹配的引用类型的指针,最后将这个引用类型作为home_address属性的值插入到persons表中.
当对象表中包含引用类型时,如果直接使用select语句进行查询,引用类型的列是一串数字码,而且引用类型的对象无法直接访问其属性。
select person_name,home_address from persons;
如果使用了deref函数,则可以查询到引用类型所指向的地址类型的值。
select person_name,deref (home_address) as home from persons;
在PL/SQL语句中使用ref类型的变量时,不能直接访问对象的属性,必须首先通过deref函数解除引用后,才能使用对象类型的属性.
更新对象表
更新对象表时既可以把对象表看作是一个普通的关系型表,与关系表一样把对象表中的每个属性作为一列调用作为一列调用update语句,也可以将一行看作是一个对象,在update语句中对对象进行赋值。
update emp_obj_table empobj set empobj.gender='M' where empobj.person_name='张小五';
另一种方法是直接更新一个对象表中的对象实例,因此需要先实例化一个对象实例。
update emp_obj_table empobj set empobj=employee_personobj('李晓琪','F',TO_DATE('1983-01-01','YYYY-MM-DD'),'众泰',7981,7000,'Testing') where person_name='张小五';
在操纵对象表时,还可以在where子句中把一行看作一个对象,可以使用对象标识符来唯一标识要更改的对象,对象标识符是对象表中为了唯一标识一条记录而定义的一串数字值,对于一个对象来说,需要使用REF函数来获取对象的标识符,在where语句中使用对象类型进行检索的示例
在where子句中使用对象类型
declare
emp_ref ref employee_personobj; --定义引用对象类型
begin --从对象表中获取对刘小燕的对象引用
select ref(e1) into emp_ref from emp_obj table e1 where person_name='刘小燕';
update emp_obj_table emp_obj set emp_obj=employee_personobj('何晓峰','F',TO_DATE('1985-08- 01','YYYY-MM-DD'),'本田',7981,7000,'developer') where ref (emp_obj)=emp_ref;
end;
如果对象表的属性列表中包含了引用类型,也就是说包含了指向行对象数据的指针,如果要修改其列所引用的数据,就必须修改相应的行对象。例如在persons表中,home_address字段是一个指向address对象类型的引用,因此如果要update这个包含引用类型的表。
declare
addr address;
begin
select deref(home_address) into addr from persons where person_name='张小五';
addr.street:='五一';
update address set street=addr.street where zip_code='21312';
end;
先使用deref函数返回指针所指向的address对象实例的引用,然后将更改的信息更新回address表中就完成了对引用记录的更改。
删除对象表
与删除普通表类似,对象表的删除使用delete语句,下面的语句像删除普通的关系表一样删除emp_obj_table表中员工名称为张小五的记录
delete from emp_obj_table where person_name='张小五';
与update类似,还可以在where子句中使用引用类型的对象进行删除。例如要删除emp_obj_table表中员工名称为刘小燕的记录。
declare
emp_ref ref employee_personobj;
begin
select ref (e1) into emp_ref from emp_obj_table e1 where person_name='刘小燕'; --从对象表中获取对刘小燕的对象引用
delete from emp_obj_table emp_obj where ref (emp_obj)=emp_ref
end; --使用delete语句删除emp_obj_table表中刘小燕的记录
通过使用ref函数,将员工名称为刘小燕的记录进行了正确的删除。
创建对象列
除了将整个对象作为表中的列来存储的对象表之外,还可以为关系表中的某一列的属性指定为对象类型,这种表称为带对象列的关系表。这种带对象类型的关系表与对象表的主要不同在于对象表时通过使用对象标识符来引用对象实例的,而对于列对象来说是没有oracle对象标识符的,列对象的对象实例属于关系型数据库中的记录,具有一个rowid值作为标识符。
定义dept_obj对象类型
create or replace type dept_obj as object(
deptno number(10),
dname varchar2(30),
loc varchar2(30),
MEMBER FUNCTION get_dept_info return varchar2
) INSTANTIABLE NOT FINAL;
定义对象类型体
create or replace type body dept_obj as
MEMBER FUNCTION get_dept_info return varchar2 is
begin
return '部门编号:' || SELF.deptno ||'部门名称:'||SELF.dname||'职位:'||SELF.loc;
end;
end;
对象类型dept_obj封装了部门信息,可以看到具有部门编号,部门名称和部门的地址信息,接下来创建一个名为emp_colobj的表,该表用来存放员工信息,但是在部门列中,将使用dept_obj对象类型作为列类型,可以再表中存储关于部门的详细信息。
create table emp_colobj(
empno number(10) NOT NULL primary key,
ename varchar2(30),
job varchar2(30),
sal number(10,2),
deptcol dept_obj
) --dept列指定为dept_obj对象类型
在对象表中绑定了某个对象类型后,不能直接对对象表中的列及类型进行操作,比如新增列或对列进行修改等,但是关系表中包含对象列类型则没有这个限制,可以使用标准的ALTER TABLE语句来进行操作.
使用对象视图
为了创建一个对象视图,必须首先创建一个与底层的数据表的列具有相匹配属性的对象类型。
定义emp_tbl_obj对象类型
create or replace type emp_tbl_obj as object(
empno number(6),
ename varchar2(10),
job varchar2(18),
mgr number(4),
hiredate DATE,
sal number(7,2),
comm number(7,2),
deptno number(2),
MEMBER FUNCTION get_emp_info return varchar2
)INSTANTIABLE NOT FINAL;
create or replace type body emp_tbl_obj as
MEMBER FUNCTION get_emp_info return varchar2 is
begin
return '员工编号:' ||SELF.empno||'员工名称:'||SELF.ename||'职位:'||SELF.job;
end;
end;
在创建对象视图时,必须要确定要使用的OID(对象标识符).对象标识符是用来唯一标识一行对象的一个字符串,通过OID可以保证对对象实例引用的唯一性。OID标识符仅在对象表和对象视图上被创建,一旦一个OID被赋给了一个对象,那么将永远属于那个对象。通常情况下,oracle会自动产生OID值,不需要要手工干预,但是在定义对象视图时,可以覆盖系统产生OID的机制,更改为使用对象表的主键来替代.
create view view_name of object_name
with object identifier(primary_key)
as
sql_statement;
view_name指定对象视图的名称,object_name用于指定对象视图的对象名称,with object identifier用于指定OID方式,可以指定表的主键作为OID,sql_statement是对关系表的sql查询语句时。
create view emp_view
of emp_tb1_obj
with object identifier(empno)
as
select e.empno,e.ename,e.job,e.mgr,e.hiredate,e.sal,e.comm,e.deptno from emp e;
使用对象类型的视图
declare
o_emp emp_tb1_obj;
begin
--查询对象类型
select value(e) into o_emp from emp_view e where empno=7369;
--输出对象类型的属性
dbms_output.put_line('员工'||o_emp.ename||'的薪资为:'||o_emp.sal);
dbms_output.put_line(o_emp.get_emp_info); --调用对象类型的成员方法
end;
管理对象类型
1.查看对象类型
COL type_name format A20;
select type_name,attributes,final,typecode from user_types where type_name like 'EMP%' and typecode='OBJECT';
通过使用select connect by语句,还可以根据type_name和supertype_name来查询对象的继承结构.
set pagesize 500;
select rpad (' ',3 * (LEVEL-1)) |from user_types where typecode='OBJECT' CONNECT BY PRIOR type_name=superty
2.修改对象类型
如果已经基于对象类型建立了其他的对象类型或对象表,那么在为对象类型增加或删除属性时,必须要使用cascade关键字.
--添加mgr属性
alter type employee_personobj add attribute mgr number(6) cascade;
--删除sal属性
alter type employee_personobj drop attribute sal cascade;
修改对象类型的成员方法
alter type employee_personobj drop member function get_emp_info return varchar2 cascade;
--新增一个get_employee的成员方法
alter type employee_personobj add member function get_employee return varchar2 cascade;
--更改对象类型体,以便增加在对象类型规范中定义的方法
create or replace type body employee_personobj as
member function get_employee return varchar2 is
begin
return '员工编号:' ||SELF.empno||'员工名称:'||SELF.person_name||'职位:'||SELF.job;
end;
end;
如果从基类删除一个方法,那么也必须修改覆盖被删除方法的子类,可以用alter type 的casade选择来判断是否有子类被影响到:如果有子类覆盖了方法,那么语句被回滚。为了能成功地从基类删除一个方法。
1.先从子类删除方法
2.从基类删除方法,然后用不带overriding关键字的alter type把它重新添加进去.
本文转自潘阔 51CTO博客,原文链接:http://blog.51cto.com/pankuo/1630258,如需转载请自行联系原作者
PL/SQL异常处理
异常处理语句块结构
定义区:定义异常,如果使用预定义异常,则不用在定义区定义异常
执行区:可以显式地触发异常,也可以由PL/SQL引擎触发异常
异常处理区:只要在执行过程中出现了异常,那么执行区中后续的语句将立即停止执行,语句执行流程 跳转到异常处理区
异常处理结构示例
declare
e_duplicate_name exception;
v_ename emp.ename%TYPE;
v_newname emp.ename%TYPE:='史密斯';
begin
select ename into v_ename from emp where empno=7369;
if v_ename=v_newname
then
RAISE e_duplicate_name;
end if;
insert into emp values(7881,v_newname,'职员',NULL,TRUNC(SYSDATE),2000,200,20);
exception
when e_duplicate_name
then
dbms_output.put_line('不能插入重复的员工名称');
end;
对于未处理的异常,可以通过when others then来提供一个统一的异常处理
declare
e_duplicate_name exception;
v_ename emp.ename%TYPE;
v_newname emp.ename%TYPE:='史密斯';
begin
select ename into v_ename from emp where empno=7369;
if v_ename=v_newname
then
RAISE e_duplicate_name;
end if;
insert into emp values(7881,v_newname,'职员',NULL,TRUNC(SYSDATE),2000,200,20);
exception
when e_duplicate_name then
dbms_output.put_line('不能插入重复的员工名称');
when others then
dbms_output.put_line('异常编码:'||SQLCODE||'异常信息:'||SQLERRM);
end;
预定义异常使用示例
declare
v_tmpstr varchar2(10);
begin
v_tmpstr:='这是临时句子';
exception
when VALUE_ERROR
then
dbms_output.put_line('出现了VALUE_ERROR错误'
||'错误编号:'
||SQLCODE
||'错误名称:'
||SQLERRM
);
end;
自定义异常
declare
e_nodeptno exception;
begin
NULL;
end;
异常是一种错误的表现形式,而不是一个真正的变量,因此不能再赋值语句或SQL语句中使用异常,但是异常和变量的作用范围和规则是相同的。
作用域范围
自定义异常和变量的作用域和规则相同。
1.在同一个块中不能声明一个异常超过两次,但是可以再不同的块中声明相同的异常。但是可以再不同的块中声明两个相同的异常。
2.内层块定义的异常不可以被外层使用,内层可以调用外层定义的异常
3.如果在子块重新声明外部块中同名的异常,将覆盖外部块中的全局异常,使得子块不能引用外部块中的全局异常,但是可以再在标签块中声明相同的异常
使用EXCEPTION_INIT
如果一些异常并没有异常名称,此时在when子句中无法使用具体的异常名称,必须要使用others异常处理器进行捕捉,通过EXCEPTION_INIT编译指示,可以为这些不在预定义异常范围之类的异常添加名称。
编译指示是指能在编译期而非运行时进行处理的编译指令
编译指令EXCEPTION_INIT将告诉编译器,将异常名称和错误编号关联起来,使得在PLSQL语句块中可以使用名称来引用所有的内部异常。
declare
e_missingnull exception;
pragma exception_init(e_missingnull,-1400);
begin
insert into emp(empno)values(null);
commit;
exception
when e_missingnull then
dbms_output.put_line('触发了ORA-1400错误!'||SQLERRM);
rollback;
end;
使用RAISE_APPLICATION_ERROR
RAISE_APPLICATION_ERROR在子程序内部使用时,能够帮助用户从存储子程序中抛出用户自定义的错误信息。
declare or replace procedure registeremployee (
p_empno in emp.empno%TYPE,
p_ename in emp.ename%TYPE,
p_sal in emp.sal%TYPE,
p_deptno in emp.deptno%TYPE
)
as
v_empcount number;
begin
if p_empno is null
then
raise_application_error(-20000,'员工编号不能为空');
else
select count(*) into v_empcount from emp where empno=p_empno;
if v_empcount>0
then
raise_application_error(-20001,
'员工编号为:'||p_empno
||'的员工已存在!'
);
end if;
end if;
if p_deptno is null
then
raise_application_error(-20002,'部门编号不能为空');
end if;
insert into emp(empno,ename,sal,deptno)values(p_empno,p_ename,p_sal,p_deptno);
exception
when others then
raise_application_error(-20003,'插入数据时出现错误!异常编码:'
||SQLCODE
||'异常描述'
||SQLERRM
);
end;
输出 begin
RegisterEmployee(7779,'李明',2000,NULL);
end;
ORA-20003:插入数据时出现错误!异常编码:-20002异常描述ORA-20002;
抛出异常
使用RAISE语句抛出异常示例
declare
e_nocomm exception;
v_comm number(10,2);
v_empno number(4):=&empno;
begin
select comm into v_comm from emp where empno=v_empno;
if v_comm is null
then
RAISE e_nocomm;
end if;
exception
when e_nocomm then
dbms_output.put_line('选择的员工没有提成!');
end;
处理异常
可以使用OR关键字进行分隔即可。
declare
e_nocomm exception;
v_comm number(10,2);
v_empno number(4):=&empno;
begin
select comm into v_comm from emp where empno=v_empno;
if v_comm is null
then
RAISE e_nocomm;
end if;
exception
when e_nocomm or no_data_found then
dbms_output.put_line('出现了异常!');
when others then
dbms_output.put_line('任何其他未处理的异常');
end;
使用SQLCODE和SQLERRM
尽管一个异常在同一时刻只能抛出一次,但是实际上错误的消息文本可能包含来自多个异常的消息这是因为异常具有传递特性。
SQLCODE函数返回当前的错误编码。
SQLERRM函数返回消息文本
declare
e_nocomm exception;
v_comm number(10,2);
v_empno number(4):=&empno;
begin
select comm into v_comm from emp where empno=v_empno;
if v_comm is null
then
RAISE e_nocomm;
end if;
exception
when others then
dbms_output.put_line('错误编码:'||SQLCODE||'错误消息:'||SQLERRM);
end;
SQLERRM函数还可以接受一个负数的单精度参数,它将返回与该数字相关的文本,否则将返回当前异常的错误消息。
when others then 必须单独出现
dbms_output.put_line('错误编码:'||SQLCODE||'错误消息:'||SQLERRM(0));
想返回NO_DATA_FOUND的错误消息,可以传递错误消息号100
异常的传递
异常的传递是指当异常被抛出时,执行立即跳转到exception语句块中的异常处理器,异常处理器中查找是否匹配的异常,如果在当前的PL/SQL块或子程序中没有找到对应的异常处理器,那么这个异常会向其PLSQL块的外层或子程序的调用方传递,直到没有可以搜索到的块为止,这里PLSQL会向PLSQL引擎抛出一个未处理的异常。
执行时异常传递
执行时异常传递是指在PLSQL块的执行部分抛出的异常的传递机制,当在执行部分抛出异常后,PLSQL使用下面的机制来确定使用哪一个异常处理器。
1.如果当前PL/SQL的异常处理部分具有一个匹配的异常处理器,则执行当前块的异常处理器,成功完成语句块,然后将控制权传递到外层语句块
2.如果当前PLSQL块中没有匹配的异常处理器,则在当前块中抛出的异常会被传递到外层的异常处理器,然后执行外层语句块中的步骤1中的匹配操作
3.如果已经到了顶层,没有外层语句块了,则异常将被传递到调用环境。
例如
declare
e_outerexception exception;
e_innerexception exception;
e_threeexception exception;
begin
begin
RAISE e_innerexception;
RAISE e_outerexception;
RAISE e_threeexception;
exception
when e_innerexception;
end;
exception
when e_outerexception then
end;
1.首先搜寻内层块的异常处理区中的异常,在本地块中的e_innerexception异常具有一个异常处理器,因此该异常将在本地块中被处理
2.其他的两个异常将向外传递,传送给外层PL/SQL块的异常处理区,在外层的异常处理区中具有一个e_outerexception的异常处理器,因此该异常将在外层块中被捕捉并处理
3.异常e_threeexception并没有任何处理器,该异常将被传递到调用环境,由调用环境进行处理。比如SQL*PLUS会弹出一个异常。
声明时异常传递
示例
begin
declare
v_ename varchar2(2):='ABC';
begin
dbms_output.put_line(v_ename);
exception
when others then
dbms_output.put_line('产生了异常');
end;
exception
when others then
dbms_output.put_line('错误编号:'
||SQLCODE||'错误消息:'
||SQLERRM);
end;
在声明一个变量v_ename,并赋予了初始值ABC,这个赋值产生了ORA-06502异常,因为varchar2(2)不能包含ABC这3个字符。尽管在子块的exception区具有when others then 异常处理语句,但是声明区中抛出的异常并不会被本地块所捕获,而是向外层传递,被外层的异常处理区中的others异常处理器捕获,因此最终可以看到如下所示:
错误编号:-6502 错误消息:ORA-06502:PL/SQL:数字或值错误:字符串缓冲区太小。
异常处理器中的异常
declare
e_outerexception exception;
e_innerexception exception;
e_threeexception exception;
begin
begin
RAISE e_innerexception;
RAISE e_outerexception;
RAISE e_threeexception;
exception
when e_innerexception then
RAISE e_outerexception;
when e_outerexception then
when others then
end;
exception
when e_outerexception then
end;
在嵌套的内层PL/SQL块中,在执行区触发了3个异常,在异常处理区中捕获取e_innerexception异常,在处理该异常的处理器中,使用RAISE语句又触发了e_outerexception.可以看到即便在内层块中包含了e_outerexception的异常处理器,但是异常并不会在内存块中的处理器中被捕获,而是跳转到了外层的异常处理器进行处理。
重新抛出异常
declare
e_nocomm exception;
v_comm number(10,2);
v_empno number(4):=&empno;
begin
select comm into v_comm from emp where empno=v_empno;
if v_comm is null
then
RAISE e_nocomm;
end if;
exception
when others then
dbms_output.put_line('错误编码:'||SQLCODE||'错误消息:'||SQLERRM(100));
RAISE;
end;
当e_nocomm触发后,会被others异常处理器处理,在该异常处理器中,首先输出了异常信息,然后调用RAISE语句将异常重新抛出。此时由于已经是最外层的语句块,异常将被传递到调用环境,因此SQLPLUS将触发异常。
可以看到当不为RASIE指定任何异常名称时,程序就会把当前的异常重新抛出。
这种重新抛出异常的方式只允许在异常程序中这样做。
异常处理准则
要实现从异常恢复,可以将异常包装在其自己的子块中,在子块中编写对应的异常控制程序,这样一旦在子块中有错误发生,子块内部的异常处理器就能捕获并处理异常。当子块结束时,就可以继续执行外层块中的下一条语句,因此如下
declare
e_duplicate_name exception;
v_ename emp.ename%TYPE;
v_newname emp.ename%TYPE:='史密斯';
begin
begin
select ename into v_ename from where empno=7369;
if v_ename=v_newname
then
RAISE e_duplicate_name;
end if;
exception
when e_duplicate_name then
v_newname:='刘大夏';
end;
insert into emp values(7881,v_newname,'职员',NULL,TRUNC(SYSDATE),2000,200,20);
exception
when others then
dbms_output.put_line('异常编码:'||SQLCODE||'异常信息:'||SQLERRM);
end;
可以看到,现在查询并检测同名的逻辑被封装到一个PL/SQL子块中,在子块中柏涵了异常处理语句,当异常触发时,将v_newname更改为另一个名字,从内层块中退出后,会继续执行insert语句,直到PL/SQL语句块终止。
获取异常抛出的位置
示例
declare
v_empno1 number(4):=&empno1;
v_empno2 number(4):=&empno2;
v_empno3 number(4):=&empno3;
v_sal1 number(10,2);
v_sal2 number(10,2);
v_sal3 number(10,2);
begin
select sal into v_sal1 from emp where empno=v_empno1;
select sal into v_sal2 from emp where empno=v_empno2;
select sal into v_sal3 from emp where empno=v_empno3;
exception
when NO_DATA_FOUND then
dbms_output.put_line('错误编号:'||SQLCODE||'错误消息:'SQLERRM);
end;
当触发了NO_DATA_FOUND异常时,如何知道是哪个select语句触发了异常?
方法一:使用递增的计数器标识SQL语句
declare
v_empno1 number(4):=&empno1;
v_empno2 number(4):=&empno2;
v_empno3 number(4):=&empno3;
v_sal1 number(10,2);
v_sal2 number(10,2);
v_sal3 number(10,2);
v_selectcounter number:=1;
begin
select sal into v_sal1 from emp where empno=v_empno1;
v_selectcounter:=2;
select sal into v_sal2 from emp where empno=v_empno2;
v_selectcounter:=3;
select sal into v_sal3 from emp where empno=v_empno3;
exception
when NO_DATA_FOUND then
dbms_output.put_line('错误编号:'||SQLCODE||'错误消息:'||SQLERRM||'触发异常的位置是:'|| v_selectcounter);
end;
方法二:将每一个select语句定义到一个子块中去,这样就可以知道哪个select语句触发了异常,当然这种方式将导致出现多个异常消息,因为没一条select语句都会被执行。
declare
v_empno1 number(4):=&empno1;
v_empno2 number(4):=&empno2;
v_empno3 number(4):=&empno3;
v_sal1 number(10,2);
v_sal2 number(10,2);
v_sal3 number(10,2);
begin
begin
select sal into v_sall from emp where empno=v_empno1;
exception
when NO_DATA_FOUND then
dbms_output.put_line('错误编号:'||SQLCODE||'错误消息:'||SQLERRM||'触发异常的位置是 1');
end;
begin
select sal into v_sal2 from emp where empno=v_empno2;
exception
when NO_DATA_FOUND then
dbms_output.put_line('错误编号:'||SQLCODE||'错误消息:'||SQLERRM||'触发异常的位置是 2');
end;
begin
select sal into v_sal3 from emp where empno=v_empno3;
exception
when NO_DATA_FOUND then
dbms_output.put_line('错误编号:'||SQLCODE||'错误消息:'||SQLERRM||'触发异常的位置 是3');
end;
exception
when NO_DATA_FOUND then
dbms_output.put_line('错误编号:'||SQLCODE||'错误消息:'||SQLERRM);
end;
方法三:可以使用DBMS_UTILITY.FORMAT_ERROR_BACKTRACE函数来获取错误位置,这个函数是oracle10g以后的版本提供的
declare
v_empno1 number(4):=&empno1;
v_empno2 number(4):=&empno2;
v_empno3 number(4):=&empno3;
v_sal1 number(10,2);
v_sal2 number(10,2);
v_sal3 number(10,2);
begin
select sal into v_sal1 from emp where empno=v_empno1;
select sal into v_sal2 from emp where empno=v_empno2;
select sal into v_sal3 from emp where empno=v_empno3;
exception
when NO_DATA_FOUND then
dbms_output.put_line('错误编号:'||SQLCODE||'错误消息:'||SQLERRM|| dbms_utility.format_backtrace);
end;
在发生错误时,oracle会为最近一次生成的异常设置一个栈,并跟踪它的传递过程。
异常与事务处理
抛出一个异常并不会终止一个事务,除非在异常处理器中显式地使用了ROLLBACK或者COMMIT语句,但是这里有一个问题,如果顶层的语句块存在一个未处理的异常,该异常将被传递到调用环境,那么事务将被服务端自动回滚。
如果想在异常发生后,不放弃事务,不进行回滚,重新再处理一次,可以按如下的3步来实现。
1.将事务放在一个子块中
2.把子块放入一个循环,重复执行事务
3.在开始事务之前标记一个保存点,如果事务执行成功,能提交事务并退出循环,如果执行失败,就将控制权交给异常处理程序,事务回滚到保存点,然后重新尝试执行事务。
在异常中重复事务执行代码
declare
e_duplicate_name EXCEPTION;
v_ename emp.ename%TYPE;
v_newname emp.ename%TYPE := '史密斯';
begin
loop
begin
savepoint 开始事务;
select ename into v_ename from emp where empno=7369;
if v_ename=v_newname
then
raise e_duplicate_name;
end if;
insert into emp values(7881,v_newname,'职员',NULL,TRUNC(SYSDATE),2000,200,20);
commit;
exit;
exception
when e_duplicate_name then
rollback to 开始事务;
v_newname:='刘大夏';
end;
end loop;
end;
本文转自潘阔 51CTO博客,原文链接:http://blog.51cto.com/pankuo/1630253,如需转载请自行联系原作者
oracle workflow 详解
原文地址:http://hutianci.iteye.com/blog/1023363
1概述... 21.1工作流的概念... 21.2工作流的目的... 21.3工作流体系结构:... 21.4工作流开发工具安装... 22工作流的组成及创建... 22.1单据类型(Item Type)... 32.2属性(Attribute)... 4
2.2.1定义一个URL属性... 6
2.2.2定义一个Form属性... 6
2.2.3定义一个Document属性... 72.3流程(Process)... 7
2.3.1在你画流程图之前,你必须先定义一个流程活动来表达流程图:... 7
2.3.2画流程图... 82.4消息(Message)... 102.5函数(Function)... 122.6通知(Notification)... 132.7查找类型(Lookup Type)... 143工作流引擎... 153.1概览... 153.2启动工作流流程... 163.3活动的状态... 163.4通知工作流引擎... 163.5工作流引擎的数据库包... 163.6工作流相关的主要表结构... 17
1.1工作流的概念
Workflow是EBS的基础架构技术之一,系统中大部分流程性的通知和审批控制、账户按规则自动生成都是通过Workflow实现的1.2工作流的目的
1. 在业务流程中发送、提供附加信息,这些附加信息以自助的Web页面或Form的形式存在,例如:在Application中审批采购订单时,采购订单的form会作为附加信息提供给审批人。
2. 定义业务规则。使用WorkflowBuilder(图形化工具)修改业务规则的流程;使用PL/SQL写流程中活动的源代码。
3. 发送电子邮件。工作流可以以电子邮件的方式向任何Application的用户发送通知。
1.3工作流体系结构:
1. 开发者客户端:只要在PC上安装了WorkflowBuilder并且可以连接到数据库,就可以用来创建和修改工作流的定义。
2. 数据库服务器:工作流是和Oracle的数据库集成在一起的。
3. 应用服务器:包括Oracle Web Server、WebDB以及发送电子邮件的后台程序。
4. 最终用户端:安装了浏览器可以使用Application、接收工作流发出的邮件和使用用来监控工作流的Web页面。
1.4工作流开发工具安装
在http://www.oracle.com/technology ... workflow/index.html下载页面上找到workflow client并下载下来,即为所要安装的Workflow
Builder。WorkflowBuilder介绍:
用来定义和修改工作流的图形化工具,运行在Windows操作系统上。可以将一个或几个工作流的定义保存到数据库中或保存成一个文件。
WorkflowBuilder由‘对象浏览器'和‘流程图表' 两种窗口构成。‘对象浏览器'以树状结构显示了工作流的对象构成,最上面一层是一个数据库连接或一个wft文件,下面一层显示的是在数据库或文件中包含的工作流定义,更下面一层的是构成一个完整的工作流定义的对象(例如:属性、流程、功能、通知、消息和快速编码)。‘流程图表'窗口使用‘对象浏览器'窗口中的对象来创建和修改工作流中的流程。
工作流的流程是由活动(活动可以是功能、通知或另一个工作流流程)和各活动之间的连线构成的。在应用中可以通过工作流引擎的API来调用一个工作流的流程。一个工作流的流程开始于一个‘开始'的活动。工作流引擎会自动地一个接一个地运行流程中的活动,直到遇到一个通知或一个暂停工作流流程的活动。当工作流引擎遇到一个通知时,它会调用通知程序来发送信息给某个角色,当用户或角色对通知作了回应,工作流引擎会接着运行后面的活动,直到遇到一个‘结束'活动。
在工作流的流程中可以使用循环、分支和平行结构以及使用子流程来实现复杂的业务规则。2工作流的组成及创建
在Workflow Builder的对象浏览器窗口中以树状的方式显示了工作流的对象的组成,在树状结构的第三层(也就是项目类型的下面一层),包含以下这些分支:属性(Attribute)、流程(或子流程Process)、功能(Function)、通知(Notification)、信息(Message)和快速编码(Lookup Types);一个完整的工作流的定义就是由这些对象构成的。
2.1单据类型(Item Type)
组成工作流的对象的集合,在工作流流程中的每一个对象都必须从属于一个项目类型。企业的业务有很多类别,同一种业务对应有不同的业务流程,同时在业务流转的过程中处理不同的业务信息。单据类型其实就相当于一个业务类型。
单据类型属性主要是定义业务信息,另外还包含一些在其它对象需要引用的信息,例如通知里的接收人就可以作为单据类型的属性。单据类型的另外一项功能就是对工作流程进行分类 ,所有工作流里的其它对象都属于一个单据类型。
Item Type是个抽象的概念,为方便理解,可以看作就是一个工作流。
创建一个Item Type:
§ 如果你没有打开一个data store,从File里选择New菜单创建一个新的Data Store,然后从鼠标右键菜单里选择New Item Type在浏览树里定义一个新的Item Type,此时单据类型的属性页会出现;
§ 每一个单据类型有一个大写的内部名称,最多8个字符,所有的工作流API、PL/SQL过程,SQL脚本在识别单据类型的时候都引用单据类型的内部名称。在内部名称里不能包括":"和"/"。 Internal Name相当于Code,在程序中经常用它来标志一个工作流;
§ 输入一个翻译性的名字,该名字是一个长一些的描述性名称,Display Name相当于User Name,你也可以在Description中为单据类型输入一段摘要信息;
§ 指定单据类型的持续类型,Persistence:当你定义一个单据类型时,你必须指定它的Persistence Type,它主要是控制单据类型实例的状态审核追踪的保存时间,如果你把持续类型设置为Permanent,则运行时的状态信息会一直保存,除非你调用WF_Purge.Totalperm()过程清除;如果你把持续类型设置为Temporary,你必须指定持续的天数,临时持续类型的单据类型实例在完成之后至少保存你指定的持续天数,在'n'天过后,你可以调用任何一个WF_Purge的API清除单据类型的运行时信息。;
§ Selector:如果你的单据类型有或者将有多个可运行的流程活动,定义一个PL/SQL函数决定在一个特殊的条件下运行哪一个流程活动。你也可以扩展这个选择函数为一个回调函数,这样在流程执行的过程中,如果会话信息中断的话,可以恢复单据类型实例的上下文信息。当你从通知详细页面上去查看一个页面属性时特别有用;
§ 选择"Roles"页规定能访问单据类型的角色(没有用过);
§ 选择"Access"页规定单据类型的访问和定制级别;
§ 选择"应用"保存你的修改;
§ 在浏览树里会出现一个二级分支来表示你刚创建的单据类型,你可以在浏览树里双击单据类型浏览或修改单据类型的属性;
2.2属性(Attribute)
就好像PL/SQL中的全局变量,它可以全局地被工作流流程中的活动引用(例如:在审批采购订单的工作流流程中,采购订单的编号就是一个项目类型属性,在审批流程的活动中可以随时获得这个属性的值,也就是采购订单的编号)。
单据类型属性是和单据类型联系在一起的,它通常充当一个全局变量,可以被流程里的活动引用或者修改。单据类型属性包含流程完成所必需的单据信息。对于活动需要引用的信息或者通知消息里包含的信息,你通常需要定义一个单据类型属性。
创建一个Attribute:
§ 选中Item Type 子树中的Attribute/右键/New Attribute,出现属性页面;
§ 输入一个大写的内部名称;
§ 输入一个显示名称,该名称会出现在浏览树里;
§ 你可以输入一段描述摘要信息;
§ 选择属性的数据类型,在定义活动属性的时候,没有Form,URL,Document属性类型。一般情况下可选以下几项:
属性具有10种类型,下面给出每种类型的说明:
Oslash; Text:属性的值是一个字符串文本;
Oslash; Number:属性的值是一个数字,你可以为数字类型的属性定义一个格式掩码;
Oslash; Date:属性的值是一个日期类型,你也可以为日期类型的属性定义一个格式掩码;
Oslash; Lookup:属性的值是一个查找类型的代码;
Oslash; Form:属性的值是一个Oracle应用的内部窗体函数名称以及函数的参数;如果你在通知消息里包括了一个窗体类型的属性,在查看通知的时候,会显示一个窗体附件的图标,可以让用户浏览到相应的页面。
Oslash; URL:属性的值是一个网络的URL地址,如果你在通知活动里包含了一个URL引用属性,在查看通知的时候,会有一个指向URL的标识,用户可以完成活动也可以访问URL地址或的额外的信息。
Oslash; Document:属性的值是一个附件文档,你可以在默认值里指定文档的类型:PL/SQL文档,通过存储过程把数据库里的信息以文档的方式表达;DM文档,一个被外部文档管理系统管理的文档。
Oslash; Role:属性的值是一个角色的内部名称,如果在一个通知里包含了一个角色类型的属性,则该属性会自动解释为角色的显示名称,省去了在内部名称和外部名称之间维护的工作。为了给角色类型的属性赋一个默认值,你必须首先从数据库里装载角色。
Oslash; Attribute:属性类型的值是对单据类型的引用。
Oslash; Event:暂时不知?(没有查到相关资料)
§ 根据你选择的属性类型,为属性指定默认值信息;
§ 对单据类型属性来说,可选的默认值是一个你输入或从列表中选择的常量,不过,这个常量可以是一个在运行时替换的字符串;对活动属性来说,默认值可以是一个常量或者一个单据类型属性;
§ 选择"应用"保存你的修改,选择"确定"保存你的修改并推出属性页面,"取消"取消你的修改并推出属性页面;
§ 如果你在定义一个单据类型属性,选择"Access"页设置允许修改属性的访问级别。
你创建的任何Attributes类型会出现在浏览树的"Attributes"分支下,所有的函数属性出现在对应的函数下面,你可以从浏览树里双击一个属性浏览或修改属性的定义。
2.2.1定义一个URL属性
§ 在属性默认值里指定一个URL地址,这个URL可以是一个常量或者一个从其它单据属性返回的值;
§ 你也可以在URL里包含字符串参数,这个参数可以是一个字符串文本或一个其它单据类型的格式替换。在参数字符串里采用其它单据类型进行格式替换,需要采用如下的格式:item_attr;
§ 如果URL里包含一个参数字符串,你必须遵守如下的限制:你不能用一个文档类型的单据类型属性对参数进行格式替换;你可以用表单或URL属性对参数进行格式替换,但不能进行嵌套替换;
§ 如果你需要在URL里传递一个日期时间参数,你需要使用TO_CHAR函数把日期时间转换为如下格式:"YYYY-MM-DD+HH24:MM:SS",同样对日起类型需要使用TO_DATE;
§ 结束工作之后选择"确定"按钮保存。
2.2.2定义一个Form属性
§ 在窗体属性页的默认值里指定一个窗体函数的名称以及可选的字符串参数(窗体函数的参数);
§ 默认值必须使用下面的格式输入:function_name:arg1=value1 arg2=value2...argn=valuen,argn的值可以是一个包含在双引号内的字符串,也可以是一个被其他单据类型属性进行格式替换的值,可以使用下面的方式定义格式替换:argn="item_attr"或者argn="value &item_attr";
§ 如果你的窗体属性内包括参数,你必须牢记以下的限制:你不能使用一个文档类型的属性进行标记替换;你可以使用一个窗体类型或URL类型的属性进行标记替换,但是你不能进行嵌套替换;
§ 保存你的工作。
2.2.3定义一个Document属性
§ 在属性页的默认值里输入一个标识文档的字符串;
§ 你可以标识以下两种类型的文档:PL/SQL文档或者存放在文档管理系统里的文档;
§ 一个PL/SQL文档表示数据库里的数据,该文档由一个存储过程产生,需要使用下面的方式来指定一个PL/SQL文档的默认值:plsql:<procedure>/<document_identifier>。在使用时用一个包和过程名称来替换procedure,用直接传送给过程的字符串参数替换document_identifier,这个参数字符串应该能标识这个文档。PL/SQL存储过程必须按照标准的API格式来写;默认值的实例如下所示:plsql:po_wf.show_req/po_req:2034;
§ 如果你想动态的生成一个PL/SQL文档,你可以把文档标识定义成一个标记替换,然后使用其它单据类型属性的值替换,例如:plsql:po_wf.show_req/item_attr1:&item_attr2;
§ 如果想指定一个由外部文档管理系统管理的文档,可以在默认值里输入如下格式的信息:DM:<node_ID>:<document_ID>:[version];
§ 选择"确定"保存你的修改。
2.3流程(Process)
流程就是业务流程的流转规则。流程包括两部分:流程里包含的活动和各个活动在不同条件下的流转关系。在定义流程的时候,因为流程里的活动可能有不同的返回结果,我们需要把不同结果的处理活动和结果联系起来,另外在OracleERP里还定义了一些标准的返回结果(例如超时),你在定义流程的时候,也可以对这些内嵌的返回结果进行处理。在流程实例流转的时候,流程里的活动有自己相应的状态,工作流引擎得到活动的状态改变信息或活动返回结果之后,按照你定义的流程流转规则控制流程的流转。流程必须属于一个单据类型。
流程定义里有一个标志属性"是否可运行标志",如果一个流程是可运行的,则可以在窗体上可以直接调用,否则该流程只能作为一个子流程包含在其它的流程里面,工作流引擎在碰到流程活动的时候,会自动激活子流程,让子流程流转。
2.3.1在你画流程图之前,你必须先定义一个流程活动来表达流程图:
§ 从浏览树里选择一个单据类型,右键Process并从菜单里选择"New Process",在活动属性页上定义流程活动信息;
§ 流程活动必须有一个全部大写的内部名称和一个显示名称,你也可以输入一段摘要信息来描述这个活动;
§ 指定流程活动的结果类型,流程活动可以不返回结果;
§ 为流程活动选择一个图标来标识它;
§ 选中Runnable,这样你就可以把流程当作一个独立的流程来运行,如果不选的话,定义的流程只能作为一个子流程在别的流程下运行;
§ 保存你的工作;
§ 选择"Details"页定义流程活动的详细信息;
§ 选择"Access"页指定允许修改流程的访问级别;
§ 保存你所作的修改;
§ 在浏览树的"Processes"分支下回出现你新定义的流程,在浏览树里用鼠标双击流程就可以查看和修改流程活动的信息。
2.3.2画流程图
在Oracle Workflow Builder里,流程图采用一种图形化的方式来显示流程里的活动以及活动之间的转移。在流程图里用一个图标来表示活动,用一个箭头来表示活动之间的转移。你可以从浏览树里把一个活动拖拽到流程图里,也可以直接在流程图里创建一个活动,在流程图里双击一个活动节点可以查看或修改节点的信息。通过在两个活动之间画一个箭头来表示活动之间的转移。
转移在流程图里使用一个箭头表示的,它表示一个活动的结束以及另外一个活动的激活。如果一个活动的结果类型是None,则转移表现为一个指向另外一个活动的箭头,表示只要初始的活动一结束,就流向下一个活动。如果一个活动有返回结果类型,则你必须把转移和活动的一个结果联系起来,你也可以为定义了返回结果的活动定义下面的转移类型:Default,Any,Timeout。在没有任何转移匹配完成结果时,工作流引擎执行默认的转移;如果定义了一个人以的转移,则不论活动的返回结果是什么,工作流引擎就执行这个转移,这样可以保证你除了在工作流里执行和放返回结果对应的活动外,还可以执行一个通用的活动。如果一个活动在完成之前已经超时,则工作流引擎执行超时转移。一个活动的返回结果可以有多个转移来进行并行处理。
(1)增加一个节点
§ 在开始画流程图之前,你必须先显示流程活动的流程窗口,你可以通过以下的方式显示流程窗口:在浏览树里双击一个定义好的流程活动;选择一个定义好的流程活动并按下Ctrl+E;选择一个流程活动,并从编辑菜单里选择流程详细信息;使用快速启动向导创建一个新的流程。
§ 可以通过下面的方式创建一个节点:从浏览树里拖拽一个活动;选择New Function、New Notification、New Process工具栏按钮创建一个节点;在弹出菜单里选择Create Activity创建一个新的节点;
§ 按住鼠标右健并把鼠标从一个活动拖到另一个活动就在两个活动之间创建了一个转移;
§ 如果来源活动定义了放回结果类型,则需要把转移和一个结果联系起来;
§ 你可以在流程图里进行Copy和Paste;
(2)定义一个节点
§ 打开流程活动的流程窗口;
§ 使用工具栏按钮创建一个新的活动;
§ 在单据类型域里,选择一个单据类型;
§ 采用下面的方式定义节点的剩余信息:选择一个已定义活动的内部名称或显示名称,Workflow Builder会自动显示出原来定义的信息;另外你也可以使用新建按钮来创建一个新的活动,并根据活动的类型分别定义它们的信息,注意,在流程图里说做的修改都会反映到浏览树里的活动信息里;
§ 选择"Node"页来定义节点的特定信息,为节点指定一个标签,因为一个活动在一个流程里可能有多个实例,可以通过标签来区分同一个活动的不同实例;
§ 指定节点是否为一个开始或结束节点;
§ 如果流程有一个接过类型,你必须为结束节点指定一个最终的结果;
§ 你可以提供一段注释信息供自己察看;
§ 对一个通知或一个子流程活动,你可以规定该活动节点是否必须在规定的时间之内完成;
§ 对一个通知节点来说,你可以覆盖同志消息的优先级,另外需要给通知活动指定一个执行者,这是一个角色;
§ 保存你的修改;
§ 如果节点是一个函数或通知活动,并且这个活动有参数,你可以选择节点属性页给这些活动属性赋值;
如果这个节点是一个子流程,则一个子流程的标识会出现在子流程节点图标上。
(3)定义活动属性值
活动属性值主要是别PL/SQL存储过程服务的,这个存储过程被函数或通知活动调用。
§ 显示属性值页;
§ 选择一个属性;
§ 在值区域里,输入这个属性的值,这个值可以是一个常量也可以是一个对单据类型属性的引用。注意,这个值的类型必须和活动属性的类型以及存储过程参数的类型保持一致;
§ 保存你的修改。
2.4消息(Message)
消息主要是为通知服务的,可以把消息当作通知的内容和类型。消息也属于一个单据类型,通知只能和同一个单据类型里的消息相关联。
每个消息可以有一个或多个属性和自己相联系,消息的属性既可以是独立的属性,也可以是对单据类型属性的一个引用。在消息体内可以对消息的属性进行引用,这样在流程的实例里,如果已经对消息的属性赋值,则在引用消息属性的地方会自动的用消息属性的值替换。我们就可以在消息体内显示必要的信息。
你可以对一个消息定义自动流转规则,这样在工作流的实例里,当流转到和该消息对应的通知活动的时候,工作流引擎就会根据用户定义的自动流转规则自动向下流转,用户就不需要参与了。
消息时工作流程里通知活动发送给角色的内容,消息可以提示户对通知进行回复或者让用户采取一个动作决定下一个活动,在工作流里一个消息的接收者被称作"Performer";每个消息都和一个单据类型联系在一起,这样可以允许消息引用单据类型属性,在运行时进行标记替换。
当你定义消息的时候,可以指定接收者回复的值,然后工作流引擎根据用户的返回值决定流程的下一个活动,你也可以在消息的主题和消息体内包括消息属性使消息成为一个上下文环境敏感的消息。你可以从浏览树里直接把一个消息直接拖拽到通知分支下面创建一个发送该消息的通知,也可以把消息拖拽到一个已存在的通知活动上更新通知发送的消息。
当你为通知活动创建一个通知的时候,你需要注意通知是否规定了一个结果类型,如果有结果类型的话,你创建的通知需要提示通知接收者一些特定的响应用来解释通知的结果类型,工作流引擎使用这些结果决定流程需要流到哪些活动。要想创建一个对特殊响应进行提示的消息,你需要完成消息页面的"Result"页,你输入的信息创建了一个特殊的"Response"消息属性,它有一个内部名称"RESULT",结果消息属性的数据类型是一个查找类型,并且必须和通知的结果查找类型一致,这可以保证通知活动的执行者从一个通知活动期望的结果值列表里选择一个值。
一旦你创建了一个消息,你可以为消息定义需要的所有属性,在浏览树里消息的属性存放在消息的下面。消息属性的来源(Send or Respond)决定了消息属性的使用方式,你可以使用"Send"类型的消息属性进行标记替换或者和消息一块发送。每个消息类型都有一个数据类型,"Send"类型的消息属性可以是一个常量或者是一个对单据类型属性的引用。"Response"类型的消息属性组成了消息的响应部分,响应消息属性为接收者的响应提供了指令。如果你定义了一个响应消息属性,你必须给它指定数据类型,你也可以给它指定一个默认值,默认值可以是一个常量,也可以是对相同数据类型单据类型属性的引用。
创建一个消息:
§ 从浏览树里选择一个单据类型子树中的Messages,然后右键并从菜单里选择"New Message",消息属性页面就会出现;
§ 为消息提供一个大写的内部名称,一个显示名称,你可以为消息输入一段描述性的信息;
§ 为消息选择一个默认的优先级,消息的优先级只是提醒接收者消息的紧急程度,对消息的传送和处理没有影响;
§ 保存你的修改;
§ 选择"Body"页定义消息体;
§ 主题会有一个和消息显示名称一样的默认值,你可以保存这个默认的主题或者输入一个新的主题,消息的主题可以包含消息的属性,在运行时通过标记替换来赋值。
§ 在消息体输入框内输入一段纯文本信息,Oracle工作流使用你输入的文本生成通知消息的一个文本版本,这段文本消息可以从一个E-mail阅读器里查看;
§ 你也可以输入一个HTML格式的消息体,同时也可以从一个HTML文件里导入,注意,当你输入或导入HTML文件的时候,不要包括<Body>...</Body>HTML标记,如果你包括了这些标记,Oracle工作流只是简单的抽取这些标记之间的内容,<Body>标记之前的内容。Oracle工作流使用你在HTML消息体内的内容为通知消息生成一个HTML格式的版本,你可以从通知的详细页面来查看HTML格式的通知消息或者从一个E-mail阅读器来查看HTML格式的消息或者HTML格式的附件,如果你的HTML消息体的内容是空的,工作流会使用你在文本消息体内输入的内容生成通知消息。另外一个需要注意的问题是,Oracle工作流不能识别消息体内的图标和图像文件。
§ 你可以再消息体内包括消息的属性,这样在运行的时候,可以使用消息属性的值进行标记替换。
§ 保存你所作的修改。
§ 选择"Role"页面规定访问该消息的角色信息;
§ 选择"Access"页面规定允许修改消息的访问级别;
§ 如果你想让通知消息提示"Performer"返回一个结果值并且希望Oracle工作流引擎把消息的结果值解释为通知活动的返回结果值,你需要选择"Result"页完成要求的信息,Oracle工作流使用你在该页面上输入的信息生成一个叫做"Result"的特殊"Response"类型的消息属性;在该页面上输入一个显示名称和一段描述性的信息,并在列表里选择一个查找类型,你选择的查找类型应该和通知或定的结果查找类型一致,在默认值区域里选择一个查找类型编码;
§ 保存你所作的修改;
§ 你刚定义的消息会出现在浏览树的Messages分支下面,你可以双击这个消息来查看或修改消息的信息;
§ 你必须定义所有包含在消息主题和消息体内的消息属性;
§ 如果你想创建一个引用单据类型属性的消息,从浏览树里选择一个单据类型属性并把它用鼠标拖拽到消息上,你必须在消息属性窗口里编辑该属性,确保消息属性有正确的来源。默认值区域会自动的设置成单据属性并引用原始的单据属性;
§ 你也可以创建一个不引用已存在单据类型属性的消息属性;
2.5函数(Function)
函数就是一个可以自动执行的活动,该活动可以完成一定的功能,函数活动通常和一个PL/SQL存储过程或者一个Oracle内部的过程联系在一起,在定义函数活动的时候必须定义该函数对应的过程名称。
函数类型的活动有自己的参数定义,函数活动的参数主要是为对应的过程服务的。在运行的时候通过给函数活动的参数赋值,经过相应的处理之后,工作流引擎就会把函数活动的参数值传给相应的过程,这样就能保证函数的正确执行。如果对应的过程没有参数的话,可以不定义参数。
创建一个Function:
§ 从浏览树选择一个单据类型,右键Function并从菜单里选择"New Function",在活动属性页里定义函数活动的信息;
§ 为函数活动提供一个大写的内部名称以及显示名称,你也可以一段描述活动的摘要信息;
§ 输入函数活动执行的函数名称,并选择函数的类型;
§ 选择函数活动的结果类型,
§ 输入函数活动的时间花费;
§ 选择一个标识函数活动的图标;
§ 保存你的修改
§ 选择"Details"页面显示或修改活动的详细信息;
§ 选择"Roles"页面制定访问活动的角色信息(这个功能可能在将来的版本里支持);
§ 选择"Access"页面规定允许修改活动的访问信息;
§ 新定义的函数活动会出现在浏览树里的"Functions"分支下面,你可以用鼠标双击这个函数活动来查看和修改函数活动的信息。
§ 如果你的函数需要输入参数,你可以为函数活动定属性。
2.6通知(Notification)
通知活动对一个人或一个角色发送一个通知,通知必须和一个消息联系在一起。通知活动有自己的结果类型,返回的结果一般是根据用户的交互产生的,除非你在消息里定义了自动流转规则。
创建一个Notification:
§ 从浏览树里选择一个单据类型,右键Notification并从菜单里选择"New Notification",在出现的活动窗口里定义通知活动;
§ 通知活动必须有一个大写的内部名称,另外还必须有一个显示名称,你也可以为通知定义一段描述信息对通知活动进行描述。
§ 指定通知活动的结果类型(一个定义好的查找类型),结果类型是一个可能返回结果的列表,你的工作流会根据活动的返回结果进行分支处理。你的通知活动也可以没有返回结果。
§ 选择你的通知想发送的消息名称;
§ 如果你想把通知指定给一个包含多个人的角色,并且给角色里的每一个人发送一份通知的拷贝,选择"Expand Role",如果你把选择该选项,就给整个角色发送一个通知的拷贝。
§ 你可以在"Function"域里指定一个PL/SQL存储过程,这就是一个"Post-Function"函数,工作流引擎会根据用户的响应在不同的模式下执行这个函数,可能的模式类型有:Respond、Forward、Time Out和Transfer;
§ 为活动选择一个图标按钮,便于在流程图里标识活动;
§ 保存你的修改;
§ 选择"Details"页面显示或修改活动的详细信息;
§ 选择"Roles"页面制定访问活动的角色信息(这个功能可能在将来的版本里支持);
§ 选择"Access"页面规定允许修改活动的访问信息;
§ 保存你的修改;
§ 新定义的通知活动会出现在浏览树里的"Notifications"分支下面,你可以用鼠标双击这个通知活动来查看和修改通知活动的信息。
2.7查找类型(Lookup Type)
查找类型是你定义的一些枚举数据,单据类型里的查找类型主要用来表达活动结果返回值类型,另外也可以在定义单据类型属性、活动属性、消息和消息属性时引用查找类型,一个查找类型有自己相应的值列表,在引用查找类型的时候,你只需要定义查找类型就可以了,和查找类型相关的值会自动过去的。
查找类型是一个静态值列表,这些列表可以被活动、单据类型、消息或属性引用,例如一个活动可以引用一个查找类型作为它可能的返回值。
当你定义一个查找类型的时候,你必须把它和一个单据类型联系起来;然而,但你在创建一个活动或属性的时候,你可以引用但前数据存储里的任何查找类型,不论这个查找类型和哪个单据类型联系在一起。
创建一个Lookup Type:
§ 从浏览树里选择一个单据类型,然后右键从菜单中选择"New",一个查找类型属性页就会出现;
§ 查找类型有一个大写的内部名称、一个显示名称,你也可以输入一段描述性的摘要信息,在引用查找类型的时候,需要指定它的内部名称;
§ 选择"Access"页定义允许修改查找类型的访问级别;
§ 保存你的修改;
§ 新定义的查找类型会出现在浏览树的"Lookup Types"分支下面,你可以在今后双击该查找类型来查看和修改它的属性;
§ 为查找类型定义编码:
Oslash; 选择一个查找类型,右键并从菜单里选择"New Lookup Code",一个查找编码属性页会出现;
Oslash; 输入大写的内部名称、显示名称,你也可以输入一段摘要信息;
Oslash; 保存你的修改;
Oslash; 新定义的查找类型编码会在相对应的查找类型下面出现。
3工作流引擎
3.1概览
Oslash; 工作流引擎是运行在数据库服务器端的PL/SQL程序
Oslash; 工作流引擎任何时候都处于激活状态,只要它的PL/SQL过程或函数被调用
Oslash; 工作流引擎管理着每个工作流流程实例的活动
Oslash; 当一个活动完成后,工作流引擎决定了下一个将被运行的活动
Oslash; 工作流引擎自动执行工作流的功能活动
Oslash; 工作流引擎会调用通知系统给用户发送通知(通知系统也是运行在服务器端的PL/SQL程序,它可以和ORACLE的WEB代理进行接口或者通过 ‘通知邮递员'程序来给最终用户发通知)。
Oslash; 将对系统资源消耗比较大的活动推迟,由后台引擎来运行
Oslash; 侦测错误并执行处理错误的流程
3.2启动工作流流程
要在应用中调用工作流的流程(例如:在采购订单的FORM中调用‘审批采购订单'的工作流流程),必须在应用的程序中使用启动工作流流程的工作流引擎API:调用WF_ENGINE.CreateProcess接着调用WF_ENGINE.StartProcess或单独调用WF_ENGINE.LaunchProcess。在这些API中都必须指定ITEM_TYPE和ITEM_KEY的值作为参数。3.3活动的状态
当工作流引擎执行一个活动时,它将修改活动的状态为以下的值之一:
Oslash; Active:活动当前正在被执行,例如主流程活动一直处于活动状态直到盖流程中的所有活动都已完成
Oslash; Complete:活动已经执行完毕,并且执行成功
Oslash; Waiting:活动正等待相关活动的完成,一个活动尽管已经运行结束,但它必须等待相关的活动也运行结束才能被标记为Complete
Oslash; Notified:活动正在等待通知的回应
Oslash; Deferred:活动被推迟到后台运行
Oslash; Error:活动在执行过程中出错
Oslash; Suspend:活动被暂挂,在暂挂被取消后将继续运行
3.4通知工作流引擎
在一个活动结束后必须通知工作流引擎。流程、通知和功能活动结束后会自动调用WF_ENGINE.CompleteActivity来通知工作流引擎它们已经运行结束。如果一个功能活动调用的是一段外部程序,那么外部程序需要调用WF_ENGINE.CompleteActivity,在活动结束的时候。3.5工作流引擎的数据库包
工作流引擎是由一些PL/SQL的数据库包组成的,大致包括:
ü WF_ENGINE:用来启动和运行工作流流程,获取、写入或创建项目类型Attribute、功能活动Attribute的值,管理活动的状态
ü WF_CORE:触发和捕获错误
ü WF_PURGE:清除无用的运行的中间数据
ü WF_DIRECTORY:用来存入和获取目录服务的信息
ü WF_MONITOR:生成工作流监控页面的URL
ü WF_NOTIFICATION:管理工作流的通知
ü WF_PREFERENCE:检索用户的偏好信息
3.6工作流相关的主要表结构Oslash; wf_item_types_vl
保存工作流的定义,即类(大的种类,如:OEOH、OEOL)
Oslash; wf_activities
保存当前系统中的工作流的名称(一个版本一条信息)
Oslash; wf_items
保存实际的工作流,或者说工作流的对象实例
例如:每当生成一条SO时,系统往里面插入一条记录(一行一条记录)
Oslash; wf_process_activities
工作流每个NODE的属性信息
u process_item_type : item_type
u PROCESS_NAME : 工作流的名称
u PROCESS_VERSION :工作流的版本信息
u ACTIVITY_ITEM_TYPE : 节点的ITEM类型
u ACTIVITY_NAME : 节点 ITEM 的名称
u INSTANCE_ID : 节点 ID
u INSTANCE_LABEL : 节点显示的 LABEL 标记
Oslash; WF_ACTIVITY_ATTRIBUTES
保存工作流实例的attribute最新值
Oslash; wf_item_activity_statuses
保存工作流实例的各个activity的状态,比如完成否,返回值
Oslash; wf_notifications
保存工作流实例的notifications消息,基本是按顺序的,可以看发给谁了
Oslash; wf_roles
角色视图,工作流引用角色的依据,有mail地址等信息
Oslash; wf_user_roles
用户和角色关系视图,工作流可以根据它进行"群发"
总结:通过这几天对Oracle之Workflow的学习,主要是按照《深入浅出Oracle之Workflow实例详解》上面的实例操作,对工作流有了进一步的理解,本文档是我搜集网上的资料整理出来的,其中很多图例是对操作界面的讲解,不是实例,很多信息都是深入浅出里面没有的,希望对浏览的人有帮助
PLSQL_Oracle面试整理(汇总)
2014-08-16 Created By BaoXinjian
一、SQL Tunning类
1. 列举集中表的连接方式
Answer:等连接(内连接)、非等连接、自连接、外连接(左、右、全)
Or hash join/merge join/nest loop(cluster join)/index join
连接类型
定义
例子
内连接
只连接匹配的行
select A.c1,B.c2 from A join B on A.c3 =
B.c3;
左外连接
包含左边表的全部行(不管右边的表中是否存在与它们匹配的行)以及右边表中全部匹配的行
select A.c1,B.c2 from A left join B on A.c3 =
B.c3;
右外连接
包含右边表的全部行(不管左边的表中是否存在与它们匹配的行)以及左边表中全部匹配的行
select A.c1,B.c2 from A right join B on A.c3 =
B.c3;
全外连接
包含左、右两个表的全部行,不管在另一边的表中是否存在与它们匹配的行
select A.c1,B.c2 from A full join B on A.c3 =
B.c3;
(theta)连接
使用等值以外的条件来匹配左、右两个表中的行
select A.c1,B.c2 from A join B on A.c3 !=
B.c3;
交叉连接
生成笛卡尔积——它不使用任何匹配或者选取条件,而是直接将一个数据源中的每个行与另一个数据源的每个行一一匹配
select A.c1,B.c2 from
A,B;
2. 不借助第三方工具,怎么样查看sql的执行计划
Solution1. 使用Explain Plan,查询PLAN_TABLE;
EXPLAIN PLAN
SET STATEMENT_ID='QUERY1'
FOR
SELECT *
FROM a
WHERE aa=1;
SELECT operation, options, object_name, object_type, ID, parent_id
FROM plan_table
3 WHERE STATEMENT_ID = 'QUERY1'
ORDER BY ID;
Solution2. SQLPLUS中的SET TRACE 即可看到Execution Plan Statistics
SET AUTOTRACE ON;
3. 如何使用CBO,CBO与RULE的区别
(1). 结构
IF 初始化参数 OPTIMIZER_MODE = CHOOSE THEN --(8I DEFAULT)
IF 做过表分析
THEN 优化器 Optimizer=CBO(COST); /*高效*/
ELSE
优化器 Optimizer=RBO(RULE); /*高效*/
END IF;
END IF;
(2). 区别:
RBO根据规则选择最佳执行路径来运行查询。
CBO根据表统计找到最低成本的访问数据的方法确定执行计划。
(3). 使用CBO需要注意:
需要经常对表进行ANALYZE命令进行分析统计;
需要稳定执行计划;
需要使用提示(Hint);
(4). 使用RULE需要注意:
选择最有效率的表名顺序
优化SQL的写法;
(5). 总结
在optimizer_mode=choose时,如果表有统计信息(分区表外),优化器将选择CBO,否则选RBO。
RBO遵循简单的分级方法学,使用15种级别要点,当接收到查询,优化器将评估使用到的要点数目,然后选择最佳级别(最少的数量)的执行路径来运行查询。
CBO尝试找到最低成本的访问数据的方法,为了最大的吞吐量或最快的初始响应时间,计算使用不同的执行计划的成本,并选择成本最低的一个,关于表的数据内容的统计被用于确定执行计划。
4. 如何定位重要(消耗资源多)的SQL
Step1. 使用CPU多的用户session定位
SELECT a.SID, spid, status, SUBSTR (a.program, 1, 40) prog, a.terminal,a.SQL_TEXT, osuser, VALUE / 60 / 100 VALUE
FROM v$session a, v$process b, v$sesstat c
WHERE c.statistic# = 12 AND c.SID = a.SID AND a.paddr = b.addr
ORDER BY VALUE DESC;
Step2. 查找
select sql_text from v$sql
where disk_reads &gt; 1000 or (executions &gt; 0 and buffer_gets/executions &gt; 30000);
5. 如何跟踪某个session的SQL
Step1. 利用TRACE 跟踪
ALTER SESSION SET SQLTRACE ON;
COLUMN SQL format a200;
SELECT machine, sql_text SQL
FROM v$sqltext a, v$session b
WHERE address = sql_address
AND machine = '&A'
ORDER BY hash_value, piece;
exec dbms_system.set_sql_trace_in_session(sid,serial#,&amp;sql_trace);
select sid,serial# from v$session where sid = (select sid from v$mystat where rownum = 1);
exec dbms_system.set_ev(&amp;sid,&amp;serial#,&amp;event_10046,&amp;level_12,'');
6. SQL调整最关注的是什么
检查系统的I/O问题
sar-d能检查整个系统的iostat(IO
statistics)
查看该SQL的response time(db block gets/consistent
gets/physical reads/sorts (disk))
7. 说说你对索引的认识(索引的结构、对dml影响、对查询影响、为什么提高查询性能)
索引有B-TREE、BIT、CLUSTER等类型。ORACLE使用了一个复杂的自平衡B-tree结构;通常来说,在表上建立恰当的索引,查询时会改进查询性能。但在进行插入、删除、修改时,同时会进行索引的修改,在性能上有一定的影响。有索引且查询条件能使用索引时,数据库会先度取索引,根据索引内容和查询条件,查询出ROWID,再根据ROWID取出需要的数据。由于索引内容通常比全表内容要少很多,因此通过先读索引,能减少I/O,提高查询性能。
b-tree index/bitmap index/function index/patitional
index(local/global)索引通常能提高select/update/delete的性能,会降低insert的速度,
8. 使用索引查询一定能提高查询的性能吗?为什么
通常,通过索引查询数据比全表扫描要快.但是我们也必须注意到它的代价.
索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时,索引本身也会被修改. 这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5
次的磁盘I/O. 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢.使用索引查询不一定能提高查询性能,索引范围查询(INDEX RANGE SCAN)适用于两种情况:
基于一个范围的检索,一般查询返回结果集小于表中记录数的30%宜采用;
基于非唯一性索引的检索
索引就是为了提高查询性能而存在的,如果在查询中索引没有提高性能,只能说是用错了索引,或者讲是场合不同
9. 绑定变量时什么,绑定变量的优缺点
绑定变量是指在SQL语句中使用变量,改变变量的值来改变SQL语句的执行结果。
优点:使用绑定变量,可以减少SQL语句的解析,能减少数据库引擎消耗在SQL语句解析上的资源。提高了编程效率和可靠性。减少访问数据库的次数,
就能实际上减少ORACLE的工作量。
缺点:经常需要使用动态SQL的写法,由于参数的不同,可能SQL的执行效率不同;
绑定变量是相对文本变量来讲的,所谓文本变量是指在SQL直接书写查询条件,
这样的SQL在不同条件下需要反复解析,绑定变量是指使用变量来代替直接书写条件,查询bind
value在运行时传递,然后绑定执行。
优点是减少硬解析,降低CPU的争用,节省shared_pool
缺点是不能使用histogram,sql优化比较困难
10. 如何稳定(固定)执行计划
可以在SQL语句中指定执行计划。使用HINTS;
query_rewrite_enabled = true
star_transformation_enabled = true
optimizer_features_enable = 9.2.0
创建并使用stored outline
11. 和排序相关的内存在8i和9i分别怎样调整,临时表空间的作用是什么
SORT_AREA_SIZE 在进行排序操作时,如果排序的内容太多,内存里不能全部放下,则需要进行外部排序,
此时需要利用临时表空间来存放排序的中间结果。
8i中sort_area_size/sort_area_retained_size决定了排序所需要的内存,
如果排序操作不能在sort_area_size中完成,就会用到temp表空间
9i中如果workarea_size_policy=auto时,
排序在pga内进行,通常pga_aggregate_target的1/20可以用来进行disk sort;
如果workarea_size_policy=manual时,排序需要的内存由sort_area_size决定, 在执行order by/group by/distinct/union/create index/index
rebuild/minus等操作时,如果在pga或sort_area_size中不能完成,排序将在临时表空间进行(disk sort),临时表空间主要作用就是完成系统中的disk
sort.
12. 存在表T(a,b,c,d),要根据字段c排序后取第21—30条记录显示,请给出sql
Solution1.
SELECT *
FROM (SELECT ROWNUM AS row_num, tmp_tab.*
FROM (SELECT a, b, c, d
FROM T
ORDER BY c) tmp_tab
WHERE ROWNUM <= 30)
WHERE row_num >= 20
ORDER BY row_num;
Solution2.
create table t(a number(,b number(,c number(,d number();
/
begin
for i in 1 .. 300 loop
insert into t values(mod(i,2),i/2,dbms_random.value(1,300),i/4);
end loop;
end;
/
Solution3.
select * from (select c.*,rownum as rn from (select * from t order by c desc) c) where rn between 21 and 30;
/
select * from (select * from test order by c desc) x where rownum &lt; 30
minus
select * from (select * from test order by c desc) y where rownum &lt; 20 order by 3 desc
相比之 minus性能较差
二:数据库基本概念类
1. Pctused and pctfree 表示什么含义有什么作用
pctused与pctfree控制数据块是否出现在freelist中, pctfree控制数据块中保留用于update的空间,当数据块中的free space小于pctfree设置的空间时,该数据块从freelist中去掉,当块由于dml操作free space大于pct_used设置的空间时,该数据库块将被添加在freelist链表中。
2. 简单描述tablespace / segment / extent / block之间的关系
(1). tablespace: 一个数据库划分为一个或多个逻辑单位,该逻辑单位成为表空间;每一个表空间可能包含一个或多个 Segment;
(2). Segments: Segment指在tablespace中为特定逻辑存储结构分配的空间。每一个段是由一个或多个extent组成。包括数据段、索引段、回滚段和临时段。
(3). Extents: 一个 extent 由一系列连续的 Oracle blocks组成.ORACLE为通过extent 来给segment分配空间。
(4). Data Blocks:Oracle 数据库最小的I/O存储单位,一个data block对应一个或多个分配给data file的操作系统块。
(5). table创建时,默认创建了一个data segment,每个data segment含有min extents指定的extents数,每个extent据据表空间的存储参数分配一定数量的blocks
3. 描述tablespace和datafile之间的关系
一个表空间可包含一个或多个数据文件。表空间利用增加或扩展数据文件扩大表空间,表空间的大小为组成该表空间的数据文件大小的和。一个datafile只能属于一个表空间;
一个tablespace可以有一个或多个datafile,每个datafile只能在一个tablespace内, table中的数据,通过hash算法分布在tablespace中的各个datafile中,tablespace是逻辑上的概念,datafile则在物理上储存了数据库的种种对象。
4. 本地管理表空间和字典管理表空间的特点,ASSM有什么特点
本地管理表空间:(9i默认)空闲块列表存储在表空间的数据文件头。
特点:减少数据字典表的竞争,当分配和收缩空间时会产生回滚,不需要合并。
字典管理表空间:(8i默认)空闲块列表存储在数据库中的字典表里.
特点:片由数据字典管理,可能造成字典表的争用。存储在表空间的每一个段都会有不同的存储字句,需要合并相邻的块;
本地管理表空间(Locally Managed Tablespace简称LMT)
8i以后出现的一种新的表空间的管理模式,通过位图来管理表空间的空间使用。字典管理表空间(Dictionary-Managed Tablespace简称DMT)
8i以前包括以后都还可以使用的一种表空间管理模式,通过数据字典管理表空间的空间使用。动段空间管理(ASSM),它首次出现在Oracle920里有了ASSM,链接列表freelist被位图所取代,它是一个二进制的数组,
能够迅速有效地管理存储扩展和剩余区块(free block),因此能够改善分段存储本质,ASSM表空间上创建的段还有另外一个称呼叫Bitmap Managed Segments(BMB 段)。
5. 回滚段的作用是什么
回滚段用于保存数据修改前的映象,这些信息用于生成读一致性数据库信息、在数据库恢复和Rollback时使用。一个事务只能使用一个回滚段。
事务回滚:当事务修改表中数据的时候,该数据修改前的值(即前影像)会存放在回滚段中,当用户回滚事务(ROLLBACK)时,ORACLE将会利用回滚段中的数据前影像来将修改的数据恢复到原来的值。
事务恢复:当事务正在处理的时候,例程失败,回滚段的信息保存在undo表空间中,ORACLE将在下次打开数据库时利用回滚来恢复未提交的数据。
读一致性:当一个会话正在修改数据时,其他的会话将看不到该会话未提交的修改。 当一个语句正在执行时,该语句将看不到从该语句开始执行后的未提交的修改(语句级读一致性)
当ORACLE执行SELECT语句时,ORACLE依照当前的系统改变号(SYSTEM CHANGE NUMBER-SCN) 来保证任何前于当前SCN的未提交的改变不被该语句处理。可以想象:当一个长时间的查询正在执行时, 若其他会话改变了该查询要查询的某个数据块,ORACLE将利用回滚段的数据前影像来构造一个读一致性视图
6. 日志的作用是什么
日志文件(Log File)记录所有对数据库数据的修改,主要是保护数据库以防止故障,以及恢复数据时使用。其特点如下:
a)每一个数据库至少包含两个日志文件组。每个日志文件组至少包含两个日志文件成员。
b)日志文件组以循环方式进行写操作。
c)每一个日志文件成员对应一个物理文件。
记录数据库事务,最大限度地保证数据的一致性与安全性
重做日志文件:含对数据库所做的更改记录,这样万一出现故障可以启用数据恢复,一个数据库至少需要两个重做日志文件
归档日志文件:是重做日志文件的脱机副本,这些副本可能对于从介质失败中进行恢复很必要。
7. SGA主要有那些部分,主要作用是什么
系统全局区(SGA):是ORACLE为实例分配的一组共享缓冲存储区,用于存放数据库数据和控制信息,以实现对数据库数据的管理和操作。
SGA主要包括:
a)共享池(shared pool) :用来存储最近执行的SQL语句和最近使用的数据字典的数据。
b)数据缓冲区 (database buffer cache):用来存储最近从数据文件中读写过的数据。
c)重作日志缓冲区(redo log buffer):用来记录服务或后台进程对数据库的操作。
另外在SGA中还有两个可选的内存结构:
d)Java pool: 用来存储Java代码。
e)Large pool: 用来存储不与SQL直接相关的大型内存结构。备份、恢复使用。
GA:db_cache/shared_pool/large_pool/java_pool
db_cache: 数据库缓存(Block Buffer)对于Oracle数据库的运转和性能起着非常关键的作用,它占据Oracle数据库SGA(系统共享内存区)的主要部分。Oracle数据库通过使用LRU算法,将最近访问的数据块存放到缓存中,从而优化对磁盘数据的访问.
shared_pool: 共享池的大小对于Oracle 性能来说都是很重要的。共享池中保存数据字典高速缓冲和完全解析或编译的的PL/SQL 块和SQL 语句及控制结构
large_pool: 使用MTS配置时,因为要在SGA中分配UGA来保持用户的会话,就是用Large_pool来保持这个会话内存使用RMAN做备份的时候,要使用Large_pool这个内存结构来做磁盘I/O缓存器
java_pool: 为java procedure预备的内存区域,如果没有使用java proc,java_pool不是必须的
8. Oracle系统进程主要有哪些,作用是什么
数据写进程(DBWR):负责将更改的数据从数据库缓冲区高速缓存写入数据文件
日志写进程(LGWR):将重做日志缓冲区中的更改写入在线重做日志文件
系统监控 (SMON): 检查数据库的一致性如有必要还会在数据库打开时启动数据库的恢复
进程监控 (PMON): 负责在一个Oracle 进程失败时清理资源
检查点进程(CKPT):负责在每当缓冲区高速缓存中的更改永久地记录在数据库中时,更新控制文件和数据文件中的数据库状态信息。
归档进程 (ARCH):在每次日志切换时把已满的日志组进行备份或归档
恢复进程 (RECO): 保证分布式事务的一致性,在分布式事务中,要么同时commit,要么同时rollback;
作业调度器(CJQ ): 负责将调度与执行系统中已定义好的job,完成一些预定义的工作.
三:备份恢复类
1. 备份如何分类
(1). 分类
逻辑备份:exp/imp 指定表的逻辑备份
物理备份:
热备份:alter tablespace begin/end backup;
冷备份:脱机备份(database shutdown)
RMAN备份
full backup/incremental backup(累积/差异)
(2). 物理备份
物理备份是最主要的备份方式。用于保证数据库在最小的数据库丢失或没有数据丢失的情况下得到恢复。
(3).冷物理
冷物理备份提供了最简单和最直接的方法保护数据库因物理损坏丢失。建议在以下几种情况中使用。
对一个已经存在大最数据量的数据库,在晚间数据库可以关闭,此时应用冷物理备份。
对需对数据库服务器进行升级,(如更换硬盘),此时需要备份数据库信息,并在新的硬盘中恢复这些数据信息,建议采用冷物理备份。
(4).热物理
主要是指备份过程在数据库打开并且用户可以使用的情况下进行。需要执行热物理备份的情况有:
由于数据库性质要求不间断工作,因而此时只能采用热物理备份。
由于备份的要求的时间过长,而数据库只能短时间关闭时。
(5).逻辑备份 (EXP/IMP)
逻辑备份用于实现数据库对象的恢复。但不是基于时间点可完全恢复的备份策略。只能作为联机备份和脱机备份的一种补充。
(6).完全逻辑备份
完全逻辑备份是将整个数据库导出到一个数据库的格式文件中,该文件可以在不同的数据库版本、操作系统和硬件平台之间进行移植。
(7).指定表的逻辑备份
通过备份工具,可以将指定的数据库表备份出来,这可以避免完全逻辑备份所带来的时间和财力上的浪费。
2. 归档是什么含义
关于归档日志:Oracle要将填满的在线日志文件组归档时,则要建立归档日志(archived redo log)。其对数据库备份和恢复有下列用处:
数据库后备以及在线和归档日志文件,在操作系统和磁盘故障中可保证全部提交的事物可被恢复。
在数据库打开和正常系统使用下,如果归档日志是永久保存,在线后备可以进行和使用。
数据库可运行在两种不同方式下:NOARCHIVELOG方式或ARCHIVELOG 方式
数据库在NOARCHIVELOG方式下使用时,不能进行在线日志的归档,
数据库在ARCHIVELOG方式下运行,可实施在线日志的归档
归档是归档当前的联机redo日志文件。
SVRMGR> alter system archive log current;
数据库只有运行在ARCHIVELOG模式下,并且能够进行自动归档,才可以进行联机备份。有了联机备份才有可能进行完全恢复。
3. 如果一个表在2004-08-04 10:30:00 被drop,在有完善的归档和备份的情况下,如何恢复
9i 新增的FLASH BACK 应该可以;
Logminer应该可以找出DML。
有完善的归档和备份,先归档当前数据,然后可以先恢复到删除的时间点之前,把DROP 的表导出来,然后再恢复到最后归档时间;
手工拷贝回所有备份的数据文件
Sql〉startup mount;
sql〉alter database recover automatic until time '2004-08-04:10:30:00';
sql〉alter database open resetlogs;
4. rman是什么,有何特点
RMAN(Recovery Manager)是DBA的一个重要工具,用于备份、还原和恢复oracle数据库, RMAN 可以用来备份和恢复数据库文件、归档日志、控制文件、系统参数文件,也可以用来执行完全或不完全的数据库恢复。
RMAN有三种不同的用户接口:COMMAND LINE方式、GUI 方式(集成在OEM 中的备份管理器)、API 方式(用于集成到第三方的备份软件中)。
具有如下特点:
1)功能类似物理备份,但比物理备份强大N倍;
2)可以压缩空块;
3)可以在块水平上实现增量;
4)可以把备份的输出打包成备份集,也可以按固定大小分割备份集;
5)备份与恢复的过程可以自动管理;
6)可以使用脚本(存在Recovery catalog 中)
7)可以做坏块监测
5. standby的特点
备用数据库(standby database):ORACLE推出的一种高可用性(HIGH AVAILABLE)数据库方案,在主节点与备用节点间通过日志同步来保证数据的同步,备用节点作为主节点的备份,可以实现快速切换与灾难性恢复,从920开始,还开始支持物理与逻辑备用服务器。
9i中的三种数据保护模式分别是:
1)、MAXIMIZE PROTECTION :最大数据保护与无数据分歧,LGWR将同时传送到备用节点,在主节点事务确认之前,备用节点也必须完全收到日志数据。如果网络不好,引起LGWR不能传送数据,将引起严重的性能问题,导致主节点DOWN机。
2)、MAXIMIZE AVAILABILITY :无数据丢失模式,允许数据分歧,允许异步传送。
正常情况下运行在最大保护模式,在主节点与备用节点的网络断开或连接不正常时,自动切换到最大性能模式,主节点的操作还是可以继续的。在网络不好的情况下有较大的性能影响。
3)、MAXIMIZE PERFORMANCE:这种模式应当可以说是从8i继承过来的备用服务器模式,异步传送,无数据同步检查,可能丢失数据,但是能获得主节点的最大性能。9i在配置DATA GUARD的时候默认就是MAXIMIZE PERFORMANCE
6. 对于一个要求恢复时间比较短的系统(数据库50G,每天归档5G),你如何设计备份策略
数据库比较大逻辑备份没什么必要,每天归档5G,每周三/周六自动归档10G,每月RMAN归档全库。应该有standby。
rman/每月一号 level 0 每周末/周三 level 1 其它每天level 2
四:系统管理类
1. 对于一个存在系统性能的系统,说出你的诊断处理思路
(1). 做statspack收集系统相关信息 了解系统大致情况/确定是否存在参数设置不合适的地方/查看top 5 event/查看top sql等
(2). 查v$system_event/v$session_event/v$session_wait 从v$system_event开始,确定需要什么资源(db file sequential read)等,深入研究v$session_event,确定等待事件涉及的会话,从v$session_wait确定详细的资源争用情况(p1-p3的值:file_id/block_id/blocks等)
(3). 通过v$sql/v$sqltext/v$sqlarea表确定disk_reads、(buffer_gets/executions)值较大的SQL
2. 列举几种诊断IO、CPU、性能状况的方法
top uptime vmstat iostat statspack sql_trace/tkprof
查v$system_event/v$session_event/v$session_wait
查v$sqlarea(disk_reads或buffer_gets/executions较大的SQL)
或者第三方的监视工具,TOAD就不错。
3. 对statspack有何认识
认识不深。仅限了解。StapSpack是Oracle公司提供的一个收集数据库运行性能指标的软件包。可以做数据库健康检查报告。
StapSpack是Oracle公司提供的一个收集数据库运行性能指标的软件包,该软件包从8i起,在9i、10g都有显著的增强
该软件包的辅助表(存储相关参数与收集的性能指标的表)由最初的25个增长到43个
收集级别参数由原来的3个(0、5、10)增加到5个(0、5、6、7、10)
通过分析收集的性能指标,数据库管理员可以详细地了解数据库目前的运行情况,对数据库实例、等待事件、SQL等进行优化调整
利用statspack收集的snapshot,可以统计制作数据库的各种性能指标的统计趋势图表。
4. 如果系统现在需要在一个很大的表上创建一个索引,你会考虑那些因素,如何做以尽量减小对应用的影响
可以先表分析一下,然后测试创建索引前后对应用的性能影响;
需要考虑的是该索引列不经常更新,不是有很多重复值的情况时, 在大表中使用索引特别有效. 创建的索引可以跟数据表分不同表空间存储。
在系统比较空闲时nologging选项(如果有dataguard则不可以使用nologging)
大的sort_ared_size或pga_aggregate_target较大
5. 对raid10 和raid5有何认识
RAID 10(或称RAID 1+0)与RAID 0+1不同,它是用硬盘驱动器先组成RAID 1阵列,然后在RAID 1阵列之间再组成RAID 0阵列。
RAID 10模式同RAID 0+1模式一样具有良好的数据传输性能,但却比RAID 0+1具有更高的可靠性。RAID 10阵列的实际容量为M×n/2,磁盘利用率为50%。RAID 10也需要至少4个硬盘驱动器构成,因而价格昂贵。
RAID 10的可靠性同RAID 1一样,但由于RAID 10硬盘驱动器之间有数据分割,因而数据传输性能优良。
RAID 5与RAID 3很相似,不同之处在于RAID 5的奇偶校验信息也同数据一样被分割保存到所有的硬盘驱动器,而不是写入一个指定的硬盘驱动器,从而消除了单个奇偶校验硬盘驱动器的瓶颈问题。RAID 5磁盘阵列的性能比RAID 3有所提高,但仍然需要至少3块硬盘驱动器。其实际容量为M×(n-1),磁盘利用率为(n-1)/n 。
五:综合随意类
1. 你最擅长的是oracle哪部分?
pl/sql及sql优化
2. 喜欢oracle吗?喜欢上论坛吗?或者偏好oracle的哪一部分?
喜欢。PL/SQL比较得心应手。
3. 随意说说你觉得oracle最有意思的部分或者最困难的部分
我对数据库的备份/恢复和性能调优经验明显不足,自然觉得有些困难。
基于ORACLE的研究应该是个宽广的领域,所以我觉得还是有意思的。
4. 为何要选择做DBA呢?
我对数据库的备份/恢复和性能调优经验明显不足,主要是缺乏环境和交流。
因此,算不上什么DBA。不过因此我更需要这样的机会。
不过就整个ORACLE 来说,一直从事与它相关的工作,感情还是颇深的。放弃可惜。而且就技术本身而言我觉得自己还是有学习和创新的能力,它的诸如数据仓库,数据挖掘之类的领域也很广。
5. 存储过程和函数的区别
存储过程是用户定义的一系列sql语句的集合,涉及特定表或其它对象的任务,用户可以调用存储过程,而函数通常是数据库已定义的方法,它接收参数并返回某种类型的值并且不涉及特定用户表。
6. 事务是什么?
事务是作为一个逻辑单元执行的一系列操作,一个逻辑工作单元必须有四个属性,称为 ACID(原子性、一致性、隔离性和持久性)属性,只有这样才能成为一个事务:
原子性:事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。
一致性:事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构(如 B 树索引或双向链表)都必须是正确的。
隔离性:由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。这称为可串行性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。
持久性:事务完成之后,它对于系统的影响是永久性的。该修改即使出现系统故障也将一直保持。
7. 游标的作用?如何知道游标已经到了最后?
游标用于定位结果集的行,通过判断全局变量@@FETCH_STATUS可以判断是否到了最后,通常此变量不等于0表示出错或到了最后。
8. 触发器分为事前触发和事后触发,这两种触发有和区别。语句级触发和行级触发有何区别。
事前触发器运行于触发事件发生之前,而事后触发器运行于触发事件发生之后。通常事前触发器可以获取事件之前和新的字段值。
语句级触发器可以在语句执行前或后执行,而行级触发在触发器所影响的每一行触发一次。
Thanks and Regards
ERP技术讨论群: 288307890
技术交流,技术讨论,欢迎加入
Technology Blog Created By Oracle ERP - 鲍新建
PLSQL_Oracle基本概念总结(汇总)
2014-08-16 Created By BaoXinjian
一、SQL Tunning类
1. 列举集中表的连接方式
Answer:等连接(内连接)、非等连接、自连接、外连接(左、右、全)
Or hash join/merge join/nest loop(cluster join)/index join
连接类型
定义
例子
内连接
只连接匹配的行
select A.c1,B.c2 from A join B on A.c3 =
B.c3;
左外连接
包含左边表的全部行(不管右边的表中是否存在与它们匹配的行)以及右边表中全部匹配的行
select A.c1,B.c2 from A left join B on A.c3 =
B.c3;
右外连接
包含右边表的全部行(不管左边的表中是否存在与它们匹配的行)以及左边表中全部匹配的行
select A.c1,B.c2 from A right join B on A.c3 =
B.c3;
全外连接
包含左、右两个表的全部行,不管在另一边的表中是否存在与它们匹配的行
select A.c1,B.c2 from A full join B on A.c3 =
B.c3;
(theta)连接
使用等值以外的条件来匹配左、右两个表中的行
select A.c1,B.c2 from A join B on A.c3 !=
B.c3;
交叉连接
生成笛卡尔积——它不使用任何匹配或者选取条件,而是直接将一个数据源中的每个行与另一个数据源的每个行一一匹配
select A.c1,B.c2 from
A,B;
2. 不借助第三方工具,怎么样查看sql的执行计划
Solution1. 使用Explain Plan,查询PLAN_TABLE;
EXPLAIN PLAN
SET STATEMENT_ID='QUERY1'
FOR
SELECT *
FROM a
WHERE aa=1;
SELECT operation, options, object_name, object_type, ID, parent_id
FROM plan_table
3 WHERE STATEMENT_ID = 'QUERY1'
ORDER BY ID;
Solution2. SQLPLUS中的SET TRACE 即可看到Execution Plan Statistics
SET AUTOTRACE ON;
3. 如何使用CBO,CBO与RULE的区别
(1). 结构
IF 初始化参数 OPTIMIZER_MODE = CHOOSE THEN --(8I DEFAULT)
IF 做过表分析
THEN 优化器 Optimizer=CBO(COST); /*高效*/
ELSE
优化器 Optimizer=RBO(RULE); /*高效*/
END IF;
END IF;
(2). 区别:
RBO根据规则选择最佳执行路径来运行查询。
CBO根据表统计找到最低成本的访问数据的方法确定执行计划。
(3). 使用CBO需要注意:
需要经常对表进行ANALYZE命令进行分析统计;
需要稳定执行计划;
需要使用提示(Hint);
(4). 使用RULE需要注意:
选择最有效率的表名顺序
优化SQL的写法;
(5). 总结
在optimizer_mode=choose时,如果表有统计信息(分区表外),优化器将选择CBO,否则选RBO。
RBO遵循简单的分级方法学,使用15种级别要点,当接收到查询,优化器将评估使用到的要点数目,然后选择最佳级别(最少的数量)的执行路径来运行查询。
CBO尝试找到最低成本的访问数据的方法,为了最大的吞吐量或最快的初始响应时间,计算使用不同的执行计划的成本,并选择成本最低的一个,关于表的数据内容的统计被用于确定执行计划。
4. 如何定位重要(消耗资源多)的SQL
Step1. 使用CPU多的用户session定位
SELECT a.SID, spid, status, SUBSTR (a.program, 1, 40) prog, a.terminal,a.SQL_TEXT, osuser, VALUE / 60 / 100 VALUE
FROM v$session a, v$process b, v$sesstat c
WHERE c.statistic# = 12 AND c.SID = a.SID AND a.paddr = b.addr
ORDER BY VALUE DESC;
Step2. 查找
select sql_text from v$sql
where disk_reads &gt; 1000 or (executions &gt; 0 and buffer_gets/executions &gt; 30000);
5. 如何跟踪某个session的SQL
Step1. 利用TRACE 跟踪
ALTER SESSION SET SQLTRACE ON;
COLUMN SQL format a200;
SELECT machine, sql_text SQL
FROM v$sqltext a, v$session b
WHERE address = sql_address
AND machine = '&A'
ORDER BY hash_value, piece;
exec dbms_system.set_sql_trace_in_session(sid,serial#,&amp;sql_trace);
select sid,serial# from v$session where sid = (select sid from v$mystat where rownum = 1);
exec dbms_system.set_ev(&amp;sid,&amp;serial#,&amp;event_10046,&amp;level_12,'');
6. SQL调整最关注的是什么
检查系统的I/O问题
sar-d能检查整个系统的iostat(IO
statistics)
查看该SQL的response time(db block gets/consistent
gets/physical reads/sorts (disk))
7. 说说你对索引的认识(索引的结构、对dml影响、对查询影响、为什么提高查询性能)
索
引有B-TREE、BIT、CLUSTER等类型。ORACLE使用了一个复杂的自平衡B-tree结构;通常来说,在表上建立恰当的索引,查询时会改进
查询性能。但在进行插入、删除、修改时,同时会进行索引的修改,在性能上有一定的影响。有索引且查询条件能使用索引时,数据库会先度取索引,根据索引内容
和查询条件,查询出ROWID,再根据ROWID取出需要的数据。由于索引内容通常比全表内容要少很多,因此通过先读索引,能减少I/O,提高查询性能。
b-tree index/bitmap index/function index/patitional
index(local/global)索引通常能提高select/update/delete的性能,会降低insert的速度,
8. 使用索引查询一定能提高查询的性能吗?为什么
通常,通过索引查询数据比全表扫描要快.但是我们也必须注意到它的代价.
索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时,索引本身也会被修改. 这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5
次的磁盘I/O. 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢.使用索引查询不一定能提高查询性能,索引范围查询(INDEX RANGE SCAN)适用于两种情况:
基于一个范围的检索,一般查询返回结果集小于表中记录数的30%宜采用;
基于非唯一性索引的检索
索引就是为了提高查询性能而存在的,如果在查询中索引没有提高性能,只能说是用错了索引,或者讲是场合不同
9. 绑定变量时什么,绑定变量的优缺点
绑定变量是指在SQL语句中使用变量,改变变量的值来改变SQL语句的执行结果。
优点:使用绑定变量,可以减少SQL语句的解析,能减少数据库引擎消耗在SQL语句解析上的资源。提高了编程效率和可靠性。减少访问数据库的次数,
就能实际上减少ORACLE的工作量。
缺点:经常需要使用动态SQL的写法,由于参数的不同,可能SQL的执行效率不同;
绑定变量是相对文本变量来讲的,所谓文本变量是指在SQL直接书写查询条件,
这样的SQL在不同条件下需要反复解析,绑定变量是指使用变量来代替直接书写条件,查询bind
value在运行时传递,然后绑定执行。
优点是减少硬解析,降低CPU的争用,节省shared_pool
缺点是不能使用histogram,sql优化比较困难
10. 如何稳定(固定)执行计划
可以在SQL语句中指定执行计划。使用HINTS;
query_rewrite_enabled = true
star_transformation_enabled = true
optimizer_features_enable = 9.2.0
创建并使用stored outline
11. 和排序相关的内存在8i和9i分别怎样调整,临时表空间的作用是什么
SORT_AREA_SIZE 在进行排序操作时,如果排序的内容太多,内存里不能全部放下,则需要进行外部排序,
此时需要利用临时表空间来存放排序的中间结果。
8i中sort_area_size/sort_area_retained_size决定了排序所需要的内存,
如果排序操作不能在sort_area_size中完成,就会用到temp表空间
9i中如果workarea_size_policy=auto时,
排序在pga内进行,通常pga_aggregate_target的1/20可以用来进行disk sort;
如果workarea_size_policy=manual时,排序需要的内存由sort_area_size决定, 在执行order by/group by/distinct/union/create index/index
rebuild/minus等操作时,如果在pga或sort_area_size中不能完成,排序将在临时表空间进行(disk sort),临时表空间主要作用就是完成系统中的disk
sort.
12. 存在表T(a,b,c,d),要根据字段c排序后取第21—30条记录显示,请给出sql
Solution1.
SELECT *
FROM (SELECT ROWNUM AS row_num, tmp_tab.*
FROM (SELECT a, b, c, d
FROM T
ORDER BY c) tmp_tab
WHERE ROWNUM <= 30)
WHERE row_num >= 20
ORDER BY row_num;
Solution2.
create table t(a number(,b number(,c number(,d number();
/
begin
for i in 1 .. 300 loop
insert into t values(mod(i,2),i/2,dbms_random.value(1,300),i/4);
end loop;
end;
/
Solution3.
select * from (select c.*,rownum as rn from (select * from t order by c desc) c) where rn between 21 and 30;
/
select * from (select * from test order by c desc) x where rownum &lt; 30
minus
select * from (select * from test order by c desc) y where rownum &lt; 20 order by 3 desc
相比之 minus性能较差
二:数据库基本概念类
1. Pctused and pctfree 表示什么含义有什么作用
pctused 与pctfree控制数据块是否出现在freelist中, pctfree控制数据块中保留用于update的空间,当数据块中的free space小于pctfree设置的空间时,该数据块从freelist中去掉,当块由于dml操作free space大于pct_used设置的空间时,该数据库块将被添加在freelist链表中。
2. 简单描述tablespace / segment / extent / block之间的关系
(1). tablespace: 一个数据库划分为一个或多个逻辑单位,该逻辑单位成为表空间;每一个表空间可能包含一个或多个 Segment;
(2). Segments: Segment指在tablespace中为特定逻辑存储结构分配的空间。每一个段是由一个或多个extent组成。包括数据段、索引段、回滚段和临时段。
(3). Extents: 一个 extent 由一系列连续的 Oracle blocks组成.ORACLE为通过extent 来给segment分配空间。
(4). Data Blocks:Oracle 数据库最小的I/O存储单位,一个data block对应一个或多个分配给data file的操作系统块。
(5). table创建时,默认创建了一个data segment,每个data segment含有min extents指定的extents数,每个extent据据表空间的存储参数分配一定数量的blocks
3. 描述tablespace和datafile之间的关系
一个表空间可包含一个或多个数据文件。表空间利用增加或扩展数据文件扩大表空间,表空间的大小为组成该表空间的数据文件大小的和。一个datafile只能属于一个表空间;
一 个tablespace可以有一个或多个datafile,每个datafile只能在一个tablespace内, table中的数据,通过hash 算法分布在tablespace中的各个datafile中,tablespace是逻辑上的概念,datafile则在物理上储存了数据库的种种对象。
4. 本地管理表空间和字典管理表空间的特点,ASSM有什么特点
本地管理表空间:(9i默认)空闲块列表存储在表空间的数据文件头。
特点:减少数据字典表的竞争,当分配和收缩空间时会产生回滚,不需要合并。
字典管理表空间:(8i默认)空闲块列表存储在数据库中的字典表里.
特点:片由数据字典管理,可能造成字典表的争用。存储在表空间的每一个段都会有不同的存储字句,需要合并相邻的块;
本地管理表空间(Locally Managed Tablespace简称LMT)
8i以后出现的一种新的表空间的管理模式,通过位图来管理表空间的空间使用。字典管理表空间(Dictionary-Managed Tablespace简称DMT)
8i以前包括以后都还可以使用的一种表空间管理模式,通过数据字典管理表空间的空间使用。动段空间管理(ASSM),它首次出现在Oracle920里有了ASSM,链接列表freelist被位图所取代,它是一个二进制的数组,
能够迅速有效地管理存储扩展和剩余区块(free block),因此能够改善分段存储本质,ASSM表空间上创建的段还有另外一个称呼叫Bitmap Managed Segments(BMB 段)。
5. 回滚段的作用是什么
回滚段用于保存数据修改前的映象,这些信息用于生成读一致性数据库信息、在数据库恢复和Rollback时使用。一个事务只能使用一个回滚段。
事务回滚:当事务修改表中数据的时候,该数据修改前的值(即前影像)会存放在回滚段中,当用户回滚事务(ROLLBACK)时,ORACLE将会利用回滚段中的数据前影像来将修改的数据恢复到原来的值。
事务恢复:当事务正在处理的时候,例程失败,回滚段的信息保存在undo表空间中,ORACLE将在下次打开数据库时利用回滚来恢复未提交的数据。
读一致性:当一个会话正在修改数据时,其他的会话将看不到该会话未提交的修改。 当一个语句正在执行时,该语句将看不到从该语句开始执行后的未提交的修改(语句级读一致性)
当 ORACLE执行SELECT语句时,ORACLE依照当前的系统改变号(SYSTEM CHANGE NUMBER-SCN) 来保证任何前于当前SCN的未提交的改变不被该语句处理。可以想象:当一个长时间的查询正在执行时, 若其他会话改变了该查询要 查询的某个数据块,ORACLE将利用回滚段的数据前影像来构造一个读一致性视图
6. 日志的作用是什么
日志文件(Log File)记录所有对数据库数据的修改,主要是保护数据库以防止故障,以及恢复数据时使用。其特点如下:
a)每一个数据库至少包含两个日志文件组。每个日志文件组至少包含两个日志文件成员。
b)日志文件组以循环方式进行写操作。
c)每一个日志文件成员对应一个物理文件。
记录数据库事务,最大限度地保证数据的一致性与安全性
重做日志文件:含对数据库所做的更改记录,这样万一出现故障可以启用数据恢复,一个数据库至少需要两个重做日志文件
归档日志文件:是重做日志文件的脱机副本,这些副本可能对于从介质失败中进行恢复很必要。
7. SGA主要有那些部分,主要作用是什么
系统全局区(SGA):是ORACLE为实例分配的一组共享缓冲存储区,用于存放数据库数据和控制信息,以实现对数据库数据的管理和操作。
SGA主要包括:
a)共享池(shared pool) :用来存储最近执行的SQL语句和最近使用的数据字典的数据。
b)数据缓冲区 (database buffer cache):用来存储最近从数据文件中读写过的数据。
c)重作日志缓冲区(redo log buffer):用来记录服务或后台进程对数据库的操作。
另外在SGA中还有两个可选的内存结构:
d)Java pool: 用来存储Java代码。
e)Large pool: 用来存储不与SQL直接相关的大型内存结构。备份、恢复使用。
GA:db_cache/shared_pool/large_pool/java_pool
db_cache: 数据库缓存(Block Buffer)对于Oracle数据库的运转和性能起着非常关键的作用,它占据Oracle数据库SGA(系统共享内存区)的主要部分。Oracle数据 库通过使用LRU算法,将最近访问的数据块存放到缓存中,从而优化对磁盘数据的访问.
shared_pool: 共享池的大小对于Oracle 性能来说都是很重要的。共享池中保存数据字典高速缓冲和完全解析或编译的的PL/SQL 块和SQL 语句及控制结构
large_pool: 使用MTS配置时,因为要在SGA中分配UGA来保持用户的会话,就是用Large_pool来保持这个会话内存使用RMAN做备份的时候,要使用Large_pool这个内存结构来做磁盘I/O缓存器
java_pool: 为java procedure预备的内存区域,如果没有使用java proc,java_pool不是必须的
8. Oracle系统进程主要有哪些,作用是什么
数据写进程(DBWR):负责将更改的数据从数据库缓冲区高速缓存写入数据文件
日志写进程(LGWR):将重做日志缓冲区中的更改写入在线重做日志文件
系统监控 (SMON): 检查数据库的一致性如有必要还会在数据库打开时启动数据库的恢复
进程监控 (PMON): 负责在一个Oracle 进程失败时清理资源
检查点进程(CKPT):负责在每当缓冲区高速缓存中的更改永久地记录在数据库中时,更新控制文件和数据文件中的数据库状态信息。
归档进程 (ARCH):在每次日志切换时把已满的日志组进行备份或归档
恢复进程 (RECO): 保证分布式事务的一致性,在分布式事务中,要么同时commit,要么同时rollback;
作业调度器(CJQ ): 负责将调度与执行系统中已定义好的job,完成一些预定义的工作.
三:备份恢复类
1. 备份如何分类
(1). 分类
逻辑备份:exp/imp 指定表的逻辑备份
物理备份:
热备份:alter tablespace begin/end backup;
冷备份:脱机备份(database shutdown)
RMAN备份
full backup/incremental backup(累积/差异)
(2). 物理备份
物理备份是最主要的备份方式。用于保证数据库在最小的数据库丢失或没有数据丢失的情况下得到恢复。
(3).冷物理
冷物理备份提供了最简单和最直接的方法保护数据库因物理损坏丢失。建议在以下几种情况中使用。
对一个已经存在大最数据量的数据库,在晚间数据库可以关闭,此时应用冷物理备份。
对需对数据库服务器进行升级,(如更换硬盘),此时需要备份数据库信息,并在新的硬盘中恢复这些数据信息,建议采用冷物理备份。
(4).热物理
主要是指备份过程在数据库打开并且用户可以使用的情况下进行。需要执行热物理备份的情况有:
由于数据库性质要求不间断工作,因而此时只能采用热物理备份。
由于备份的要求的时间过长,而数据库只能短时间关闭时。
(5).逻辑备份 (EXP/IMP)
逻辑备份用于实现数据库对象的恢复。但不是基于时间点可完全恢复的备份策略。只能作为联机备份和脱机备份的一种补充。
(6).完全逻辑备份
完全逻辑备份是将整个数据库导出到一个数据库的格式文件中,该文件可以在不同的数据库版本、操作系统和硬件平台之间进行移植。
(7).指定表的逻辑备份
通过备份工具,可以将指定的数据库表备份出来,这可以避免完全逻辑备份所带来的时间和财力上的浪费。
2. 归档是什么含义
关于归档日志:Oracle要将填满的在线日志文件组归档时,则要建立归档日志(archived redo log)。其对数据库备份和恢复有下列用处:
数据库后备以及在线和归档日志文件,在操作系统和磁盘故障中可保证全部提交的事物可被恢复。
在数据库打开和正常系统使用下,如果归档日志是永久保存,在线后备可以进行和使用。
数据库可运行在两种不同方式下:NOARCHIVELOG方式或ARCHIVELOG 方式
数据库在NOARCHIVELOG方式下使用时,不能进行在线日志的归档,
数据库在ARCHIVELOG方式下运行,可实施在线日志的归档
归档是归档当前的联机redo日志文件。
SVRMGR> alter system archive log current;
数据库只有运行在ARCHIVELOG模式下,并且能够进行自动归档,才可以进行联机备份。有了联机备份才有可能进行完全恢复。
3. 如果一个表在2004-08-04 10:30:00 被drop,在有完善的归档和备份的情况下,如何恢复
9i 新增的FLASH BACK 应该可以;
Logminer应该可以找出DML。
有完善的归档和备份,先归档当前数据,然后可以先恢复到删除的时间点之前,把DROP 的表导出来,然后再恢复到最后归档时间;
手工拷贝回所有备份的数据文件
Sql〉startup mount;
sql〉alter database recover automatic until time '2004-08-04:10:30:00';
sql〉alter database open resetlogs;
4. rman是什么,有何特点
RMAN(Recovery Manager)是DBA的一个重要工具,用于备份、还原和恢复oracle数据库, RMAN 可以用来备份和恢复数据库文件、归档日志、控制文件、系统参数文件,也可以用来执行完全或不完全的数据库恢复。
RMAN有三种不同的用户接口:COMMAND LINE方式、GUI 方式(集成在OEM 中的备份管理器)、API 方式(用于集成到第三方的备份软件中)。
具有如下特点:
1)功能类似物理备份,但比物理备份强大N倍;
2)可以压缩空块;
3)可以在块水平上实现增量;
4)可以把备份的输出打包成备份集,也可以按固定大小分割备份集;
5)备份与恢复的过程可以自动管理;
6)可以使用脚本(存在Recovery catalog 中)
7)可以做坏块监测
5. standby的特点
备 用数据库(standby database):ORACLE推出的一种高可用性(HIGH AVAILABLE)数据库方案,在主节点与备用节点间通过日志同步来保证数据的同步,备用节点作为主节点的备份,可以实现快速切换与灾难性恢复,从 920开始,还开始支持物理与逻辑备用服务器。
9i中的三种数据保护模式分别是:
1)、MAXIMIZE PROTECTION :最大数据保护与无数据分歧,LGWR将同时传送到备用节点,在主节点事务确认之前,备用节点也必须完全收到日志数据。如果网络不好,引起LGWR不能传送数据,将引起严重的性能问题,导致主节点DOWN机。
2)、MAXIMIZE AVAILABILITY :无数据丢失模式,允许数据分歧,允许异步传送。
正常情况下运行在最大保护模式,在主节点与备用节点的网络断开或连接不正常时,自动切换到最大性能模式,主节点的操作还是可以继续的。在网络不好的情况下有较大的性能影响。
3)、MAXIMIZE PERFORMANCE:这种模式应当可以说是从8i继承过来的备用服务器模式,异步传送,无数据同步检查,可能丢失数据,但是能获得主节点的最大性能。9i在配置DATA GUARD的时候默认就是MAXIMIZE PERFORMANCE
6. 对于一个要求恢复时间比较短的系统(数据库50G,每天归档5G),你如何设计备份策略
数据库比较大逻辑备份没什么必要,每天归档5G,每周三/周六自动归档10G,每月RMAN归档全库。应该有standby。
rman/每月一号 level 0 每周末/周三 level 1 其它每天level 2
四:系统管理类
1. 对于一个存在系统性能的系统,说出你的诊断处理思路
(1). 做statspack收集系统相关信息 了解系统大致情况/确定是否存在参数设置不合适的地方/查看top 5 event/查看top sql等
(2). 查v$system_event/v$session_event/v$session_wait 从v$system_event开始,确定需要什么资源(db file sequential read)等,深入研究v$session_event,确定等待事件涉及的会话,从v$session_wait确定详细的资源争用情况(p1-p3的 值:file_id/block_id/blocks等)
(3). 通过v$sql/v$sqltext/v$sqlarea表确定disk_reads、(buffer_gets/executions)值较大的SQL
2. 列举几种诊断IO、CPU、性能状况的方法
top uptime vmstat iostat statspack sql_trace/tkprof
查v$system_event/v$session_event/v$session_wait
查v$sqlarea(disk_reads或buffer_gets/executions较大的SQL)
或者第三方的监视工具,TOAD就不错。
3. 对statspack有何认识
认识不深。仅限了解。StapSpack是Oracle公司提供的一个收集数据库运行性能指标的软件包。可以做数据库健康检查报告。
StapSpack是Oracle公司提供的一个收集数据库运行性能指标的软件包,该软件包从8i起,在9i、10g都有显著的增强
该软件包的辅助表(存储相关参数与收集的性能指标的表)由最初的25个增长到43个
收集级别参数由原来的3个(0、5、10)增加到5个(0、5、6、7、10)
通过分析收集的性能指标,数据库管理员可以详细地了解数据库目前的运行情况,对数据库实例、等待事件、SQL等进行优化调整
利用statspack收集的snapshot,可以统计制作数据库的各种性能指标的统计趋势图表。
4. 如果系统现在需要在一个很大的表上创建一个索引,你会考虑那些因素,如何做以尽量减小对应用的影响
可以先表分析一下,然后测试创建索引前后对应用的性能影响;
需要考虑的是该索引列不经常更新,不是有很多重复值的情况时, 在大表中使用索引特别有效. 创建的索引可以跟数据表分不同表空间存储。
在系统比较空闲时nologging选项(如果有dataguard则不可以使用nologging)
大的sort_ared_size或pga_aggregate_target较大
5. 对raid10 和raid5有何认识
RAID 10(或称RAID 1+0)与RAID 0+1不同,它是用硬盘驱动器先组成RAID 1阵列,然后在RAID 1阵列之间再组成RAID 0阵列。
RAID 10模式同RAID 0+1模式一样具有良好的数据传输性能,但却比RAID 0+1具有更高的可靠性。RAID 10阵列的实际容量为M×n/2,磁盘利用率为50%。RAID 10也需要至少4个硬盘驱动器构成,因而价格昂贵。
RAID 10的可靠性同RAID 1一样,但由于RAID 10硬盘驱动器之间有数据分割,因而数据传输性能优良。
RAID 5与RAID 3很相似,不同之处在于RAID 5的奇偶校验信息也同数据一样被分割保存到所有的硬盘驱动器,而不是写入一个指定的硬盘驱动器,从而消除了单个奇偶校验硬盘驱动器的瓶颈问题。RAID 5磁盘阵列的性能比RAID 3有所提高,但仍然需要至少3块硬盘驱动器。其实际容量为M×(n-1),磁盘利用率为(n-1)/n 。
五:综合随意类
1. 你最擅长的是oracle哪部分?
pl/sql及sql优化
2. 喜欢oracle吗?喜欢上论坛吗?或者偏好oracle的哪一部分?
喜欢。PL/SQL比较得心应手。
3. 随意说说你觉得oracle最有意思的部分或者最困难的部分
我对数据库的备份/恢复和性能调优经验明显不足,自然觉得有些困难。
基于ORACLE的研究应该是个宽广的领域,所以我觉得还是有意思的。
4. 为何要选择做DBA呢?
我对数据库的备份/恢复和性能调优经验明显不足,主要是缺乏环境和交流。
因此,算不上什么DBA。不过因此我更需要这样的机会。
不过就整个ORACLE 来说,一直从事与它相关的工作,感情还是颇深的。放弃可惜。而且就技术本身而言我觉得自己还是有学习和创新的能力,它的诸如数据仓库,数据挖掘之类的领域也很广。
5. 存储过程和函数的区别
存储过程是用户定义的一系列sql语句的集合,涉及特定表或其它对象的任务,用户可以调用存储过程,而函数通常是数据库已定义的方法,它接收参数并返回某种类型的值并且不涉及特定用户表。
6. 事务是什么?
事务是作为一个逻辑单元执行的一系列操作,一个逻辑工作单元必须有四个属性,称为 ACID(原子性、一致性、隔离性和持久性)属性,只有这样才能成为一个事务:
原子性:事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。
一致性:事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构(如 B 树索引或双向链表)都必须是正确的。
隔 离性:由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务 修改它之后的状态,事务不会查看中间状态的数据。这称为可串行性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行 的状态相同。
持久性:事务完成之后,它对于系统的影响是永久性的。该修改即使出现系统故障也将一直保持。
7. 游标的作用?如何知道游标已经到了最后?
游标用于定位结果集的行,通过判断全局变量@@FETCH_STATUS可以判断是否到了最后,通常此变量不等于0表示出错或到了最后。
8. 触发器分为事前触发和事后触发,这两种触发有和区别。语句级触发和行级触发有何区别。
事前触发器运行于触发事件发生之前,而事后触发器运行于触发事件发生之后。通常事前触发器可以获取事件之前和新的字段值。
语句级触发器可以在语句执行前或后执行,而行级触发在触发器所影响的每一行触发一次。
Thanks and Regards
ERP技术讨论群: 288307890
技术交流,技术讨论,欢迎加入
Technology Blog Created By Oracle ERP - 鲍新建
RAC3——RAC原理开始
1、RAC并发
RAC的本质是一个数据库,只不过现在这个数据库运行在了多台计算机上,在原先的单实例中,一个进程是否可以修改一条数据,取决于是否有其他进程(同一台计算机上)并发修改。在RAC环境下,这种判断已经不够了,还必须坚持其他计算机上的进程是否有并发修改。
于是RAC要解决的第一个问题就是:如何在多台计算机环境下感知并发的存在?
对于检查本机上的并发,用传统的单实例中的锁机制就可以解决,单对于其他计算机上的并发检测,必须引入一个新的机制,这个机制就是分布式锁管理器(distributed lock management DLM),我们可以把DLM想象成一个“仲裁”,他记录着哪个节点正在用哪种方式操作哪个数据,比负责协调解决节点间的竞争。
下面我们用一个例子来说明DLM的机制原理:
一个2节点的RAC,节点1想要修改数据A,节点1想DLM请求,DLM发现数据A还没有被任何节点使用,DLM就授权给节点1,并向DLM登记节点1对数据1的使用,这时,节点2也想修改数据A,节点2向DLM请求,DLM发现数据A正被节点1使用,DLM就会请求节点1,“先给节点2用吧”,节点1收到请求后释放其对数据1的占用,节点2能够操作数据A。DLM记录这个过程。
需要强调的是DLM负责的是节点间的协调,而节点内的协调不是DLM的责任,我们继续考虑上面的例子:
这时,节点2的进程1在修改数据A,节点2的进程2也想修改数据A,节点2仍然请求DLM,但是DLM发现节点2已经有权限,无序授权。进程2对DLM的请求被通过,但是进程2是否能够修改数据A,还需要进一步通过传统的锁模式检查。
解决了第一个问题后,第二个问题就出现了,我们上文提到的数据A到底是啥呢?或者说DLM到底在那个层次上对资源的冲突进行协调?那个A是一行记录?还是一个数据块?还是一个数据文件?哈哈——答案是:数据块!!!
也就是说,进程想要修改A时,向DLM提出申请的是“数据块A的操作权限”。
oracle集群发展历史分为两个阶段,最初的是oracle并行服务器(oracle parallel server OPS),之后到9i时改成RAC,两个阶段的DLM名称也不同 ,ops的叫做pcm,RAC的叫做Cache Fusion。现在看来我们只需知道一点:现在DLM的名称叫Cache Fusion。
在DLM中,根据资源数量,活动密集程度,把资源分成两类:cache fusion 和 non-cache fusion。
cache fusion resource:是指数据块这种资源,包括 普通数据块,索引数据块,段头块(segement header), undo 数据块。
非数据块资源全部都归类为non-cache fusion resource :包括数据文件,控制文件,数据字典视图,library cache,row cache等等。
对于典型的non-cache-fusion资源,我们对library cache做一个说明,library cache中主要存的是sql语句,执行计划,plsql的包,存储过程,还有这些对象所引用的对象,当这些sql语句进行编译的时候,会对这些对象应用的对象加上一个library cache lock ,而在这些sql对象执行的时候,会对这些引用的对象加上library cache pin来保证sql语句执行的过程中应用对象的结构不会发生变化。
需要特别说明的是,当编译完成后,引用对象上的library cache lock会由shared或者exclusive模式转变成null模式,null模式的library cache look相当于一个触发器,每当引用的对象的结构遭到改变,或者定义被修改,如添加一列。。那么引用他的sql语句编译的对象就成了无效的了,需要对那个sql语句重新编译。例如:select * from a。编译后这个语句的执行计划对象会在a上加一个null模式的library cache lock。当我们改变a的机构(如增加一个新字段),此触发器就会导致select
* from a 这个语句的执行计划失效。重新执行此sql的时候,需要重新编译。
在RAC环境下这个问题进一步的延伸,在每个节点上,都有可能有表a的引用对象。在任何一个节点上对a的结构进行了修改,那其他所有节点上a的对象都应被置为无效。因此:除了传统的library cache lock之外,每个节点的LCK0进程会对本实例library cache 中对象加一个shared mode的IV(Invalidation) instance lock。如果某用户想要修改对象的定义。必须先获得一个Exclusive 模式的IV锁,这会通知本地的LCK0进程释放shared
mode锁。本地LCK0释放这个shared mode锁之前,会通知其他节点的LCK0,其他节点的LCK0进程收到这个消息,会将本地library cache 中的相关的对象置为无效。
这是一种广播机制,这种通信过程是通过实例的LMD(此进程详细介绍在下一节)进程完成的。
Row Cache中存放的是数据字典,其目的是编译过程减少对磁盘的访问。其内容也需要在所有实例中同步。其同步机制和library cache 是一样的,也是由LCK0进程完成。
2、 GRD(Global Resource Directory )
可以把 GRD 看作一个内部数据库,这里记录的是每一个数据块在集群间分布图,它位于每一个实例的SGA 中,但是每个实例 SGA 中都是部分 GRD , 所有实例的GR 汇总在一起就是个完整的 GRD 。
RAC 会根据每个资源的名称从集群中选择一 个节点作为它的 Master Node , 而其他节点叫作 Shadow Node。 Master Node 的 GRD 中记录了该资源在所有节 点上的使用信息 ,而 Shadow Node 的 GRD 中只需要记录资源在该节点上的使用情况,这些信息实际就是PCM Lock信息 。 PCM Lock 有 3个属性: Mode ,Role和 PI(Past Image)。下图显示了GRD内容结构:
3、PCM Lock
由上文我们知道了GRD中记录的是pcm lock信息,这种锁有3个属性:mode,role,PI。
下面我们挨个看看这三个属性是怎么个意思:
1)mode:这个属性用于描述锁的模式,其中有3种取值,如下所示:
2)role:每个数据块可以被多个节点修改,role这个属性是用来描述“脏数据块”在集群间的分布状况的,其中有local 和 global 两个取值,下面结合mode来解释各个role的含义:
对于local role,可能的mode 只会是S 和 X;如果mode是S ,代表这个内存数据块时和磁盘上的内容完全一致的;如果mode是X,则代表这个数据块在内存中做过修改,但是修改没有被写回磁盘,也就是“脏数据块”;对于拥有local role的实例而言,如果要把这个数据块写到磁盘,不需要联系GRD,由本实例完成即可。
如果拥有local role的X mode 的实例要给其他实例发送这个数据块,如果发送的是和磁盘一直的版本,也就是说接收方收到的也是磁盘一致的版本,那么本实例就仍然保持local role; 如果发送的是和磁盘不一致的版本,那么角色就要转变成global,同时接收方的角色也是global,代表同时有多个实例拥有“脏数据块”版本。
如果是global role,可能的mode 是S,X,Null,global role 首先意味着有多个实例拥有和磁盘一致版本,这时如果想要把这个数据写到擦盘,必须联系GRD,由拥有数据块的current版本来完成写动作。
3)past Image: 下面通过一个例子说明什么是past image,假设一个2节点的rac集群,某个数据块在磁盘上的scn=100:
好了下面我们开始。。。实例1要修改这个数据块,从磁盘读入SGA进行修改,修改后内存的scn=110。实例2也要修改该这个数据块,实例1就会通过cache fusion 把这个数据块传送给实例2,发送的是scn=110的版本,即current copy的数据块;这时实例1还会保留这个scn=110的数据块在sga中,但是不能在进行任何修改操作,这时实例1拥有的这个拷贝就是一个past image,其中scn=110;在实例1发送这个数据块之前,会把log buffer 的内容写到redo log中。接下来实例2修改这个数据块,修改后的scn=120;注意,此时磁盘上的版本仍然是scn=100;假设实例1现在因为日志切换,触发了检查点动作,因为实例1上的数据块是个脏数据块(但不是最脏的,哈哈,还有实例2上那个scn=120的版本最脏),所以要把这个数据块也同步到磁盘。实例1会找到GRD,发现实例2拥有这个数据块的current版本,GRD会通知实例2把这个数据块写入磁盘。实例2完成写入之后,会通知其他实例(所有拥有PI版本的实例)释放他们拥有的PI内存了。这时,实例1会在log
buffer中记录一条BWR(block write record)记录,然后释放PI内存。
假设实例2没有完成写时就异常宕机了,这时会触发实例1上进行crash recovery(不同与单实例instance recovery)虽然修改动作都被记录在各个节点的联机日志中,但是因为实例1拥有最近的PI,所以只需要实例1的PI及实例2的联机日志就可以完成恢复。
所以,past image代表着这个实例的SGA中是否拥有和磁盘内容不一致的版本,以及版本顺序,并不是代表这个节点是否曾经修改过这个数据块,past image主要能够加速crash recovery的恢复过程。
下面通过读写实例介绍RAC的工作过程:
4、AST
到现在为止,想必各位已经知道了所谓的cache fusion资源(也就是数据块)是如何被传输工作的了,但是前面的讲述故意遗漏了一个细节还没有交代,就是这些请求在DLM中是如何管理的,主要是避免分散读者的注意力,现在把这部分内容补上。
DLM使用两个队列跟踪所有的lock 请求,并用两个ASTs(asynchronous traps)来完成请求的发送和响应,实际就是异步中断(interrupt)或者陷阱(trap)。下图显示的是资源和队列的关系,granted queue中记录的是所有已经获得的lock的进程,而convert queue记录时是所有等待lock的进程。
进程1和进程2拥有数据块S模式的锁,因此在granted queue 中有记录,假设现在进程2要获得X模式的锁,进程2必须先向DLM提出请求;请求提交给DLM后,DLM就要把进程2放在convert queue中。向拥有不兼容模式锁的进程1发送一个blocking ASTs,这是一个异步请求,所以DLM不必等待响应。当进程1接受到这个BAST之后,就会把这个lock降级为null模式,DLM把进程2的锁模式转换为x模式,如下图所示:
然后,DLM发送一个acquisition ASTn(AAST)给进程2,并把进程2放到Granted queue中,如下图所示,进程2就可以继续处理了:
5、RAC并发控制总结
在cache fusion中,每一个数据块都会被映射成一个cache fusion 资源,或者说是一个PCM资源,pcm资源实际是一个数据结构,资源的名称就是数据块的地址(dba)。每个数据请求动作是分步完成的。首先把数据块地址x转换成pcm资源名称,然后把这个pcm资源请求提交给DLM,DLM进行global lock的申请,释放活动,只有获得了pcm lock,才能继续下一步;也就是说第一步“实例要获得数据块的使用权”
除了获得数据块的使用权,还要考虑数据块状态。在单实例中,进程想要修改数据块,必须在数据块的当前版本(current copy)上进行修改,在RAC环境下也一样,如果实例要修改该数据块,必须获得这个数据块的当前版本拷贝,这就涉及一系列的问题:如何获得数据块的拷贝在集群节点间的分布图,如何知道哪个节点拥有的是当前的拷贝,如何完成传递过程,这一些问题的解决机制就是内存融合技术(cache fusion) 。一旦实例获得了访问权限,并且也得到了正确的版本。然后进程就能访问资源了,进程间仍然使用传统的lock,latch,这一点和单实例没有区别。