星期六, 07月 22nd, 2006


Oracle中学22 Jul 2006 11:09 pm

  一、如何分析即LogMiner解释  从目前来看,分析Oracle日志的唯一方法就是使用Oracle公司提供的LogMiner来进行, Oracle数据库的所有更改都记录在日志中,但是原始的日志信息我们根本无法看懂,而LogMiner就是让我们看懂日志信息的工具。从这一点上看,它和tkprof差不多,一个是用来分析日志信息,一个则是格式化跟踪文件。通过对日志的分析我们可以实现下面的目的:   从目前来看,分析日志的唯一方法就是使用公司提供的来进行,数据库的所有更改都记录在日志中,但是原始的日志信息我们根本无法看懂,而就是让我们看懂日志信息的工具。从这一点上看,它和差不多,一个是用来分析日志信息,一个则是格式化跟踪文件。通过对日志的分析我们可以实现下面的目的:  1、查明数据库的逻辑更改;  2、侦察并更正用户的误操作;  、侦察并更正用户的误操作;  3、执行事后审计;  4、执行变化分析。  、执行变化分析。  不仅如此,日志中记录的信息还包括:数据库的更改历史、更改类型(INSERTUPDATEDELETEDDL等)、更改对应的SCN号、以及执行这些操作的用户信息等,LogMiner在分析日志时,将重构等价的SQL语句和UNDO语句(分别记录在V$LOGMNR_CONTENTS视图的SQL_REDOSQL_UNDO中)。这里需要注意的是等价语句,而并非原始SQL语句,例如:我们最初执行的是“delete a where c1 ‘cyx’;”,而LogMiner重构的是等价的6DELETE语句。所以我们应该意识到V$LOGMNR_CONTENTS视图中显示的并非是原版的现实,从数据库角度来讲这是很容易理解的,它记录的是元操作,因为同样是“delete a where c1 ‘cyx’;”语句,在不同的环境中,实际删除的记录数可能各不相同,因此记录这样的语句实际上并没有什么实际意义,LogMiner重构的是在实际情况下转化成元操作的多个单条语句。  另外由于Oracle重做日志中记录的并非原始的对象(如表以及其中的列)名称,而只是它们在Oracle数据库中的内部编号(对于表来说是它们在数据库中的对象ID,而对于表中的列来说,对应的则是该列在表中的排列序号:COL 1, COL 2 等),因此为了使LogMiner重构出的SQL语句易于识别,我们需要将这些编号转化成相应的名称,这就需要用到数据字典(也就说LogMiner本身是可以不用数据字典的,详见下面的分析过程),LogMiner利用DBMS_LOGMNR_D.BUILD()过程来提取数据字典信息。  另外由于重做日志中记录的并非原始的对象(如表以及其中的列)名称,而只是它们在数据库中的内部编号(对于表来说是它们在数据库中的对象,而对于表中的列来说,对应的则是该列在表中的排列序号:等),因此为了使重构出的语句易于识别,我们需要将这些编号转化成相应的名称,这就需要用到数据字典(也就说本身是可以不用数据字典的,详见下面的分析过程),利用过程来提取数据字典信息。  LogMiner包含两个PL/SQL包和几个视图:  1dbms_logmnr_d包,这个包只包括一个用于提取数据字典信息的过程,即dbms_logmnr_d.build()过程。   、包,这个包只包括一个用于提取数据字典信息的过程,即过程。  2dbms_logmnr包,它有三个过程:  add_logfile(name varchar2, options number) – 用来添加/删除用于分析的日志文件;  用来添加删除用于分析的日志文件;  start_logmnr(start_scn number, end_scn number, start_time number,end_time number, dictfilename varchar2, options number) – 用来开启日志分析,同时确定分析的时间/SCN窗口以及确认是否使用提取出来的数据字典信息。  end_logmnr() – 用来终止分析会话,它将回收LogMiner所占用的内存。  用来终止分析会话,它将回收所占用的内存。  与LogMiner相关的数据字典。  1v$logmnr_dictionaryLogMiner可能使用的数据字典信息,因logmnr可以有多个字典文件,该视图用于显示这方面信息。  、,可能使用的数据字典信息,因可以有多个字典文件,该视图用于显示这方面信息。  2v$logmnr_parameters,当前LogMiner所设定的参数信息。   3v$logmnr_logs,当前用于分析的日志列表。  4v$logmnr_contents,日志分析结果。    、,日志分析结果。   二、Oracle9i LogMiner的增强:  1、支持更多数据/存储类型:链接/迁移行、CLUSTER表操作、DIRECT PATH插入以及DDL操作。在V$LOGMNR_CONTENTSSQL_REDO中可以看到DDL操作的原句(CREATE USER除外,其中的密码将以加密的形式出现,而不是原始密码)。如果TX_AUDITING初始化参数设为TRUE,则所有操作的数据库账号将被记录。  、支持更多数据存储类型:链接迁移行、表操作、插入以及操作。在的中可以看到操作的原句(除外,其中的密码将以加密的形式出现,而不是原始密码)。如果初始化参数设为,则所有操作的数据库账号将被记录。  2、提取和使用数据字典的选项:现在数据字典不仅可以提取到一个外部文件中,还可以直接提取到重做日志流中,它在日志流中提供了操作当时的数据字典快照,这样就可以实现离线分析。  3、允许对DML操作按事务进行分组:可以在START_LOGMNR()中设置COMMITTED_DATA_ONLY选项,实现对DML操作的分组,这样将按SCN的顺序返回已经提交的事务。  、允许对操作按事务进行分组:可以在中设置选项,实现对操作的分组,这样将按的顺序返回已经提交的事务。  4、支持SCHEMA的变化:在数据库打开的状态下,如果使用了LogMinerDDL_DICT_TRACKING选项,Oracle9iLogMiner将自动对比最初的日志流和当前系统的数据字典,并返回正确的DDL语句,并且会自动侦察并标记当前数据字典和最初日志流之间的差别,这样即使最初日志流中所涉及的表已经被更改或者根本已经不存在,LogMiner同样会返回正确的DDL语句。  5、在日志中记录更多列信息的能力:例如对于UPDATE操作不仅会记录被更新行的情况,还可以捕捉更多前影信息。   、在日志中记录更多列信息的能力:例如对于操作不仅会记录被更新行的情况,还可以捕捉更多前影信息。  6、支持基于数值的查询:Oracle9i LogMiner在支持原有基于元数据(操作、对象等)查询的基础上,开始支持基于实际涉及到的数据的查询。例如涉及一个工资表,现在我们可以很容易地查出员工工资由1000变成2000的原始更新语句,而在之前我们只能选出所有的更新语句。  三、Oracle8i/9i的日志分析过程  三、的日志分析过程  LogMiner只要在实例起来的情况下都可以运行,LogMiner使用一个字典文件来实现Oracle内部对象名称的转换,如果没有这个字典文件,则直接显示内部对象编号,例如我们执行下面的语句:   delete from “C”.”A” where “C1″ = ‘gototop’ and ROWID = ‘AAABg1AAFAAABQaAAH’;
  如果没有字典文件,LogMiner分析出来的结果将是:
  delete from “UNKNOWN”.”OBJ# 6197″ where “COL 1″ = HEXTORAW(’d6a7d4ae’) and ROWID
  = ‘AAABg1AAFAAABQaAAH’; 如果想要使用字典文件,数据库至少应该出于MOUNT状态。然后执行dbms_logmnr_d.build过程将数据字典信息提取到一个外部文件中。下面是具体分析步骤:   1、确认设置了初始化参数:UTL_FILE_DIR,并确认Oracle对改目录拥有读写权限,然后启动实例。示例中UTL_FILE_DIR参数如下:  SQL> show parameter utl
  NAME TYPE VALUE
  ———————— ———– ——————————
  utl_file_dir string /data6/cyx/logmnr
              这个目录主要用于存放dbms_logmnr_d.build过程所产生的字典信息文件,如果不用这个,则可以不设,也就跳过下面一步。   2、生成字典信息文件:  exec dbms_logmnr_d.build(dictionary_filename =>’
  dic.ora’,dictionary_location => ‘/data6/cyx/logmnr’);
           其中dictionary_location指的是字典信息文件的存放位置,它必须完全匹配UTL_FILE_DIR的值,例如:假设UTL_FILE_DIR=/data6/cyx/logmnr/,则上面这条语句会出错,只因为UTL_FILE_DIR后面多了一个“/”,而在很多其它地方对这一“/”是不敏感的。  dictionary_filename指的是放于字典信息文件的名字,可以任意取。当然我们也可以不明确写出这两个选项,即写成:  指的是放于字典信息文件的名字,可以任意取。当然我们也可以不明确写出这两个选项,即写成:  exec dbms_logmnr_d.build(’dic.ora’,'/data6/cyx/logmnr’);
  
  如果你第一步的参数没有设,而直接开始这一步,Oracle会报下面的错误:  ERROR at line 1:
  ORA-01308: initialization parameter utl_file_dir is not set
  ORA-06512: at “SYS.DBMS_LOGMNR_D”, line 923
  ORA-06512: at “SYS.DBMS_LOGMNR_D”, line 1938
  ORA-06512: at line 1
                需要注意的是,在oracle817 for Windows版中会出现以下错误:  14:26:05 SQL> execute dbms_logmnr_d.build(’oradict.ora’,'c:\oracle\admin\ora\log’);
  BEGIN dbms_logmnr_d.build(’oradict.ora’,'c:\oracle\admin\ora\log’); END;
  *
  ERROR at line 1:
  ORA-06532: Subscript outside of limit
  ORA-06512: at “SYS.DBMS_LOGMNR_D”, line 793
  ORA-06512: at line 1
                    解决办法:   编辑“$ORACLE_HOME/rdbms/admindbmslmd.sql”文件,把其中的
  TYPE col_desc_array IS VARRAY(513) OF col_description;
  改成:
  TYPE col_desc_array IS VARRAY(700) OF col_description; 

              需要注意的是,在版中会出现以下错误: 保存文件,然后执行一遍这个脚本:  15:09:06 SQL> @c:\oracle\ora81\rdbms\admin\dbmslmd.sql
  Package created.
  Package body created.
  No errors.
  Grant succeeded.
                然后重新编译DBMS_LOGMNR_D包:  15:09:51 SQL> alter package DBMS_LOGMNR_D compile body;
  Package body altered.
  之后重新执行dbms_logmnr_d.build即可:
  15:10:06 SQL> execute dbms_logmnr_d.build(’oradict.ora’,'c:\oracle\admin\ora\log’);
  PL/SQL procedure successfully completed.
        之后重新执行即可:        3、添加需要分析的日志文件  SQL>exec dbms_logmnr.add_logfile( logfilename=>’
  /data6/cyx/rac1arch/arch_1_197.arc’, options=>dbms_logmnr.new);
  PL/SQL procedure successfully completed.
  

              然后重新编译包:           这里的options选项有三个参数可以用:  NEW – 表示创建一个新的日志文件列表  表示创建一个新的日志文件列表  ADDFILE – 表示向这个列表中添加日志文件,如下面的例子   REMOVEFILE – addfile相反。  SQL> exec dbms_logmnr.add_logfile( logfilename=>’
  /data6/cyx/rac1arch/arch_2_86.arc’, options=>dbms_logmnr.addfile);
  PL/SQL procedure successfully completed.
            4、当你添加了需要分析的日志文件后,我们就可以让LogMiner开始分析了:  SQL> exec dbms_logmnr.start_logmnr(dictfilename=>’/data6/cyx/logmnr/dic.ora’);
  PL/SQL procedure successfully completed.
          如果你没有使用字典信息文件(此时我们只需要启动实例就可以了),那么就不需要跟dictfilename参数:  SQL> exec dbms_logmnr.start_logmnr();
  PL/SQL procedure successfully completed. 

          、当你添加了需要分析的日志文件后,我们就可以让开始分析了:       当然dbms_logmnr.start_logmnr()过程还有其它几个用于定义分析日志时间/SCN窗口的参数,它们分别是:  STARTSCN / ENDSCN – 定义分析的起始/结束SCN号,  定义分析的起始结束号,  STARTTIME / ENDTIME – 定义分析的起始/结束时间。   例如下面的过程将只分析从 ‘2003-09-21 09:39:00′‘2003-09-21 09:45:00′这段时间的日志:  SQL> exec dbms_logmnr.start_logmnr(dictfilename=>’/data6/cyx/logmnr/dic.ora’ , -
  starttime => ‘2003-09-21 09:39:00′,endtime => ‘2003-09-21 09:45:00′);
  PL/SQL procedure successfully completed.
            上面过程第一行结尾的“-”表示转行,如果你在同一行,则不需要。我们可以看到有效日志的时间戳:  SQL> select distinct timestamp from v$logmnr_contents;
  TIMESTAMP
  ——————-
  2003-09-21 09:40:02
  2003-09-21 09:42:39
                这里需要注意的是,因为我之前已经设置NLS_DATE_FORMAT环境变量,所以上面的日期可以直接按这个格式写就行了,如果你没有设,则需要使用to_date函数来转换一下。  SQL> !env grep NLS
  NLS_LANG=american_america.zhs16cgb231280
  NLS_DATE_FORMAT=YYYY-MM-DD HH24:MI:SS
  ORA_NLS33=/oracle/oracle9/app/oracle/product/9.2.0/ocommon/nls/admin/data
  使用to_date的格式如下:
  exec dbms_logmnr.start_logmnr(dictfilename=>’/data6/cyx/logmnr/dic.ora’,-
  starttime => to_date(’2003-09-21 09:39:00′,’YYYY-MM-DD HH24:MI:SS’),-
  endtime => to_date(’2003-09-21 09:45:00′,’YYYY-MM-DD HH24:MI:SS’));
  

          上面过程第一行结尾的表示转行,如果你在同一行,则不需要。我们可以看到有效日志的时间戳:            使用的格式如下:          STARTSCN ENDSCN参数使用方法类似。  5、好了,在上面的过程执行结束之后,我们就可以通过访问与LogMiner相关的几个视图来提取我们需要的信息了。其中在v$logmnr_logs中可以看到我们当前分析的日志列表,如果数据库有两个实例(即OPS/RAC),在v$logmnr_logs中会有两个不同的THREAD_ID  、好了,在上面的过程执行结束之后,我们就可以通过访问与相关的几个视图来提取我们需要的信息了。其中在中可以看到我们当前分析的日志列表,如果数据库有两个实例(即),在中会有两个不同的。  而真正的分析结果是放在v$logmnr_contents中,这里面有很多信息,我们可以根据需要追踪我们感兴趣的信息。后面我将单独列出来讲常见的追踪情形。  6、全部结束之后,我们可以执行dbms_logmnr.end_logmnr过程退出LogMiner分析过程,你也可以直接退出SQL*PLUS,它会自动终止。   、全部结束之后,我们可以执行过程退出分析过程,你也可以直接退出,它会自动终止。   四、如何利用LogMiner分析Oracle8的日志文件  虽然说LogMinerOracle8i才推出来,但我们同样可以用它来分析Oracle8的日志文件,只不过稍微麻烦了一点,并且有一定的限制,下面是具体做法:   虽然说是才推出来,但我们同样可以用它来分析的日志文件,只不过稍微麻烦了一点,并且有一定的限制,下面是具体做法:  我们首先复制Oracle8i$ORACLE_HOME/rdbms/admin/dbmslmd.sql脚本到Oracle8数据库所在主机的同样目录;这个脚本用于创建dbms_logmnr_d包(注意,Oracle9i中还将创建dbms_logmnr包),如果是8.1.5脚本名字为dbmslogmnrd.sql。然后在Oracle8的数据库上运行这个脚本,之后使用dbms_logmnr_d.build过程创建字典信息文件。现在我们就可以把Oracle8的归档日志连同这个字典信息文件复制到Oracle8i数据库所在的主机上,之后在Oracle8i数据库中从上面分析过程的第三步开始分析Oracle8的日志,不过  dbms_logmnr.start_logmnr()中使用的是Oracle8的字典信息文件。  中使用的是的字典信息文件。  按照我前面所说的那样,如果不是字典文件,我们则可以直接将Oracle8的归档日志复制到Oracle8i数据库所在主机,然后对它进行分析。  其实这里涉及到了一个跨平台使用LogMiner的问题,笔者做过试验,也可以在Oracle9i中来分析Oracle8i的日志。但这些都是有所限制的,主要表现在:   其实这里涉及到了一个跨平台使用的问题,笔者做过试验,也可以在中来分析的日志。但这些都是有所限制的,主要表现在:  1LogMiner所使用的字典文件必须和所分析的日志文件是同一个数据库所产生的,并且该数据库的字符集应和执行LogMiner数据库的相同。这很好理解,如果不是同一个数据库所产生就不存在对应关系了。   2、生成日志的数据库硬件平台和执行LogMiner数据库的硬件平台要求一致,操作系统版本可以不一致。笔者做试验时(如果读者有兴趣可以到我网站http://www.ncn.cn上下载试验全过程,因为太长就不放在这里了),所用的两个数据库操作系统都是Tru64 UNIX,但一个是 V5.1A,另一个则是V4.0F。如果操作系统不一致则会出现下面的错误:  ORA-01284: file /data6/cyx/logmnr/arch_1_163570.arc cannot be opened
  ORA-00308: cannot open archived log ‘/data6/cyx/logmnr/arch_1_163570.arc’
  ORA-27048: skgfifi: file header information is invalid
  ORA-06512: at “SYS.DBMS_LOGMNR”, line 63
  ORA-06512: at line 1
                五、分析v$logmnr_contents  前面我们已经知道了LogMiner的分析结果是放在v$logmnr_contents中,这里面有很多信息,我们可以根据需要追踪我们感兴趣的信息。那么我们通常感兴趣的有哪些呢?  前面我们已经知道了的分析结果是放在中,这里面有很多信息,我们可以根据需要追踪我们感兴趣的信息。那么我们通常感兴趣的有哪些呢?  1、追踪数据库结构变化情况,即DDL操作,如前所述,这个只有Oracle9i才支持:  SQL> select timestamp,sql_redo from v$logmnr_contents2
  where upper(sql_redo) like ‘%CREATE%’;
  TIMESTAMP
  ——————-
  SQL_REDO
  ————————-
  2003-09-21 10:01:55
  create table t (c1 number);                   2、追踪用户误操作或恶意操作:   例如我们现实中有这样需求,有一次我们发现一位员工通过程序修改了业务数据库信息,把部分电话的收费类型改成免费了,现在就要求我们从数据库中查出到底是谁干的这件事?怎么查?LogMiner提供了我们分析日志文件的手段,其中v$logmnr_contentsSESSION_INFO列包含了下面的信息:  login_username=NEW_97
  client_info= OS_username=oracle8 Machine_name=phoenix1
  OS_terminal=ttyp3 OS_process_id=8004 OS_program name=sqlplus@phoenix1
  (TNS V1-V3)
  

  前面我们已经知道了的分析结果是放在中,这里面有很多信息,我们可以根据需要追踪我们感兴趣的信息。那么我们通常感兴趣的有哪些呢?  、追踪数据库结构变化情况,即操作,如前所述,这个只有才支持:            虽然其中信息已经很多了,但在我们的业务数据库中,程序是通过相同的login_username登录数据库的,这样单从上面的信息是很难判断的。   不过我们注意到,因为公司应用服务器不是每个人都有权限在上面写程序的,一般恶意程序都是直接通过他自己的PC连到数据库的,这就需要一个准确的定位。IP追踪是我们首先想到的,并且也满足我们的实际要求,因为公司内部IP地址分配是统一管理的,能追踪到IP地址我们就可以准确定位了。但从面的SESSION_INFO中我们并不能直接看到IP,不过我们还是有办法的,因为这个SESSION_INFO里面的内容其实是日志从V$SESSION视图里提取的,我们可以在生产数据库中创建一个追踪客户端IP地址的触发器:  create or replace trigger on_logon_trigger
  after logon on database
  begin
  dbms_application_info.set_client_info(sys_context(’userenv’, ‘ip_address’));
  end;
  /
                  现在,我们就可以在V$SESSION视图的CLIENT_INFO列中看到新登录的客户端IP地址了。那么上面的提出的问题就可以迎刃而解了。假如被更新的表名为HMLX,我们就可以通过下面的SQL来找到所需信息:  SQL > select session_info ,sql_redo from v$logmnr_contents
  2 where upper(operation) = ‘UPDATE’ and upper(sql_redo) like ‘%HMLX%’
  3 /
  SESSION_INFO
  —————————————–
  SQL_REDO
  —————————————–
  login_username=C client_info=10.16.98.26 OS_username=sz-xjs-chengyx Machine_name
  =GDTEL\SZ-XJS-CHENGYX
  update “C”.”HMLX” set “NAME” = ‘free’ where “NAME” = ‘ncn.cn’ and ROWID = ‘AAABhTAA
  FAAABRaAAE’;                         

                        

            虽然其中信息已经很多了,但在我们的业务数据库中,程序是通过相同的登录数据库的,这样单从上面的信息是很难判断的。

  、全部结束之后,我们可以执行过程退出分析过程,你也可以直接退出,它会自动终止。 

如果想要使用字典文件,数据库至少应该出于状态。然后执行过程将数据字典信息提取到一个外部文件中。下面是具体分析步骤:

  、支持更多数据存储类型:链接迁移行、表操作、插入以及操作。在的中可以看到操作的原句(除外,其中的密码将以加密的形式出现,而不是原始密码)。如果初始化参数设为,则所有操作的数据库账号将被记录。  、提取和使用数据字典的选项:现在数据字典不仅可以提取到一个外部文件中,还可以直接提取到重做日志流中,它在日志流中提供了操作当时的数据字典快照,这样就可以实现离线分析。

  用来终止分析会话,它将回收所占用的内存。  与相关的数据字典。

  、执行变化分析。  不仅如此,日志中记录的信息还包括:数据库的更改历史、更改类型(、、、等)、更改对应的号、以及执行这些操作的用户信息等,在分析日志时,将重构等价的语句和语句(分别记录在视图的和中)。这里需要注意的是等价语句,而并非原始语句,例如:我们最初执行的是,而重构的是等价的条语句。所以我们应该意识到视图中显示的并非是原版的现实,从数据库角度来讲这是很容易理解的,它记录的是元操作,因为同样是语句,在不同的环境中,实际删除的记录数可能各不相同,因此记录这样的语句实际上并没有什么实际意义,重构的是在实际情况下转化成元操作的多个单条语句。

职业生涯22 Jul 2006 11:00 pm

介绍  许多年来,我在不同的新闻组上花费了很多时间与那些想知道如何得到数据库管理员(DBA)的工作或者如何成长为一名DBA的IT人进行交谈,现在他们有了工作。这些年来许多人针对如何达到这个目标提出了不同意见。本文即是那些意见的综合并且能够在如何才能出色的完成DBA的工作方面给出好的建议。这篇文章同样对于如何让一名DBA变得对老板更有价值。假如你已经是一名DBA,那么也许你会希望跳过文章的前几段。

  我应该成为一名DBA么?

  我曾问过的一个问题是一个人应该从事DBA这样的职业么。这个问题并不容易回答,因为它因人而异。有的人有成为一名好DBA所需要的天赋。而其他人并不认为能够很容易掌握成为DBA的秘诀。DBA职业需要掌握一定的技能。而且还需要其他IT职业所不必需的要求。因此,为了回答这个问题,我通常给将要成为DBA的人描述DBA职业所必需的要求。下面的段落中,我都将以问题结尾。在继续下一段以前请花一些时间考虑并且回答这些问题。

  许多人因为许多原因而立志要成为DBA。其中一个主要原因是薪水。DBA是IT业中薪水最高的职业之一。其他人想成为DBA是因为喜欢赞扬DBA是受到的荣誉,或者因为他看上去很酷!我个人认为,成为DBA是很值得的。它是一个很有意思,令人兴奋的职业。那么,你把DBA作为一个可能的职业选择的原因是什么呢?

  除非你已经提前准备好了,否则你可能会发现从事DBA职业充满了挫折和令人头痛。一个数据库存在于操作系统和最终用户应用程序之间。同样地,DBA必须非常精通他的数据库所在的操作系统。DBA并不一定需要知道所有有关操作系统的知识,但是他知道得越多越好。数据库与操作系统联系非常紧密。理解这种关联是十分必要的。DBA还需要知道服务器硬件以及它如何影响与帮助数据库。同时,DBA必须理解应用软件。DBA可能会被要求帮助开发人员创建可靠,健全的数据库应用程序。还有,最重要的也是最明显的,DBA必须十分彻底的理解数据库引擎,它是如何工作的,所有的引擎是如何组合在一起的,以及如何影响数据库引擎向最终用户和应用程序传送数据的能力。我见过的最好的DBA有非常深刻的理解而且不仅仅在数据库本身。他们知道一些系统管理与应用开发。好多时候他们在成为DBA之前有其中一个或两个背景。无论如何,成为一名合格的而不是出色的DBA也需要许多背景知识。你是否已经做好准备开始学习直到你感到已经无法再学下去了?

  许多和我交谈过的,在开始DBA职业时遇到困难的人,在尝试着吸收大量DBA所需了解的信息时实际上都会有一些问题。毕竟系统管理员是全职学习操作系统的细节。应用程序开发人员全职学习如何编写好的程序。DBA不仅要非常了解这两种不同的工作,而且还需要花费更多的时间去理解数据库的体系结构,以及理解每一样东西的每一块是如何组合在一起的!听起来是不是很让人畏缩?有许多人失败后这样想,并且把DBA工作看作一项非常困难的事情。也有那些传播和理解所有这些信息,并且使用这些信息做出好的,听起来具有技术性的决定的人。正如我以前是一名DBA时喜欢说的,所有这些对我来说看上去像一个大谜团。把这些所有的很好的组合在一起就是挑战。你是哪一种人?

  许多DBA是随时侯命的。他们会在白天或晚上的所有时间接到呼叫去解决他们的数据库出现的致命问题。数据库是商业信息技术基础组织的必不可少的组成部分。没有数据,就没有必要拥有一个计算机系统。数据推动商业。假如amazon.com的网站不能在数据库中搜索产品并且假如没有人能够为他们的产品下订单,那么它会变成什么样?它就不会在商业中存在很久。当数据库down掉,即使只是很短的时间,公司也会损失重大。基于这个原因,DBA到达现场后必须尽可能迅速的解决问题。许多公司有自己的DBA团队以便可以轮流待命。这些DBA 24×7小时的维持数据库应用程序。假如工作需要的话,你准备好随时候命了么?

  一些DBA的职责包括为软件打补丁或者对数据库做些改变。通常,这些操作不能在公司职员工作的时候做,因为此时数据库必须运行以便他们能够工作。这意味着DBA经常不得不在很早或者深夜甚至周末,总之,在正常工作时间以外来完成工作。你准备好在非正常时间工作,或者你在找一个朝九晚五的工作?

  对DBA而言,需要掌握的一个重要内容就是通常被称为“软技术”的东西。DBA需要在团队中很好的工作,通常团队是在变化的,如系统管理员,网络管理员,应用程序开发人员,项目经理和其他人。DBA要能够用流利的英语解释复杂的技术概念,让团队中其他人明白。DBA还要能够在数据库相关问题上指挥团队队员。你的软技术怎么样?

  下面不是全部列表,但是包括了DBA的典型职责:

  · 把监视数据库实例当作每日必做工作以保证其可用性。解决不可用的问题。

  · 收集系统统计和性能信息以便定向和配置分析。

  · 配置和调整数据库实例以便在应用程序特定要求下达到最佳性能。

  · 分析和管理数据库安全性。控制和监视用户对数据库的访问。必要时审计数据库的使用。

  · 监视备份程序。必要时提供恢复。测试备份与恢复程序。

  · 升级RDBMS软件并且在必要时使用补丁。必要时升级或者迁移数据库实例。

  · 通过数据库相关动作来支持应用程序开发人员。

  · 跟随数据库趋向和技术。当可应用时使用新技术。安装,测试和评估Oracle新的相关产品。

  · 执行存储和物理设计。均衡设计问题以完成性能优化。

  · 创建,配置和设计信的数据库实例。

  · 诊断,故障检测和解决任何数据库相关问题。必要时联系Oracle支持人员以便使问题得到较好的解决。

  · 确保Oracle网络软件(SQL*Net, Net8, Names, OiD)配置和运行的很好。

  · 与系统管理员(Unix &NT)一起工作以保证Oracle相关事务得到很好的处理。

  · 为有效的,定期的维护数据库创建任何必要的脚本。

  前面各段的问题是为了使你考虑一名DBA该做些什么,帮助你决定这是不是适合你的职业。我并非意味着假如你的目标是成为DBA这些会阻止你。我只是尝试着展现一些事实。我看到过一些DBA一旦被实际工作打击了就一蹶不振。他们花费时间,精力和一些金钱获得了他们的第一份DBA工作。我个人认为这个职业非常有价值。而且我无法想像现在做任何其他的会怎样。所以,这一段帮助你决定这是不是你希望从事的。假如它是,那么尽你所有去得到它!

  我怎样得到第一份DBA工作?

  你已经阅读了前面的段落并且认为成为一名DBA是一个好的职业。祝贺你!我希望你的职业能变成你想像得那么令人兴奋和有意义。那么,你如何找到第一份DBA工作?这个问题我已经听别人问了许多许多遍。

  在90年代早期,因特网急速发展。它使公司象草一般萌芽。公司蜂拥而至并且开始创建他们在网上的形象。几乎所有这些有网站的公司都需要一个数据库作为web应用的后台。不幸的是,当时在该领域却没有那么多DBA。在IT业,DBA变得奇缺。那段时间里,得到一份DBA工作看上去只要可以拼出“Oracle”或者可能只是在大学里接触过一学期的数据库就行。为了使生活变得更好,DBA的匮乏促使公司付给有潜力的职员很高的薪水。假如你想要成为一名DBA,很容易,非常容易。你需要做的就是证明你了解什么是数据库然后工作就会比你预期的更早的出现在你面前。

  然后因特网的泡沫破灭了。大量投产因特网的公司破产。许多给公司工作的DBA重新寻找工作。缺少DBA的公司找到一名有DBA经验的人比以前容易得多。在21世纪初期,由于经济并不十分稳固,因此生活并不容易(至少在美国如此)。公司都勒紧了他们的裤腰带。所有这些转化为更少的工作机会和DBA候选人更少的工作空缺。

  得到第一份DBA工作的最艰难的部分是每一个职位都要求有一些工作经验。如果你从公司的角度出发,你就可以理解为什么对DBA职位而言经验是必需的。假如他没有一点经验,你会付给这个人很高的工资让他去操作,维护和运行你IT基础组织的最大最重要的一部分么?你的公司会付钱给一名没有经验的DBA么?并且,在等待他成长起来的过程中可能会损失上百万美元的收入。对大多数公司而言,这些问题的答案肯定是‘不’。所以,没有经验,获得你的第一份DBA工作是很困难的。

  第一份DBA工作现在成了恶性循环的境遇。假如我没有任何经验,我怎样才能得到DBA的工作呢?我没有工作的话又怎么能得到DBA经验呢?这是要战胜的最困难的障碍。最困难的部分是获得第一个DBA工作。这部分的剩下部分将针对实现你第一个DBA工作的目标给你一些建议。

  提示#1:接受教育。–尽可能多的学习有关数据库的知识。这很可能将占用你正常工作以外的部分时间也精力。参加本地大学举办的数据库培训班。许多培训公司都会举办数据库管理员的培训班。假如你的老板不资助你的学习,那么你可能不得不自己支付这笔费用。许多DBA职业要求至少为计算机科学或相关专业本科以上学历,因此你必须至少有那样的文凭。

  提示#2:锻炼成为DBA。–许多数据库供应商都允许你下载他们数据库系统的测试版或评估版。下载一份并且在自己的个人电脑上安装软件。练习使用数据库。故意破坏数据库并且尝试修复它。尝试着履行你所能想到的尽可能多的DBA职责。测试和磨练你在自己的测试平台上的技能这样你就可以证明你的数据库管理能力。

  提示#3:获得认证。–许多数据库提供商都提供自己的数据库产品的认证。许多公司现在都把认证看作是一种标准。需要记住的一件事是仅获得认证是不够的。通过DBA认证测试并不意味着你知道如何管理一个数据库。它只是告诉你以后可能的老板,现在你拥有了一定的技术。它还告诉你的老板你对DBA工作的态度是很认真的。我看到许多人抱怨他们已经得到了认证但是没有经验,却仍然不能得到第一份DBA工作。认证本身并不能使你得到工作,但它是无害的。即使没有其他的,在你进行认证的时候你也已经学到了许多知识。只是不要依赖认证来给你带来你要找的工作。你需要的比这还要多。但它会在最后帮助你。

  提示#4:利用你现有的技能。–许多DBA具有系统管理员背景。其他的有应用程序开发背景。假如可能,查看你能否利用现有的技能来得到工作。现在的目标就是为你和你的老板创造一个双赢的局面。例如,让我们假设你已经是一名系统管理员而想进入DBA领域。也许你会找到一份工作,这份工作一部分时间里可以用到你的系统管理技能,并且在剩下的时间里可以使你涉及到数据库管理领域。假如你已经是一名某个产品平台上的DBA但你希望转到其他产品平台,那么看看你能否找到一份同时接触两个产品平台的工作。这样,公司和你都得到了想要的。在你定向到了DBA工作后,你可以试着得到一个能让你全职作它的职位,也许还可以在同一个公司中。

  提示#5:利用现在的机会。–有时候,一个人进入DBA领域仅仅需要的是正确的地方和正确的时机。假如你现在的老板有一个机会让你进行任何数据库的项目,抓住这个机会!任何数据库经验就比没有数据库经验要好。让你的管理者知道你十分积极的在寻找任何可能的数据库机会。他们就有可能在下次机会到来的时候想到你。进行这些数据库项目以及看到你要成为一个DBA的渴望以后,他们可能会决定培训你,提拔你。许多许多人都是以这种方式获得他的第一个DBA工作,在进行了一些数据库相关的项目后不知不觉的成为一名较低级的DBA。通常当一名DBA离开公司后,公司将在内部寻找一个候选人,假如他们认为这名候选人是可训练的话。

  提示#6:寻找较低级的DBA职位。–假如你只是为了一个较低级的DBA工作,看到DBA职位的需求描述说他们正在寻找高级DBA或者其他的。所以,让我们严谨一些。你并没有一个高级方面的经验。我已经讨论过了对于这样的职位为什么公司不会考虑你。但是他们会在低级的职位上考虑你。低级的DBA在高级DBA的指导下完成工作。他们摸索窍门。一般来说,高级DBA对数据库承担责任,同时也获得所有的荣誉。但是不要焦急。随着你的事业发展,你将会有越来越多的责任和得到越来越多的信任。因为你没有任何经验,你应该从这里开始启航。

  我也听到过一些公司寻找一名高级DBA,但是到最后,他们实际想要雇一名低级的DBA。你或许希望申请这样的职位虽然你也许没有资格。他们可能还是会决定雇佣你。但是提前说明你仍然在摸索阶段并且已经是较低级的DBA水平。不要试图欺骗他们让他们认为你是高级DBA的水平。这只会降低你得到这项工作的机会。

  这些提示将帮助你得到第一份DBA的工作。祝你在寻找工作时有好运气。当你已经找到了第一份DBA工作后,继续下面的部分来学习如何往下走下去。

  我刚得到我的第一份工作!现在该怎样?

  祝贺你!你现在是DBA俱乐部的正式成员了!对于这份梦寐以求的令人激动的职业,你准备好了么?你的工作才刚开始并且你在学习上已经落在后面。你将会发现要成为一名高效的数据库管理员有大量的知识你必须掌握。你的第一年或前两年将花费比以前更多的时间来学习。假如你发现学习知识的数量使你大脑超负荷,那么休息一下,歇口气,然后再回到学习中去。为了帮助你继续走下去,你可以按照下面的方法进行:

  步骤#1: 关系型数据库理论 –这部分我假设你将管理的数据库是一个“关系型”数据库。其他数据库模型也存在,但是关系型模式是近二十年工业上占统治地位的一种数据库模式。假如你的数据库系统是其他的模式,那么学习它的理论。相关数据库理论是十分重要的。它是其他一切的基础。我也看到许多跳到数据库管理职位的人从不想去学习纯粹的关系型数据库理论。不可避免的,在他们的事业中对理论基础的匮乏作为缺点暴露了出来。假如你对关系型数据库理论理解得很好,那么你将非常容易的在任何平台的关系型数据库管理系统(RDBMS)中转变。我使用Oracle数据库,或者IBM的DB2,或者微软的SQL Server是无关紧要的。他们都是关系型数据库系统。他们在最底层都在做着相同的事情。区别在于他们怎样去做相同的事情。纯粹的关系型数据库理论对于较低级的DBA来说并非必需的。但是假如你想要超越低级DBA的水平它就是十分重要的。许多大学的教科书都很好的包含了关系型数据库的理论。其中一本被广泛使用的教科书就是由Elmasri and Navathe编写的数据库系统基础,Bejamin/Cummings Press。

  步骤#2: 彻底的学习查询语言 –数据库都有语言让你能够从数据库中得到数据,把数据放到数据库中,以及修改数据库中的数据。对于关系型数据库而言,这种语言就是结构化查询语言(SQL)。这门语言是你与数据库接触的工具。不能让这个工具成为以后学习的障碍,这一点很重要。在你的测试数据库中练习不同的SQL语句直到他们变成了你的习惯。这方面的一本非常好的书叫做Oracle 9i完全参考(Oracle 9i The Complete Reference)由Loney 和Koch编写,Oracle Press。每一名Oracle DBA都应该在他事业的早期阅读这本书。Oracle 9i参考手册(Oracle 9i SQL Reference manual)是另一个很重要的知识来源。在他们的技术网站TechNet上(http://technet.oracle.com)你可以访…t上有一个账号。

  步骤#3: 开始学习基本的数据库管理工作 –这难道不是你最开始在这里的原因?为什么它在列表的第三位?我们尝试着建造一个知识的金字塔,我强烈的感觉到一个人需要知道关系型数据库理论和SQL,并且在你学习如何进行基本的数据库管理工作时把他们当作工具来使用。这些工作包括启动和关闭数据库,备份和恢复数据库,以及创建/删除/修改数据库对象。对于Oracle数据库管理而言,在市面上有大量的书籍可以给你所期望的一个很好的体会。这本书是Oracle 9i DBA手册(Oracle 9i DBA Handbook by Loney on Oracle Press)。我知道的大多数DBA都在他们事业的早期不只一遍的阅读过这本书。这里,你应该同时阅读和理解Oracle 9i 概念指导,Oracle 9i管理员指导,以及Oracle 9i备份与恢复指导(Oracle 9i Concepts Guide, the Oracle 9i Administrator’s Guide, and the Oracle 9i Backup and Recovery Guide)都来自Oracle文档。

  步骤#4: 阅读,阅读,再阅读 –由于你才刚开始你的DBA职业生涯,因此你正在开始为你的技能奠定基础。这需要一段很长的时间去形成,吸收和领会所有你将学到的知识。毫无疑问的,比你资深的DBA由许多工作要做,因此他们可能不会总是腾出大量时间辅导你的学习。你不得不靠自己学习很多东西。这就是阅读的目的。市面上有许多书籍可以解答许多数据库相关的话题。Oracle Press是Oracle公司的官方出版社,有大量的Oracle相关书籍。同时也有其他的出版社,如Wrox Press 和 O’Reilly Press。你也可以找到Oracle文档来阅读。并且还有许多网站和新闻组。尽可能多的读书使你能够继续下去。还有,不只一遍的阅读它们可以使你吸收你第一次阅读时错过的内容。

  步骤#5: 创建测试案例 –我经常看到初学者问一些很基础的问题,其实假如他们花一些时间来考虑,这些问题都是很容易解答的。毫无疑问的,在你开始学习Oracle的时候你会有许多的问题。看看这些问题你能不能自己回答出来。例如,我又一次被问到能不能向有唯一性约束的列中插入空值。最开始,这看上去也许不是很容易回答的问题。但它却是非常容易去试验的!只需要创建一个简单的表。在其中的一列,假如唯一性约束。尝试着在该列插入一个空值。有效么?你应该能够非常容易的回答出这个问题了。那么,为什么要创建这些案例呢?一个原因是这样做可以提高你解决问题的能力。创建这些案例需要的技能就是解决问题用到的技能。解决问题的技能将会对你的DBA事业有很大的帮助。另一个原因是随着你的事业的发展,你将经常需要创建更复杂的测试案例以便保证数据库和应用程序的成功。在将来,甚至简单的测试案例也可以组成更复杂的数据库和应用程序分解。

  步骤#6: 找一个良师 –一个良师能够为你的DBA生涯(或者其它类似的职业)引领方向。他们能够给你指示,回答问题以及在你的DBA的成长过程中帮助你节约一些时间。但愿这篇文章能够在你事业发展的一段时间内起到良师益友的作用。假如你与一名资深的DBA共同工作,那么那个人应该有责任为你的事业进行有益的指导。你也可以同时选择其他的人指导你。

  步骤#7: 参加本地用户群 –许多跨国家的城市有本地用户群,他们定期聚会讨论数据库相关的话题。假如可能,参加其中一个本地用户群。这将给你一个与他人相互交流的很好的方法。

  我如何能够从一名DBA初学者变为一个具有中级水平的DBA?

  你已经成为DBA一段时间了,你现在希望你的技术水平提高一阶么?下一步该怎么做?首先,往回看前面的部分,确认你已经完成了所有的步骤。彻底理解SQL语言是十分重要的。理解关系型数据库理论和掌握基本的数据库管理任务也是非常重要的。到如今,你应该阅读文档和其他书籍到已经郁闷了。假如没有,那么你还没准备好继续深造,增长你的DBA的技术水平。假如你已经准备好继续了,我已为你的继续深造准备了一些方法。

  步骤#1: 学习操作系统和你的服务器硬件 – 正如我前面所说,数据库存在于操作系统和服务器硬件之上。理解这些组成部分如何工作是很必要的。你应该知道如何与特殊的操作系统相合。你如何删除或者编辑文件?假如你的操作系统是Unix,你应该掌握命令行以及Unix命令如何辅助你工作。对于运行在Windows或其他操作系统上而言也是一样的。你同时需要对服务器的硬件有一定的了解。物理内存和虚拟内存有什么区别?RAID是什么以及不同的级别是如何产生影响的?为什么数据库喜欢更多的物理硬盘而非一个大硬盘卷?你需要知道这些事情以便你能够容易的与系统管理员进行如何配置好你的服务器以便使它能够充分的支持数据库方面的交谈。

  步骤#2: 学习应用程序设计因为它与数据库相关 – 如前面所述,数据库存在于操作系统与数据库应用程序之间。你真的需要这两者。SQL语言是如何帮助创建好的应用程序的?绑定变量是什么并且为什么他们很重要?Tom Kyte 写了一本非常好的书,在Oracle应用程序设计上给出了很好的建议。他的Expert One-on-one Oracle书可在 Wrox Press找到。我强烈推荐阅读此书。他详细的叙述了那些能够生成和破坏Oracle应用程序的东西。你需要知道这些,因为你的应用程序开发人员希望从你这里得到指导和数据库知识。学习任何与应用程序设计有关的知识。也许参加一个关于软件工程,操作系统或数据结构的课程班会有好处。

  步骤#4: 取得认证 – 也许你的工作并不需要,但是取得认证一定对你有益。作为DBA的每一天里,你学到了许多新的和令人激动的事情。也许在你职业生涯的这段时间里,有几天你没学到任何新的东西。但你仍然有很多要学习。成为一名OCP(Oracle Certified Professional) DBA要求你必须已经学到了数据库管理所有方面的基础。我发现在OCP考试的学习过程中,我学到了在我工作中从未接触过的东西。一次我学到了我从未碰到过的一个特殊课题,在后来的日子里我就能够使用那个知识解决问题。假如我从为在OCP考试中学倒它,那么我永远也不会用那种特殊的方法去解决问题。这已经一次次的发生在我的面前。有的人可能会说认证实际上真的不值得。我要说它只会对你有益无害。所以,去取得认证吧!

  步骤#5: 获得一个资源库 – 在前面的部分中,我指出每个DBA都应该在Technet上有个账号。这是你其中一个主要资源。但是同时还有许多其他资源。很多人共享他们的Oracle知识。假如你还没有开始,你应该用网络浏览器去搜索并收集很多Oracle资源。愿意的话,你可以从访问我的网站(http://www.peasland.net)开始。下面是一些Oracle DBA必须了解得网站列表:

  Ask Tom – http://asktom.oracle.com

  Jonathan Lewis web site – http://www.jlcomp.demon.co.uk/

  Ixora (Steve Adams) – http://www.ixora.com.au

  Orapub – http://www.orapub.com

  Metalink (Oracle支持网站) – http://metalink.oracle.com

   国内的:

   ITPUB论坛 – http://www.itpub.net

   Oracle技术网 – http://www.oradb.net

  还有许多其它的好网站。

  步骤#6: 开始在不同的新闻组和论坛上交流 – 也许你已经发现了他们,但假如现在你还没有那么是时候去开始了。有许多的新闻组和论坛可以回答你的任何Oracle问题。在Oracle群落里还有许多高手愿意和你共享他们的知识。你所要做的就是提问。下面是一个列表包含了可以开始交流的最好的因特网团体:

  Usenet newsgroups – comp.databases.oracle.server 和 comp.databases.oracle.misc 是两个可以交流的非常著名的世界性的新闻组。他们拥有大量的针对Oracle问题的交流卷宗。观看这些组的最好的方法式使用新闻广播员。但是假如你想通过基于web的方式访问,也可以通过Google搜索引擎搜索它。 (http://groups.google.com/groups?hl=…atabases.oracle)

  Quest Pipelines – 当他们在最开始还属于软件提供商RevealNet的时候,被称为the RevealNet Pipelines。现在,Quest购买了RevealNet 并且拥有Pipelines 。因为Pipelines是中等的,所以这些是我最喜欢的。你可以在这里找到Pipelines (http://www.quest-pipelines.com/index.asp)。

  观察别人是如何经历考验和磨难的是一件好事。假如你有问题,可以自由的在群里提出来。假如你要提出问题,通常应该包括一些信息,比如你的Oracle版本和Oracle运行的平台。这些将会得到有很大的差别的答案。假如你忘记了,会有人提醒你!甚至你不用提问也可以从其他人的答案中学到许多知识。我已经记不得多少次我之所以能够解决问题完全是因为我记得其他人在新闻组里问过相同的问题。

  我如何从一名具有中级水平的DBA转变为一名高级DBA?

  好,作为DBA你已经坚持不懈的努力了很长时间。你感到自己已经准备好往前再走一步。成为一名高级DBA需要什么?下面的部分将帮助你走下去。

  步骤#1: 阅读所有的文档 – Oracle文档通常并不是很容易阅读的。无数次,你翻阅文档只是为了要弄清整件事情。假如文档是最好的东西,那么那些站在你的书架里的Oracle书籍就不会有市场。但是文档确实包含了一些无法在任何其他地方找到的信息。例如,你无法找到每一个专门的INIT.ORA参数或V$视图的详细说明。书本上也许会涉及一部分,但是Oracle文档却包含它们所有。我遇到过一个非常厉害的高级DBA,他没有从头至尾的阅读过Oracle文档。这不是偶然的。Oracle文档是必须阅读的。也许到现在为止你已经读过Oracle概念指导十二遍了。但是当Oracle 10i发布了,你还要再次阅读。任何有关10i的新概念将在文档里记录。假如你真的想更上一层,那么,去阅读那些文档。不要逃避它。

  步骤#2: 成为一名专家 – Oracle数据库是一个非常复杂的东西。为了更上一层,你必须精通产品的许多组成部分。以备份与恢复开始。成为一名备份与恢复的专家。故意的破坏数据库然后察看如何恢复它。尝试以任何可能的方式破坏数据库然后查看还有没有可能恢复。你将彻底的理解备份与恢复的概念。在你成为了备份与恢复的专家以后,再去成为其他领域的专家。你会有无穷无尽的题目要去掌握。在你整个职业生涯中都保持如此。但是请记住,无论你有多么专业,在某些领域,某些人会在某些方面知道的比你多。不要带个人情绪。只是尽可能多的从那个人那里学到知识。

  Route #3: 积极参加新闻组,论坛和用户组 – 在前面,我提过为什么不同的新闻组和论坛是学习新知识的很好的地方。现在轮到你进行下一步并且去回答任何你能够回答的问题。你将会惊讶于在这过程中你能学到那么多!

  Route #4: 写白皮书并且展示它们 – 这与前面提到的方法类似。首先,共享你拥有的知识是很重要的。假如你的职业生涯已经走到这一步,那么从某种意义上讲,是离不开他人的贡献的。所以,现在是你为他人奉献的时候了。第二,当你企图共享你的信息的时候会有令人惊异的事情发生。在你要用清楚的,简练的语言表述问题以便其他人能够使用时,那些信息在你的头脑里经过了一个令人瞠目的过程。这个过程使你巩固了知识,这是无法通过其它方法进行的。所以在白皮书中共享那些信息,讨论会,以及新闻组和论坛都是你学习和使你的能力更上一层的非常好的方法。顺着这条路,你应该作两件事。第一,认识到你将会犯错误。其他人将会很高兴的指出那些错误,有时在某种意义上那并不是很好。不要企图掩藏你的错误。承认它们并从它们那里学习。第二,学会说你不知道答案而不是企图以欺骗的方式通过。人们早晚会知道你在企图蒙蔽他们。简单的告诉他们现在你对答案并不肯定,但你会在查到答案后回来告诉他们。假如你时刻谨记这两件事,你就不会违背你的诚实而且你将成长为一名职业的IT人。

  步骤#5: 成为解决Oracle问题的专家– 高级DBA通常都是被看作是解决复杂的Oracle问题的人。你将会用到你所有的技术来解决许多问题,这些技术都是你的职业生涯中积累下来的。我前面提到的任何事都将会在解决问题的过程中用到;文档,书籍,新闻组,测试案例,和其它DBA都将辅助你解决问题。

  步骤#6: 成为性能调优的专家– 高级DBA通常都是被看作是调整数据库和应用程序性能的人。假如你是高级DBA但是你却不能分析性能瓶颈,那么你的公司将会到别处寻找这些服务。

  步骤#7: 成为承载能力计划的专家– 高级DBA通常都是被看作是根据数据增长量和交易增长来计划数据库承载能力的人。高级DBA需要在影响应用程序性能以前发现系统瓶颈。例如,DBA应该知道在数据库将可用的磁盘空间用完以前预置更多的磁盘空间。不密切关注承载能力计划将会导致生产数据库宕机。

  步骤#8: 密切关注新的技术– 高级DBA应该对IT界的关数据库技术的未来有好的建议。有什么可用的技术可以帮助数据库?例如,学习存储领域网的优缺点以及如何把它们应用到数据库系统。有什么技术在不远的将来可以用,哪些能够帮助我们?例如,写这篇文章的时候,linux操作系统正在变得越来越流行。Lunix会给你的数据库操作系统平台带来些什么?它能为你的公司工作么?

  结论

  从获得第一份工作,到从一名初级的DBA成长成一名高级DBA,我希望这篇文章在如何度过你的职业生涯的各个阶段方面给了你一些建议。无论你现在处在你的DBA职业生涯的哪一阶段都可以用到这篇文章。

读书笔记22 Jul 2006 10:59 pm

  Oracle专家高级编程学习笔记

  一.准备工作

  1.建立基本调试环境

  Create demo schema(建立演示模式)

  sqlplus>start $ORACLE_HOME/sqlplus/demo/demobld.sql
 
  2.建立login.sql

  ———————————
  define_editor=vi
  set serveroutput on size 1000000
  set trimspool on
  set long 500
  set linesize 100
  set pagesize 9999

  column plan_plus_exp format a80
  column global_name new_value gname

  select lower(user)||’@'||decode(global_name,’WEBDB.FANYAMIN.NET’,'webdb’,global_name) global_name from global_name;

  set sqlprompt ‘&gname>’
  set termout on
  ————————————————-

  3.在sqlplus中配置AutoTrace

  1)cd $ORACLE_HOME/rdbms/admin

  log into sqlplus as system(connect system/manager@webdb)
  run @utlxplan
  run create public synonym plan_table for plan_table;
  run grant all on plan_table to public;

  2)

  cd $ORACLE_HOME/sqlplus/admin
  log into sqlplus as sys(connect sys/change_on_install@webdb)
  run @plustrce
  run grant plustrace to public;

  二.内容

  1.开发成功的Oracle应用程序

  一条简单秘诀

  if(possible) 就用单条SQL语句完成
  else if(possible) 用plsql程序
  else if(possible) 用java存储过程
  else if(possible) 用C外部过程
  else 考虑是否真有必要这样做

  两个指导方针

  1)不要在MTS下运行长事务  system@webdb>alter system  flush shared_pool;
  system@webdb>set timing on

  执行这两个sql文件,一比较,发现使用绑定变量后,速度快多了

  –bindtest1.sql, did not use bind variable
  —————————————————————————–
  declare
   type rc is ref cursor;
   l_rc rc;
   l_dummy all_objects.object_name%type;
   l_start number default dbms_utility.get_time;
  begin
   for i in 1..1000
   loop
    open l_rc for
    ’select object_name from all_objects where object_id=’||i;
    fetch l_rc into l_dummy;
    close l_rc;
   end loop;
   dbms_output.put_line(round((dbms_utility.get_time-l_start)/100,2)||’seconds…’);
  end;
  /
  –bindtest2.sql,  use bind variable
  ———————————————————–
  declare
   type rc is ref cursor;
   l_rc rc;
   l_dummy all_objects.object_name%type;
   l_start number default dbms_utility.get_time;
  begin
   for i in 1..1000
   loop
    open l_rc for 
    ’select object_name from all_objects where object_id=:x’
    using i;
    fetch l_rc into l_dummy;
    close l_rc;
   end loop;
   dbms_output.put_line(round((dbms_utility.get_time-l_start)/100,2)||’seconds…’);
  end;
  /
  Oracle封锁策略

  1)只有当修改时,oracle在行级上锁定数据,不要把锁定上升到块级或表级
  2)Oracle决不会为读取而锁定数据,简单读取不能在数据行上设置锁定
  3)数据的写入器不会阻塞数据读取器.
  4)只有当另一个数据写入器已经锁定了某行数据后,才阻塞其他人对该行数据的写入.数据读取器决不会阻塞数据的写入器

  Oracle的这种”不阻塞”方法用一个副作用,如果想要一次只有一个用户访问某行数据,在多用户环境中,必须经常使用在多线程程序设计用到的相似技巧,

  select * from resources where resource-name=:room_name FOR UPDATE

  Oracle的并行控制机制(多版本一致读)
  一致性查询:在某一时刻查询产生一致结果
  非阻塞查询:数据写入器从来不阻塞查询

  四、如何利用LogMiner分析Oracle8的日志文件

  虽然说LogMiner是Oracle8i才推出来,但我们同样可以用它来分析Oracle8的日志文件,只不过稍微麻烦了一点,并且有一定的限制,下面是具体做法:

  我们首先复制Oracle8i的$ORACLE_HOME/rdbms/admin/dbmslmd.sql脚本到Oracle8数据库所在主机的同样目录;这个脚本用于创建dbms_logmnr_d包(注意,Oracle9i中还将创建dbms_logmnr包),如果是8.1.5脚本名字为dbmslogmnrd.sql。然后在Oracle8的数据库上运行这个脚本,之后使用dbms_logmnr_d.build过程创建字典信息文件。现在我们就可以把Oracle8的归档日志连同这个字典信息文件复制到Oracle8i数据库所在的主机上,之后在Oracle8i数据库中从上面分析过程的第三步开始分析Oracle8的日志,不过

  dbms_logmnr.start_logmnr()中使用的是Oracle8的字典信息文件。

  按照我前面所说的那样,如果不是字典文件,我们则可以直接将Oracle8的归档日志复制到Oracle8i数据库所在主机,然后对它进行分析。

  其实这里涉及到了一个跨平台使用LogMiner的问题,笔者做过试验,也可以在Oracle9i中来分析Oracle8i的日志。但这些都是有所限制的,主要表现在:

  1、LogMiner所使用的字典文件必须和所分析的日志文件是同一个数据库所产生的,并且该数据库的字符集应和执行LogMiner数据库的相同。这很好理解,如果不是同一个数据库所产生就不存在对应关系了。

  2、生成日志的数据库硬件平台和执行LogMiner数据库的硬件平台要求一致,操作系统版本可以不一致。笔者做试验时(如果读者有兴趣可以到我网站http://www.ncn.cn上下载试验全过程,因为太长就不放在这里了),所用的两个数据库操作系统都是Tru64 UNIX,但一个是 V5.1A,另一个则是V4.0F。如果操作系统不一致则会出现下面的错误:

  ORA-01284: file /data6/cyx/logmnr/arch_1_163570.arc cannot be opened
  ORA-00308: cannot open archived log ‘/data6/cyx/logmnr/arch_1_163570.arc’
  ORA-27048: skgfifi: file header information is invalid
  ORA-06512: at “SYS.DBMS_LOGMNR”, line 63
  ORA-06512: at line 1
  

  五、分析v$logmnr_contents

  前面我们已经知道了LogMiner的分析结果是放在v$logmnr_contents中,这里面有很多信息,我们可以根据需要追踪我们感兴趣的信息。那么我们通常感兴趣的有哪些呢?

  1、追踪数据库结构变化情况,即DDL操作,如前所述,这个只有Oracle9i才支持:

  SQL> select timestamp,sql_redo from v$logmnr_contents2
  where upper(sql_redo) like ‘%CREATE%’;
  TIMESTAMP
  ——————-
  SQL_REDO
  ————————-
  2003-09-21 10:01:55
  create table t (c1 number);

  2、追踪用户误操作或恶意操作:

  例如我们现实中有这样需求,有一次我们发现一位员工通过程序修改了业务数据库信息,把部分电话的收费类型改成免费了,现在就要求我们从数据库中查出到底是谁干的这件事?怎么查?LogMiner提供了我们分析日志文件的手段,其中v$logmnr_contents的SESSION_INFO列包含了下面的信息:

  login_username=NEW_97
  client_info= OS_username=oracle8 Machine_name=phoenix1
  OS_terminal=ttyp3 OS_process_id=8004 OS_program name=sqlplus@phoenix1
  (TNS V1-V3)
  

  虽然其中信息已经很多了,但在我们的业务数据库中,程序是通过相同的login_username登录数据库的,这样单从上面的信息是很难判断的。

  不过我们注意到,因为公司应用服务器不是每个人都有权限在上面写程序的,一般恶意程序都是直接通过他自己的PC连到数据库的,这就需要一个准确的定位。IP追踪是我们首先想到的,并且也满足我们的实际要求,因为公司内部IP地址分配是统一管理的,能追踪到IP地址我们就可以准确定位了。但从面的SESSION_INFO中我们并不能直接看到IP,不过我们还是有办法的,因为这个SESSION_INFO里面的内容其实是日志从V$SESSION视图里提取的,我们可以在生产数据库中创建一个追踪客户端IP地址的触发器:

  create or replace trigger on_logon_trigger
  after logon on database
  begin
  dbms_application_info.set_client_info(sys_context(’userenv’, ‘ip_address’));
  end;
  /
  

  现在,我们就可以在V$SESSION视图的CLIENT_INFO列中看到新登录的客户端IP地址了。那么上面的提出的问题就可以迎刃而解了。假如被更新的表名为HMLX,我们就可以通过下面的SQL来找到所需信息:

  SQL > select session_info ,sql_redo from v$logmnr_contents
  2 where upper(operation) = ‘UPDATE’ and upper(sql_redo) like ‘%HMLX%’
  3 /
  SESSION_INFO
  —————————————–
  SQL_REDO
  —————————————–
  login_username=C client_info=10.16.98.26 OS_username=sz-xjs-chengyx Machine_name
  =GDTEL\SZ-XJS-CHENGYX
  update “C”.”HMLX” set “NAME” = ‘free’ where “NAME” = ‘ncn.cn’ and ROWID = ‘AAABhTAA
  FAAABRaAAE’;

数据库设计22 Jul 2006 10:24 pm

以下是针对事务型数据库:
1.是否使用联合主键?个人倾向于少采用联合主键。因为这样会降低索引的效率,联合主键一般都要用到至少一个业务字段,往往是字符串型的,而且理论上多字段的索引比单字段的索引要慢些。看上去似乎也不那么清爽。
在实际的设计中,我尽量避免使用联合主键,有些时候“不得不”使用联合主键。

2.PK采用无意义的字段(逻辑主键)还是有意义的字段(业务主键)?个人倾向于“逻辑主键”,理由是这样设计出的数据库模型结构清晰、关系脉络清楚,往往更符合“第三范式”(虽然不是故意的,呵呵)。而且更容易避开“联合主键”,而且可以使用索引效率高的字段类型,比如int、long、number。缺点是用无意义的字段建立表间的关系,使跨表查询增多,效率下降。(矛盾无处不在,前面刚说完可以提高效率,这里马上又降低效率)。“业务主键”可以提升查询编码的简洁度和效率。
个人使用实际状况,总体来说“逻辑主键”比“业务主键”执行效率低,但不会低到无法满足需求。采用“逻辑主键”比采用“业务主键”更利于数据库模型的结构、关系清晰,也更便于维护。
对于分析型数据库,如数据仓库,千万不要这样做。

3.不要使用多对多关系?个人倾向于少使用多对多关系。这个问题其实不是数据库设计的问题了,在数据库设计中,多对多关系也仅仅存在于逻辑模型(E-R)阶段,物理模型不在有多对多关系,实际数据库中也不会有“多对多”关系。这是使用ORM时的问题,比如使用Hibernate,多对多关系有时会使编码看起来灵活一些,代价是效率的明显降低。
个人实际使用中,设计时基本不考虑多对多关系,但编码时总会有小组成员使用一些多对多关系,自己建立多对多的ORM,使自己编码方便些,用在数据量小的地方,影响不大。大数据量,则“禁止使用”。

4.为每个表增加一个state字段?我习惯在设计时给每个表设一个state字段,取值0或1,默认值为1,具体业务意义或操作上的意义可以自定义。可以作为一个状态控制字段,如查询、更新、删除条件,单据是否有效(业务单据对应的表会有业务意义上的“有/无效”或“状态”字段,这种情况下,我还是会再加一个state字段),甚至仅仅是控制一条数据是否“有效”(有效的意义你自己定)。在数据迁移(如转入分析用的数据库)时也可能会发挥作用。

5.为每个表设置一些备用字段?没办法,我总是设计不出“完美”的数据表,给每个表加几个备用字段(我一般用字符串型,随你)可以应付“不时之需”,尤其是需要长期维护的、业务可能有临时性变动的系统。

6.尽量不要在一个表中存入其关联表的字段?建议不存!这样做确实可以提高查询效率,但在一个有很多表,并且关联表多的情况下,很难保持数据的一致性!数据库结构也比较糟糕。而且不存,也不会使效率十分低下。

7.不要去直接修改数据库?个人认为这点很重要,当需要修改时,应该先去修改模型,然后同步物理数据库,尤其是团队开发,否则要多做更多的事情来搞定,也可能会引入更多的错误。 

Oracle中学22 Jul 2006 10:07 pm

   在Oracle数据库中,DBA可以通过观测一定的表或视图来了解当前空间的使用状况,进而作出可能的调整决定。

     一.表空间的自由空间  通过对表空间的自由空间的观察,可用来判断分配给某个表空间的空间是太多还是不够。请看下列的语句SQL > select a.file_id “FileNo”,a.tablespace_name
“Tablespace_name”,
2 a.bytes “Bytes”,a.bytes-sum(nvl(b.bytes,0)) “Used”,
3 sum(nvl(b.bytes,0)) “Free”,
4 sum(nvl(b.bytes,0))/a.bytes*100 “%free”
5 from dba_data_files a, dba_free_space b
6 where a.file_id=b.file_id(+)
7 group by a.tablespace_name ,
8 a.file_id,a.bytes order by a.tablespace_name;

File Tablespace
No _nameBytes Used Free %free
—— ——— ——– ——— ——— ———
11IDX_JF .146E+09 849305600 1.297E+09 60.431806
9 JFSJTS 2.146E+09 1.803E+09 343793664 16.016961
10JFSJTS 2.146E+09 1.359E+09 787431424 36.685546
2 RBS523239424 359800832 163438592 31.235909
12RBS1.610E+09 1.606E+09 3104768 .19289495
8 RBSJF 3.220E+09 2.716E+09 504356864 15.662396
7 SFGLTS 2.146E+09 1.228E+09 918159360 42.776014
6 SFSJTS 2.146E+09 1.526E+09 620093440 28.889457
1 SYSTEM 523239424 59924480 463314944 88.547407
3 TEMP 523239424294912 522944512 99.943637
4 TOOLS 15728640 12582912 314572820
5 USERS 7340032 81927331840 99.888393

12 rows selected.

  可以看出,在FileNo12的表空间RBS中,只有0.19%的分配空间未被使用,这个比例太小了,而在SYSTEMTEMP等表空间中,高达80%以上的空间未被利用,对于生产型数据库,这个表空间的设置有些偏高。

  可以看出,在为的表空间中,只有的分配空间未被使用,这个比例太小了,而在及等表空间中,高达以上的空间未被利用,对于生产型数据库,这个表空间的设置有些偏高。  关于自由空间的管理,有下面的一些建议:  利用ExportImport命令卸出和装入表空间可以释放大量的空间,从而缓解增加另外的数据文件的要求。

  利用及命令卸出和装入表空间可以释放大量的空间,从而缓解增加另外的数据文件的要求。  如果包含具有高插入(insert)和更新(update)活动的表的表空间中自由空间的比重下降到了15%以下,要为此表空间增加更多的空间。  对于一个基本是静态表数据的表空间,如果有多于20%的自由空间,则可以考虑减少分配给它的文件空间量。

  对于一个基本是静态表数据的表空间,如果有多于的自由空间,则可以考虑减少分配给它的文件空间量。  减少SYSTEM表空间的空间量比较困难,因为那要重建数据库。 表及索引的扩展

二表及索引的扩展  A.为了防止表或索引被过分扩展,及时实现对数据库的调整,用户应当经常对有关对象进行观察。  我们可以认为,扩展区域大于5个的表或索引为过分扩展(overextended)。请看下面的语句: 

  我们可以认为,扩展区域大于个的表或索引为过分扩展。请看下面的语句: SQL > select substr(segment_name,1,15)
Segment_name,segment_type,
2 substr(tablespace_name,1,10)
Tablepace_name,extents,Max_extents
3from dba_segments
4where extents >5 and owner=’JFCL’
5order by segment_name;
SEGMENT_NAMESEGMENT TABLEPACE_
EXTENTS MAX_EXTENTS
_TYPE
————– ——— ———-
CHHDFYB TABLE JFSJTS 11121
CHHDFYB_DHHMINDEX JFSJTS9121
DJHZFYB_BF TABLE JFSJTS 17500
DJHZFYB_DJHMINDEX IDX_JF6500
DJHZFYB_JZHMINDEX IDX_JF7500
GSMFYB TABLE JFSJTS 11121
JFDHTABLE JFSJTS 14500
JFDH_DHHM INDEX IDX_JF 61500
JFDH_JZHM INDEX IDX_JF 64500
XYKFYB TABLE JFSJTS7121
YHDATABLE JFSJTS6500
YHDA_BAKTABLE JFSJTS6500
YHHZFYB_12 TABLE JFSJTS 10500

13 rows selected.

  通过观察, DBA可以及时发现问题并进行相应的处理。
我们可以利用export卸出表,然后删除表,再利用import命令将表装入,这样,可以将不连续的区域合并成一个连续的空间。  B.如果用户希望对表的空间设置进行优化,例如,需要改变表EMPinitial参数,可以采用下面的方法:

  如果用户希望对表的空间设置进行优化,例如,需要改变表的参数,可以采用下面的方法:  1.在将EMP表卸出并删除后执行imp命令时使用indexfile参数:  imp userid=scott/tiger file=emp.dmp indexfile=emp.sql Oracle把表和索引的创建信息写到指定的文件,而不是把数据写回。

  把表和索引的创建信息写到指定的文件,而不是把数据写回。  2.打开emp.sql文件:REM CREATE TABLE “SCOTT”.”EMP” (”EMPNO”
NUMBER(4, 0), “ENAME”
REM VARCHAR2(10), “JOB” VARCHAR2(9),
“MGR” NUMBER(4, 0), “HIREDATE” DATE,
REM “SAL” NUMBER(7, 2), “COMM” NUMBER
(7, 2), “DEPTNO” NUMBER(2, 0))
REM PCTFREE 10 PCTUSED 40 INITRANS 1
MAXTRANS 255 LOGGING STORAGE(INITIAL
REM 10240 NEXT 10240 MINEXTENTS 1 MAXEXTENTS
121 PCTINCREASE 50 FREELISTS
REM 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE “USER_DATA” ;
REM … 14 rows

  对它进行编辑,去除“REM”等信息,找到Initial参数,根据需要改变它。  3.SQL*plus中执行emp.sql

  在中执行。  4.装入数据:mp userid=scott/tiger ignore=y file=emp.dmp

  需要注意的是,ignore参数必须设为Y.  C.可以用下面的语句来观察表或索引距离达到最大扩展的状况,“UNUSE”为距离达到最大扩展的值,在User_extents表中,extent_id是从0开始记述数的。 

  可以用下面的语句来观察表或索引距离达到最大扩展的状况,为距离达到最大扩展的值,在表中,是从开始记述数的。 

SQL >select a.table_name “TABLE_NAME”,max
(a.max_extents) “MAXEXTENTS” ,
2 max(b.extent_id)+1 “IN USE”, MAX
(a.max_extents)-(max(b.extent_id)+1) “UNUSE”
3 from user_tables a, user_extents b
4where a.table_name=b.segment_name
5 group by a.table_name ORDER BY 4;
TABLE_NAME MAXEXTENTS IN USEUNUSE
———- ———– ——– ———
YZPHB 98 1 97
SHJYB 121 1 120
SHFYB 121 1 120
RCHDB 121 1 120
SJTXDZB121 1 120
SJTXDAB121 1 120
CHYHB 121 1 120
JFDH 50014 486
8 rows selected.

  如果“UNUSE”小到一定的程度,我们就应该加以关注,进行适当的调整处理。 关于连续空间

三关于连续空间  可以用下面的语句来查看数据库中的自由空间:SQL > select * from dba_free_space
where tablespace_name=’SFSJTS’
2 order by block_id;

TABLESPACE FILE_ID BLOCK_ID BYTESBLOCKS
_NAME
———– ——— ————–
SFSJTS 6 133455 1064960 130
SFSJTS 6 133719 1032192 126
SFSJTS 6 133845 1064960 130
SFSJTS 6 135275 1064960 130
SFSJTS 6 135721 606208 74
SFSJTS 6 139877 901120 110
SFSJTS 6 143497 737280 90
SFSJTS 6 220248 737280 90
SFSJTS 6 246228 491520 60
SFSJTS 6 261804 1064960 130

10 rows selected.

  我们可以通过命令的结果来估计相邻自由空间的真正数量。对每一行,用起始快的id(BLOCK_ID)加上自由块(BLOCKS)的数量,如果其和与下一行的块id(BLOCK_ID)相等,则此两行是连续的。如上例第二行和第三行,133719+126=133845,而1338456+130!=135275,所以从block_id133719开始,有126+130=256block的连续空间。  在Oracle数据库的后台,系统监视器(SMON)周期性地合并自由空间相邻的块,以得到更大的连续块。而DBA可以用SQL命令来完成这个工作:alter tablespace tablespace_name coalesce;

  Oracle空间管理对数据库的工作性能有重要影响,其管理方法值得我们认真摸索研究。

  空间管理对数据库的工作性能有重要影响,其管理方法值得我们认真摸索研究。

ORACLE小学22 Jul 2006 10:03 pm

  SELECT语句用于从数据库中查询数据,当在PL/SQL中使用SELECT语句时,要与INTO子句一起使用,查询的返回值被赋予INTO子句中的变量,变量的声明是在DELCARE中。SELECT         INTO语法如下:
  SELECT [DISTICT|ALL]{*|column[,column,…]}
  INTO (variable[,variable,…] |record)
  FROM {table|(sub-query)}[alias]
  WHERE…………
  PL/SQL
中SELECT语句只返回一行数据。如果超过一行数据,那么就要使用显式游标(对游标的讨论我们将在后面进行),INTO子句中要有与SELECT子句中相同列数量的变量。INTO子句中也可以是记录变量。 
%TYPE
属性
  在PL/SQL中可以将变量和常量声明为内建或用户定义的数据类型,以引用一个列名,同时继承他的数据类型和大小。这种动态赋值方法是非常有用的,比如变量引用的列的数据类型和大小改变了,如果使用了%TYPE,那么用户就不必修改代码,否则就必须修改代码。
例:
v_empno SCOTT.EMP.EMPNO%TYPE;
v_salary EMP.SALARY%TYPE;
 不但列名可以使用%TYPE,而且变量、游标、记录,或声明的常量都可以使用%TYPE。这对于定义相同数据类型的变量非常有用。
  DELCARE
  V_A NUMBER(5):=10;
  V_B V_A%TYPE:=15;
  V_C V_A%TYPE;
  BEGIN
    DBMS_OUTPUT.PUT_LINE
    (’V_A=’||V_A||’V_B=’||V_B||’V_C=’||V_C);
  END
 
  SQL>/
  V_A=10 V_B=15 V_C=
  PL/SQL procedure successfully completed.
  SQL> 
其他DML语句
  其它操作数据的DML语句是:INSERT、UPDATE、DELETE和LOCK TABLE,这些语句在PL/SQL中的语法与在SQL中的语法相同。我们在前面已经讨论过DML语句的使用这里就不再重复了。在DML语句中可以使用任何在DECLARE部分声明的变量,如果是嵌套块,那么要注意变量的作用范围。 
例:

CREATE OR REPLACE PROCEDURE FIRE_EMPLOYEE (pempno in number)
 AS
  v_ename EMP.ENAME%TYPE;
  BEGIN
   
SELECT ename INTO v_ename
    FROM emp
    WHERE empno=p_empno;
    INSERT INTO FORMER_EMP(EMPNO,ENAME)
    VALUES (p_empno,v_ename);
    DELETE FROM emp
    WHERE empno=p_empno;
    UPDATE former_emp
    SET date_deleted=SYSDATE
    WHERE empno=p_empno;
   
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE(’Employee Number Not Found!’);
  END
DML
语句的结果
  当执行一条DML语句后,DML语句的结果保存在四个游标属性中,这些属性用于控制程序流程或者了解程序的状态。当运行DML语句时,PL/SQL打开一个内建游标并处理结果,游标是维护查询结果的内存中的一个区域,游标在运行DML语句时打开,完成后关闭。隐式游标只使用SQL%FOUND,SQL%NOTFOUND,SQL%ROWCOUNT三个属性.SQL%FOUND,SQL%NOTFOUND是布尔值,SQL%ROWCOUNT是整数值。
SQL%FOUNDSQL%NOTFOUND
  在执行任何DML语句前SQL%FOUND和SQL%NOTFOUND的值都是NULL,在执行DML语句后,SQL%FOUND的属性值将是:

. TRUE :INSERT
. TRUE ELETE
和UPDATE,至少有一行被DELETE或UPDATE.
. TRUE :SELECT INTO至少返回一行

当SQL%FOUND为TRUE时,SQL%NOTFOUND为FALSE。
SQL%ROWCOUNT
 
在执行任何DML语句之前,SQL%ROWCOUNT的值都是NULL,对于SELECT         INTO语句,如果执行成功,SQL%ROWCOUNT的值为1,如果没有成功,SQL%ROWCOUNT的值为0,同时产生一个异常NO_DATA_FOUND.
SQL%ISOPEN
SQL%ISOPEN是一个布尔值,如果游标打开,则为TRUE, 如果游标关闭,则为FALSE.对于隐式游标而言SQL%ISOPEN总是FALSE,这是因为隐式游标在DML语句执行时打开,结束时就立即关闭。

事务控制语句
  事务是一个工作的逻辑单元可以包括一个或多个DML语句,事物控制帮助用户保证数据的一致性。如果事务控制逻辑单元中的任何一个DML语句失败,那么整个事务都将回滚,在PL/SQL中用户可以明确地使用COMMIT、ROLLBACK、SAVEPOINT以及SET TRANSACTION语句。
  COMMIT语句终止事务,永久保存数据库的变化,同时释放所有LOCK,ROLLBACK终止现行事务释放所有LOCK,但不保存数据库的任何变化,SAVEPOINT用于设置中间点,当事务调用过多的数据库操作时,中间点是非常有用的,SET TRANSACTION用于设置事务属性,比如read-write和隔离级等。 
显式游标

  当查询返回结果超过一行时,就需要一个显式游标,此时用户不能使用select into语句。PL/SQL管理隐式游标,当查询开始时隐式游标打开,查询结束时隐式游标自动关闭。显式游标在PL/SQL块的声明部分声明,在执行部分或异常处理部分打开,取数据,关闭。
使用游标
  这里要做一个声明,我们所说的游标通常是指显式游标,因此从现在起没有特别指明的情况,我们所说的游标都是指显式游标。要在程序中使用游标,必须首先声明游标。
声明游标
语法:
  CURSOR cursor_name IS select_statement;
在PL/SQL中游标名是一个未声明变量,不能给游标名赋值或用于表达式中。
例:
  DELCARE
  CURSOR C_EMP IS SELECT empno,ename,salary
  FROM emp
  WHERE salary>2000 
  ORDER BY ename;
  ……..
  BEGIN
 
在游标定义中SELECT语句中不一定非要表可以是视图,也可以从多个表或视图中选择的列,甚至可以使用*来选择所有的列 。 
 
打开游标

使用游标中的值之前应该首先打开游标,打开游标初始化查询处理。打开游标的语法是:
  OPEN cursor_name
    cursor_name
是在声明部分定义的游标名。
 
例:
  OPEN C_EMP; 关闭游标
语法:
  CLOSE cursor_name
 
例:
  CLOSE C_EMP; 从游标提取数据
  从游标得到一行数据使用FETCH命令。每一次提取数据后,游标都指向结果集的下一行。语法如下:
  FETCH cursor_name INTO variable[,variable,…]
 
对于SELECT定义的游标的每一列,FETCH变量列表都应该有一个变量与之相对应,变量的类型也要相同。
  

例:
  SET SERVERIUTPUT ON
  DECLARE
  v_ename EMP.ENAME%TYPE;
  v_salary EMP.SALARY%TYPE;
  CURSOR c_emp IS SELECT ename,salary FROM emp;
  BEGIN
    OPEN c_emp;
      FETCH c_emp INTO v_ename,v_salary;
        DBMS_OUTPUT.PUT_LINE(’Salary of Employee’|| v_ename ||’is’|| v_salary);
      FETCH c_emp INTO v_ename,v_salary;
        DBMS_OUTPUT.PUT_LINE(’Salary of Employee’|| v_ename ||’is’|| v_salary);
      FETCH c_emp INTO v_ename,v_salary;
        DBMS_OUTPUT.PUT_LINE(’Salary of Employee’|| v_ename ||’is’|| v_salary);
    CLOSE c_emp;
  END
   
 
这段代码无疑是非常麻烦的,如果有多行返回结果,可以使用循环并用游标属性为结束循环的条件,以这种方式提取数据,程序的可读性和简洁性都大为提高,下面我们使用循环重新写上面的程序:
SET SERVERIUTPUT ON
DECLARE
v_ename EMP.ENAME%TYPE;
v_salary EMP.SALARY%TYPE;
CURSOR c_emp IS SELECT ename,salary FROM emp;
BEGIN
OPEN c_emp;
  LOOP
    FETCH c_emp INTO v_ename,v_salary;
    EXIT WHEN c_emp%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(’Salary of Employee’|| v_ename ||’is’|| v_salary);
END 
记录变量
  定义一个记录变量使用TYPE命令和%ROWTYPE,关于%ROWsTYPE的更多信息请参阅相关资料。
  记录变量用于从游标中提取数据行,当游标选择很多列的时候,那么使用记录比为每列声明一个变量要方便得多。
  当在表上使用%ROWTYPE并将从游标中取出的值放入记录中时,如果要选择表中所有列,那么在SELECT子句中使用*比将所有列名列出来要得多。
  例:
SET SERVERIUTPUT ON
DECLARE
R_emp EMP%ROWTYPE;
CURSOR c_emp IS SELECT * FROM emp;
BEGIN
OPEN c_emp;
  LOOP
  FETCH c_emp INTO r_emp;
  EXIT WHEN c_emp%NOTFOUND;
  DBMS_OUT.PUT.PUT_LINE(’Salary of Employee’||r_emp.ename||’is’|| r_emp.salary);
  END LOOP;
CLOSE c_emp;
END;
  
%ROWTYPE也可以用游标名来定义,这样的话就必须要首先声明游标:
  

SET SERVERIUTPUT ON
DECLARE
CURSOR c_emp IS SELECT ename,salary FROM emp;
R_emp c_emp%ROWTYPE;
BEGIN
OPEN c_emp;
LOOP
  FETCH c_emp INTO r_emp;
  EXIT WHEN c_emp%NOTFOUND;
  DBMS_OUT.PUT.PUT_LINE(’Salary of Employee’||r_emp.ename||’is’|| r_emp.salary);
END LOOP;
CLOSE c_emp;
END; 
带参数的游标
  与存储过程和函数相似,可以将参数传递给游标并在查询中使用。这对于处理在某种条件下打开游标的情况非常有用。它的语法如下:
  CURSOR cursor_name[(parameter[,parameter],…)] IS select_statement;
  
定义参数的语法如下:
  Parameter_name [IN] data_type[{:=|DEFAULT} value]
  

  与存储过程不同的是,游标只能接受传递的值,而不能返回值。参数只定义数据类型,没有大小。
  另外可以给参数设定一个缺省值,当没有参数值传递给游标时,就使用缺省值。游标中定义的参数只是一个占位符,在别处引用该参数不一定可靠。
  在打开游标时给参数赋值,语法如下:
  OPEN cursor_name[value[,value]….];
参数值可以是文字或变量。
  例:
DECALRE
CURSOR c_dept IS SELECT * FROM dept ORDER BY deptno;
CURSOR c_emp (p_dept VARACHAR2) IS
SELECT ename,salary
FROM emp
WHERE deptno=p_dept
ORDER BY ename
r_dept DEPT%ROWTYPE;
v_ename EMP.ENAME%TYPE;
v_salary EMP.SALARY%TYPE;
v_tot_salary EMP.SALARY%TYPE;
BEGIN
OPEN c_dept;
  LOOP
    FETCH c_dept INTO r_dept;
    EXIT WHEN c_dept%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(’Department:’|| r_dept.deptno||’-’||r_dept.dname);
    v_tot_salary:=0;
    OPEN c_emp(r_dept.deptno);
        LOOP
          FETCH c_emp INTO v_ename,v_salary;
          EXIT WHEN c_emp%NOTFOUND;
          DBMS_OUTPUT.PUT_LINE(’Name:’|| v_ename||’ salary:’||v_salary);
          v_tot_salary:=v_tot_salary+v_salary;
        END LOOP;
    CLOSE c_emp;
    DBMS_OUTPUT.PUT_LINE(’Toltal Salary for dept:’|| v_tot_salary);
  END LOOP;
CLOSE c_dept;
END; 

游标FOR循环
在大多数时候我们在设计程序的时候都遵循下面的步骤:
1、打开游标
2、开始循环
3、从游标中取值
那一行被返回
5、处理
6、关闭循环
7、关闭游标
  可以简单的把这一类代码称为游标用于循环。但还有一种循环与这种类型不相同,这就是FOR循环,用于FOR循环的游标按照正常的声明方式声明,它的优点在于不需要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。游标FOR循环的语法如下:
  FOR record_name IN
(corsor_name[(parameter[,parameter]…)]
| (query_difinition)
LOOP
statements
END LOOP;
  
下面我们用for循环重写上面的例子:
DECALRE
CURSOR c_dept IS SELECT deptno,dname FROM dept ORDER BY deptno;
CURSOR c_emp (p_dept VARACHAR2) IS
SELECT ename,salary
FROM emp
WHERE deptno=p_dept
ORDER BY ename
v_tot_salary EMP.SALARY%TYPE;
BEGIN
  FOR r_dept IN c_dept LOOP
  DBMS_OUTPUT.PUT_LINE(’Department:’|| r_dept.deptno||’-’||r_dept.dname);
  v_tot_salary:=0;
  FOR r_emp IN c_emp(r_dept.deptno) LOOP
  DBMS_OUTPUT.PUT_LINE(’Name:’ || v_ename || ’salary:’ || v_salary);  
  v_tot_salary:=v_tot_salary+v_salary;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE(’Toltal Salary for dept:’|| v_tot_salary);
END LOOP;
END; 
在游标FOR循环中使用查询
  在游标FOR循环中可以定义查询,由于没有显式声明所以游标没有名字,记录名通过游标查询来定义。
DECALRE
v_tot_salary EMP.SALARY%TYPE;
BEGIN
FOR r_dept IN (SELECT deptno,dname FROM dept ORDER BY deptno) LOOP
  DBMS_OUTPUT.PUT_LINE(’Department:’|| r_dept.deptno||’-’||r_dept.dname);
  v_tot_salary:=0;
  FOR r_emp IN (SELECT ename,salary
       
   FROM emp
           
WHERE deptno=p_dept
           
ORDER BY ename) LOOP
    DBMS_OUTPUT.PUT_LINE(’Name:’|| v_ename||’ salary:’||v_salary);
    v_tot_salary:=v_tot_salary+v_salary;
  END LOOP;
DBMS_OUTPUT.PUT_LINE(’Toltal Salary for dept:’|| v_tot_salary);
END LOOP;
END;
  

游标中的子查询
  语法如下:
 
CURSOR C1 IS SELECT * FROM emp
WHERE deptno NOT IN (SELECT deptno
  FROM dept
  WHERE dname!=’ACCOUNTING’);
可以看出与SQL中的子查询没有什么区别。  游标中的更新和删除
  在PL/SQL中依然可以使用UPDATE和DELETE语句更新或删除数据行。显式游标只有在需要获得多行数据的情况下使用。PL/SQL提供了仅仅使用游标就可以执行删除或更新记录的方法。
UPDATE或DELETE语句中的WHERE CURRENT OF子串专门处理要执行UPDATE或DELETE操作的表中取出的最近的数据。要使用这个方法,在声明游标时必须使用FOR UPDATE子串,当对话使用FOR UPDATE子串打开一个游标时,所有返回集中的数据行都将处于行级(ROW-LEVEL)独占式锁定,其他对象只能查询这些数据行,不能进行UPDATE、DELETE或SELECT…FOR         UPDATE操作。
  语法:
  FOR UPDATE [OF [schema.]table.column[,[schema.]table.column]..
  [nowait]
 
 
在多表查询中,使用OF子句来锁定特定的表,如果忽略了OF子句,那么所有表中选择的数据行都将被锁定。如果这些数据行已经被其他会话锁定,那么正常情况下ORACLE将等待,直到数据行解锁。  在UPDATE和DELETE中使用WHERE CURRENT OF子串的语法如下:
  

WHERE{CURRENT OF cursor_name|search_condition}
  
例:
DELCARE
CURSOR c1 IS SELECT empno,salary
FROM emp
WHERE comm IS NULL
FOR UPDATE OF comm;
v_comm NUMBER(10,2);
BEGIN
  FOR r1 IN c1 LOOP
  IF r1.salary 

SQL优化22 Jul 2006 09:59 pm

操作符优化

     IN 操作符 

     用IN写出来的SQL的优点是比较容易写及清晰易懂,这比较适合现代软件开发的风格。     但是用IN的SQL性能总是比较低的,从ORACLE执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别: 

    ORACLE试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询。由此可见用IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了。 

    推荐方案:在业务密集的SQL当中尽量不采用IN操作符。

     NOT IN操作符 

     此操作是强列推荐不使用的,因为它不能应用表的索引。 

    推荐方案:用NOT EXISTS 或(外连接+判断为空)方案代替

      操作符(不等于) 

     不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。      推荐方案:用其它相同功能的操作运算代替,如     a0 改为 a>0 or a

    a’’ 改为 a>’’

     IS NULL 或IS NOT NULL操作(判断字段是否为空) 

     判断字段是否为空一般是不会应用索引的,因为B树索引是不索引空值的。     推荐方案: 

    用其它相同功能的操作运算代替,如     a is not null 改为 a>0 或a>’’等。 

    不允许字段为空,而用一个缺省值代替空值,如业扩申请中状态字段不允许为空,缺省为申请。 

    建立位图索引(有分区的表不能建,位图索引比较难控制,如字段值太多索引会使性能下降,多人更新操作会增加数据块锁的现象)

     > 及 操作符(大于或小于操作符) 

    大于或小于操作符一般情况下是不用调整的,因为它有索引就会采用索引查找,但有的情况下可以对它进行优化,如一个表有100万记录,一个数值型字段A,30万记录的A=0,30万记录的A=1,39万记录的A=2,1万记录的A=3。那么执行A>2与A>=3的效果就有很大的区别了,因为A>2时ORACLE会先找出为2的记录索引再进行比较,而A>=3时ORACLE则直接找到=3的记录索引。

     LIKE操作符 

LIKE操作符可以应用通配符查询,里面的通配符组合可能达到几乎是任意的查询,但是如果用得不好则会产生性能上的问题,如LIKE ‘%5400%’ 这种查询不会引用索引,而LIKE ‘X5400%’则会引用范围索引。一个实际例子:用YW_YHJBQK表中营业编号后面的户标识号可来查询营业编号 YY_BH LIKE ‘%5400%’ 这个条件会产生全表扫描,如果改成YY_BH LIKE ’X5400%’ OR YY_BH LIKE ’B5400%’ 则会利用YY_BH的索引进行两个范围的查询,性能肯定大大提高。

    UNION操作符 

     UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。如:     select * from gc_dfys 

    union     select * from ls_jg_dfys 

    这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。     推荐方案:采用UNION ALL操作符替代UNION,因为UNION ALL操作只是简单的将两个结果合并后就返回。 

    select * from gc_dfys     union all 

    select * from ls_jg_dfys   

    SQL书写的影响     同一功能同一性能不同写法SQL的影响 

    如一个SQL在A程序员写的为     Select * from zl_yhjbqk 

    B程序员写的为     Select * from dlyx.zl_yhjbqk(带表所有者的前缀) 

    C程序员写的为     Select * from DLYX.ZLYHJBQK(大写表名) 

    D程序员写的为      Select *  from DLYX.ZLYHJBQK(中间多了空格) 

    以上四个SQL在ORACLE分析整理之后产生的结果及执行的时间是一样的,但是从ORACLE共享内存SGA的原理,可以得出ORACLE对每个SQL 都会对其进行一次分析,并且占用共享内存,如果将SQL的字符串及格式写得完全相同则ORACLE只会分析一次,共享内存也只会留下一次的分析结果,这不仅可以减少分析SQL的时间,而且可以减少共享内存重复的信息,ORACLE也可以准确统计SQL的执行频率。

     WHERE后面的条件顺序影响 

     WHERE子句后面的条件顺序对大数据量表的查询会产生直接的影响,如     Select * from zl_yhjbqk where dy_dj = ‘1KV以下’ and xh_bz=1 

    Select * from zl_yhjbqk where xh_bz=1  and dy_dj = ‘1KV以下’ 

    以上两个SQL中dy_dj(电压等级)及xh_bz(销户标志)两个字段都没进行索引,所以执行的时候都是全表扫描,第一条SQL的dy_dj = ‘1KV以下’条件在记录集内比率为99%,而xh_bz=1的比率只为0.5%,在进行第一条SQL的时候99%条记录都进行dy_dj及xh_bz的比较,而在进行第二条SQL的时候0.5%条记录都进行dy_dj及xh_bz的比较,以此可以得出第二条SQL的CPU占用率明显比第一条低。 

     查询表顺序的影响 

    在FROM后面的表中的列表顺序会对SQL执行性能影响,在没有索引及ORACLE没有对表进行统计分析的情况下ORACLE会按表出现的顺序进行链接,由此因为表的顺序不对会产生十分耗服务器资源的数据交叉。(注:如果对表进行了统计分析,ORACLE会自动先进小表的链接,再进行大表的链接)

     SQL语句索引的利用 

     对操作符的优化(见上节)     对条件字段的一些优化 

    采用函数处理的字段不能利用索引,如:     substr(hbs_bh,1,4)=’5400’,优化处理:hbs_bh like ‘5400%’ 

    trunc(sk_rq)=trunc(sysdate), 优化处理: 

    sk_rq>=trunc(sysdate) and sk_rq
sysdate+1)

     进行了显式或隐式的运算的字段不能进行索引,如: 

     ss_df+20>50,优化处理:ss_df>30     ‘X’||hbs_bh>’X5400021452’,优化处理:hbs_bh>’5400021542’ 

    sk_rq+5=sysdate,优化处理:sk_rq=sysdate-5     hbs_bh=5401002554,优化处理:hbs_bh=’ 5401002554’,注:此条件对hbs_bh 进行隐式的to_number转换,因为hbs_bh字段是字符型。 

    条件内包括了多个本表的字段运算时不能进行索引,如:      ys_df>cx_df,无法进行优化 

    qc_bh||kh_bh=’5400250000’,优化处理:qc_bh=’5400’ and kh_bh=’250000’

     应用ORACLE的HINT(提示)处理 

    提示处理是在ORACLE产生的SQL分析执行路径不满意的情况下要用到的。它可以对SQL进行以下方面的提示

     目标方面的提示: 

     COST(按成本优化)     RULE(按规则优化) 

    CHOOSE(缺省)(ORACLE自动选择成本或规则进行优化)      ALL_ROWS(所有的行尽快返回) 

    FIRST_ROWS(第一行数据尽快返回)

     执行方法的提示: 

     USE_NL(使用NESTED LOOPS方式联合)      USE_MERGE(使用MERGE JOIN方式联合) 

    USE_HASH(使用HASH JOIN方式联合)

     索引提示: 

    INDEX(TABLE INDEX)(使用提示的表索引进行查询)

     其它高级提示(如并行处理等等) 

    ORACLE的提示功能是比较强的功能,也是比较复杂的应用,并且提示只是给ORACLE执行的一个建议,有时如果出于成本方面的考虑ORACLE也可能不会按提示进行。根据实践应用,一般不建议开发人员应用ORACLE提示,因为各个数据库及服务器性能情况不一样,很可能一个地方性能提升了,但另一个地方却下降了,ORACLE在SQL执行分析方面已经比较成熟,如果分析执行的路径不对首先应在数据库结构(主要是索引)、服务器当前性能(共享内存、磁盘文件碎片)、数据库对象(表、索引)统计信息是否正确这几方面分析。

  未经过全面测试,可能有些地方分析不正确, 请网友多多指正!! 

职业生涯22 Jul 2006 09:34 pm

  1、如果有一定的数据库基础,知道SQL是怎么回事,即使写不出来简单的SQL,但可以看动它们,你就可以跳到2。否则请先看一下数据库基础和SQL语言,不需要很深,更不需要去记忆那些复杂的SQL命令,这些可以留在以后边应用边学习、记忆。

   2、要想学好ORACLE,首先要学习ORACLE的体系结构,现在你不需要深入理解它们,但要分清几个关键的概念:   instance&database, memory structure,process&those files,such as data file, control file, init parameter file etc以及database ,tablespace,data file和tablespace,segmnet,extent & block它们之间的关系。

   当然还要分清undo tablespace & redo log file等,对于很多初学者来说,能够对这些概念有个较为清晰的认识并不是一件容易的事,而这些并非Oracle的全部。

   3、2是有关ORACLE的一些基本概念,下面要学习的是一些简单的的实际操作,就是如何去管理ORACLE数据库,当然不是管理全部的ORACLE。在此过程中你将对SQL和ORACLE体系结构有个更深入的了解。

   4、到了这一步你基本上算是初步掌握了ORACLE,下面将要根据你的方向来具体学习其它的ORACLE知识了。如果你是开发人员,则需要更多地去学习PL/SQL以及DEVELOPER,而这将是一门新的课程。如果你是一名DBA,请继续。

   5、现在你可以根据自己的实际情况,进行有选择的学习,也就是说下面的内容没有特别顺序要求。可以选择深入学习ORACLE的管理、备份与恢复、性能调整、网络等。当然在学习这些知识的过程中,如果有实际的工作更好,这样你可以在实际中成长,学会TROUBLE SHOOTING。

  6、当然在学习的过程中,你可以在网站或论坛中与他人进行交流,可以看别人的一些经验文章,也可以自己写一些心得体会。技术进步的本身就是经验的积累和提炼过程,希望大家共同成长,欢迎大家相互交流。

ORACLE小学22 Jul 2006 12:56 am

一 触发器介绍

触发器是一种特殊的存储过程,它在插入,删除或修改特定表中的数据时触发执行,它比数据库本身标准的功能有更精细和更复杂的数据控制能力。数据库触发器有以下的作用:

* 安全性。可以基于数据库的值使用户具有操作数据库的某种权利。

# 可以基于时间限制用户的操作,例如不允许下班后和节假日修改数据库数据。

# 可以基于数据库中的数据限制用户的操作,例如不允许股票的价格的升幅一次超过10%。

* 审计。可以跟踪用户对数据库的操作。

# 审计用户操作数据库的语句。

# 把用户对数据库的更新写入审计表。

* 实现复杂的数据完整性规则。

# 实现非标准的数据完整性检查和约束。触发器可产生比规则更为复杂的限制。与规则不同,触发器可以引用列或数据库对象。例如,触发器可回退任何企图吃进超过自己保证金的期货。

# 提供可变的缺省值。

* 实现复杂的非标准的数据库相关完整性规则。触发器可以对数据库中相关的表进行连环更新。例如,在auths表author_code列上的删除触发器可导致相应删除在其它表中的与之匹配的行。

# 在修改或删除时级联修改或删除其它表中的与之匹配的行。

# 在修改或删除时把其它表中的与之匹配的行设成NULL值。

# 在修改或删除时把其它表中的与之匹配的行级联设成缺省值。

# 触发器能够拒绝或回退那些破坏相关完整性的变化,取消试图进行数据更新的事务。当插入一个与其主健不匹配的外部键时,这种触发器会起作用。例如,可以在books.author_code列上生成一个插入触发器,如果新值与auths.author_code列中的某值不匹配时,插入被回退。

* 同步实时地复制表中的数据。

* 自动计算数据值,如果数据的值达到了一定的要求,则进行特定的处理。例如,如果公司的帐号上的资金低于5万元则立即给财务人员发送警告数据。

ORACLE与SYBASE数据库的触发器有一定的区别,下面将分别讲述这两种数据库触发器的作用和写法。

二 ORACLE 触发器

ORACLE产生数据库触发器的语法为:

create [or replace] trigger 触发器名 触发时间 触发事件

on 表名

[for each row]

pl/sql 语句

其中:

触发器名:触发器对象的名称。由于触发器是数据库自动执行的,因此该名称只是一个名称,没有实质的用途。

触发时间:指明触发器何时执行,该值可取:

before—表示在数据库动作之前触发器执行;

after—表示在数据库动作之后出发器执行。

触发事件:指明哪些数据库动作会触发此触发器:

insert:数据库插入会触发此触发器;

update:数据库修改会触发此触发器;

delete:数据库删除会触发此触发器。

表 名:数据库触发器所在的表。

for each row:对表的每一行触发器执行一次。如果没有这一选项,则只对整个表执行一次。

举例:下面的触发器在更新表auths之前触发,目的是不允许在周末修改表:

create trigger auth_secure

before insert or update or delete //对整表更新前触发

on auths

begin

if(to_char(sysdate,’DY’)=’SUN’

RAISE_APPLICATION_ERROR(-20600,’不能在周末修改表auths’);

end if;

end

三 SYBASE数据库触发器

SYBASE数据库触发器的作用与ORACLE非常类似,仅有较小的差异。

SYBASE产生触发器的语法为:

CREATE TRIGGER 触发器名

ON 表名

FOR INSERT,UPDATE,DELETE

AS

SQL_statement |

FOR INSERT,UPDATE

AS

IF UPDATE(column_name) [AND|OR UPDATE(column_name)]…

SQL_statements

上面FOR子句用来指定在触发器上的哪些数据更新命令可激活该触发器。IF UPDATE子句检查对指定列的操作类型,在IF UPDATE子句中可指定多个列。

与ORACLE不同,对于每条SQL语句,触发器只执行一次。触发器在数据更新语句完成以后立即执行。触发器和启动它的语句被当作一个事务处理,事务可以在触发器中回退。

下面举例说明SYBASE触发器的写法。

create trigger forinsert_books

on books

for insert

as

if(select count(*) from auths,inserted

where auths.author_code=insert.author_code)!=@@rowcount

begin

rollback transaction

print “books 表中 author_code 列的值在auths 表中不存在。”

end

ORACLE小学22 Jul 2006 12:52 am

  SQL*Loader是Oracle数据库导入外部数据的一个工具.它和DB2的Load工具相似,但有更多的选择,它支持变化的加载模式,可选的加载及多表加载.
  
  如何使用 SQL*Loader 工具
  
  我们可以用Oracle的sqlldr工具来导入数据。例如:
  sqlldr scott/tiger control=loader.ctl
  
  控制文件(loader.ctl) 将加载一个外部数据文件(含分隔符). loader.ctl如下:
  load data
  infile ‘c:\data\mydata.csv’
  into table emp
  fields terminated by “,” optionally enclosed by ‘”‘
  ( empno, empname, sal, deptno )
  
  mydata.csv 如下:
  10001,”Scott Tiger”, 1000, 40
  10002,”Frank Naude”, 500, 20
  
  下面是一个指定记录长度的示例控制文件。”*” 代表数据文件与此文件同名,即在后面使用BEGINDATA段来标识数据。
  load data
  infile *
  replace
  into table departments
  ( dept position (02:05) char(4),
  deptname position (08:27) char(20)
  )
  begindata
  COSC COMPUTER SCIENCE
  ENGL ENGLISH LITERATURE
  MATH MATHEMATICS
  POLY POLITICAL SCIENCE
  Unloader这样的工具
  Oracle 没有提供将数据导出到一个文件的工具。但是,我们可以用SQL*Plus的select 及 format 数据来输出到一个文件:
  set echo off newpage 0 space 0 pagesize 0 feed off head off trimspool on
  spool oradata.txt
  select col1 || ‘,’ || col2 || ‘,’ || col3
  from tab1
  where col2 = ‘XYZ’;
  spool off
  
  另外,也可以使用使用 UTL_FILE PL/SQL 包处理:
  rem Remember to update initSID.ora, utl_file_dir=’c:\oradata’ parameter
  declare
  fp utl_file.file_type;
  begin
  fp := utl_file.fopen(’c:\oradata’,'tab1.txt’,'w’);
  utl_file.putf(fp, ‘%s, %s\n’, ‘TextField’, 55);
  utl_file.fclose(fp);
  end;
  /
  
  当然你也可以使用第三方工具,如SQLWays ,TOAD for Quest等。
  
  加载可变长度或指定长度的记录
  
  如:
  LOAD DATA
  INFILE *
  INTO TABLE load_delimited_data
  FIELDS TERMINATED BY “,” OPTIONALLY ENCLOSED BY ‘”‘
  TRAILING NULLCOLS
  ( data1,
  data2
  )
  BEGINDATA
  11111,AAAAAAAAAA
  22222,”A,B,C,D,”
  
  下面是导入固定位置(固定长度)数据示例:
  LOAD DATA
  INFILE *
  INTO TABLE load_positional_data
  ( data1 POSITION(1:5),
  data2 POSITION(6:15)
  )
  BEGINDATA
  11111AAAAAAAAAA
  22222BBBBBBBBBB
  
  跳过数据行:
  
  可以用 “SKIP n” 关键字来指定导入时可以跳过多少行数据。如:
  LOAD DATA
  INFILE *
  INTO TABLE load_positional_data
  SKIP 5
  ( data1 POSITION(1:5),
  data2 POSITION(6:15)
  )
  BEGINDATA
  11111AAAAAAAAAA
  22222BBBBBBBBBB
  
  导入数据时修改数据:
  
  在导入数据到数据库时,可以修改数据。注意,这仅适合于常规导入,并不适合 direct导入方式.如:
  LOAD DATA
  INFILE *
  INTO TABLE modified_data
  ( rec_no “my_db_sequence.nextval”,
  region CONSTANT ‘31′,
  time_loaded “to_char(SYSDATE, ‘HH24:MI’)”,
  data1 POSITION(1:5) “:data1/100″,
  data2 POSITION(6:15) “upper(:data2)”,
  data3 POSITION(16:22)”to_date(:data3, ‘YYMMDD’)”
  )
  BEGINDATA
  11111AAAAAAAAAA991201
  22222BBBBBBBBBB990112
  
  LOAD DATA
  INFILE ‘mail_orders.txt’
  BADFILE ‘bad_orders.txt’
  APPEND
  INTO TABLE mailing_list
  FIELDS TERMINATED BY “,”
  ( addr,
  city,
  state,
  zipcode,
  mailing_addr “decode(:mailing_addr, null, :addr, :mailing_addr)”,
  mailing_city “decode(:mailing_city, null, :city, :mailing_city)”,
  mailing_state
  )
  
  将数据导入多个表:
  
  如:
  LOAD DATA
  INFILE *
  REPLACE
  INTO TABLE emp
  WHEN empno != ‘ ‘
  ( empno POSITION(1:4) INTEGER EXTERNAL,
  ename POSITION(6:15) CHAR,
  deptno POSITION(17:18) CHAR,
  mgr POSITION(20:23) INTEGER EXTERNAL
  )
  INTO TABLE proj
  WHEN projno != ‘ ‘
  ( projno POSITION(25:27) INTEGER EXTERNAL,
  empno POSITION(1:4) INTEGER EXTERNAL
  )
  
  导入选定的记录:
  
  如下例: (01) 代表第一个字符, (30:37) 代表30到37之间的字符:
  LOAD DATA
  INFILE ‘mydata.dat’ BADFILE ‘mydata.bad’ DISCARDFILE ‘mydata.dis’
  APPEND
  INTO TABLE my_selective_table
  WHEN (01) ‘H’ and (01) ‘T’ and (30:37) = ‘19991217′
  (
  region CONSTANT ‘31′,
  service_key POSITION(01:11) INTEGER EXTERNAL,
  call_b_no POSITION(12:29) CHAR
  )
  
  导入时跳过某些字段:
  
  可用 POSTION(x:y) 来分隔数据. 在Oracle8i中可以通过指定 FILLER 字段实现。FILLER 字段用来跳过、忽略导入数据文件中的字段.如:
  LOAD DATA
  TRUNCATE INTO TABLE T1
  FIELDS TERMINATED BY ‘,’
  ( field1,
  field2 FILLER,
  field3
  )
  
  导入多行记录:
  
  可以使用下面两个选项之一来实现将多行数据导入为一个记录:
  
  CONCATENATE: – use when SQL*Loader should combine the same number of physical records together to form one logical record.
  
  CONTINUEIF – use if a condition indicates that multiple records should be treated as one. Eg. by having a ‘#’ character in column 1.
  
  SQL*Loader 数据的提交:
  
  一般情况下是在导入数据文件数据后提交的。
  
  也可以通过指定 ROWS= 参数来指定每次提交记录数。
  
  提高 SQL*Loader 的性能:
  
  1) 一个简单而容易忽略的问题是,没有对导入的表使用任何索引和/或约束(主键)。如果这样做,甚至在使用ROWS=参数时,会很明显降低数据库导入性能。
  
  2) 可以添加 DIRECT=TRUE来提高导入数据的性能。当然,在很多情况下,不能使用此参数。
  
  3) 通过指定 UNRECOVERABLE选项,可以关闭数据库的日志。这个选项只能和 direct 一起使用。
  
  4) 可以同时运行多个导入任务.
  
  常规导入与direct导入方式的区别:
  
  常规导入可以通过使用 INSERT语句来导入数据。Direct导入可以跳过数据库的相关逻辑(DIRECT=TRUE),而直接将数据导入到数据文件中。

下一页 »