oracle数据库案例教程


目录 翰子昂系列理论教材 - I - 目录 专题一 Oracle 入门........................................................................................................................... 1 1. 教学目标........................................................................................................................ 1 2. 工作任务........................................................................................................................ 1 3. 相关实践知识................................................................................................................ 1 3.1 Oracle 10g 的安装和卸载 .................................................................................... 1 3.2 用 Net Configuration Assistant 配置客户端服务名 ......................................... 10 3.3 启动 Oracle 服务 ............................................................................................... 16 3.4 认识 Oracle 常用工具 ....................................................................................... 18 3.5 创建数据库和表空间........................................................................................ 24 3.6 Oracle 中的用户管理 ......................................................................................... 32 4. 实验.............................................................................................................................. 34 5. 课后作业...................................................................................................................... 35 专题二 锁和表分区......................................................................................................................... 36 1. 教学目标...................................................................................................................... 36 2. 工作任务...................................................................................................................... 36 3. 相关实践知识.............................................................................................................. 36 3.1 使用行级锁和时间戳来保证数据完整性........................................................ 36 3.2 使用表级锁........................................................................................................ 40 3.3 使用表分区........................................................................................................ 43 4. 提高.............................................................................................................................. 49 5. 实验.............................................................................................................................. 50 6. 课后作业...................................................................................................................... 50 专题三 SQL 语句和 SQL 函数....................................................................................................... 52 教学目标........................................................................................................................... 52 案例一 Oracle 数据库中的 DDL、DML 和 DCL ................................................................. 53 1. 教学目标...................................................................................................................... 53 2. 工作任务...................................................................................................................... 53 3. 相关实践知识.............................................................................................................. 53 4. 实验.............................................................................................................................. 67 5. 课后作业...................................................................................................................... 67 案例二 Oracle 数据库中的函数及子查询 ............................................................................. 68 1. 教学目标...................................................................................................................... 68 2. 工作任务...................................................................................................................... 68 3. 相关实践知识.............................................................................................................. 68 3.1 综合使用各种函数............................................................................................ 68 3.2 字符函数............................................................................................................ 78 3.3 数字函数............................................................................................................ 81 3.4 日期函数............................................................................................................ 84 3.5 转换函数............................................................................................................ 85 4. 实验.............................................................................................................................. 86 目录 - II - 翰子昂系列理论教材 5. 课后作业...................................................................................................................... 86 专题四 数据库对象......................................................................................................................... 87 1. 教学目标...................................................................................................................... 87 2. 工作任务...................................................................................................................... 87 3. 相关实践知识.............................................................................................................. 87 3.1 使用同义词........................................................................................................ 87 3.2 使用序列实现自增主键.................................................................................... 89 3.3 使用视图............................................................................................................ 92 3.4 使用索引加快表的查询.................................................................................... 99 4. 实验............................................................................................................................ 108 5. 课后作业.................................................................................................................... 108 专题五 PL/SQL 编程 .....................................................................................................................110 1. 教学目标.....................................................................................................................110 2. 工作任务.....................................................................................................................110 3. 相关实践知识.............................................................................................................110 3.1 用 PL/SQL 块来查询表内信息........................................................................111 3.2 用条件控制语句来实现为员工加薪...............................................................116 3.3 使用循环控制语句...........................................................................................118 3.4 用顺序控制语句实现更新员工工资...............................................................119 3.5 PL/SQL 异常处理机制的应用......................................................................... 120 4. 提高............................................................................................................................ 124 5. 实验............................................................................................................................ 125 6. 课后作业.................................................................................................................... 126 专题六 游标................................................................................................................................... 127 1. 教学目标.................................................................................................................... 127 2. 工作任务.................................................................................................................... 127 3. 相关实践知识............................................................................................................ 127 3.1 使用游标属性.................................................................................................. 127 3.2 用游标生成员工报表...................................................................................... 129 3.3 用游标生成分部门员工报表.......................................................................... 133 3.4 用游标更新员工工资...................................................................................... 135 3.5 使用 REF 游标动态返回结果集..................................................................... 137 4. 提高............................................................................................................................ 138 5. 实验............................................................................................................................ 140 6. 课后作业.................................................................................................................... 140 专题七 过程、函数和程序包....................................................................................................... 141 1. 教学目标.................................................................................................................... 141 2. 工作任务.................................................................................................................... 141 3. 相关实践知识............................................................................................................ 141 3.1 无参的显示“Hello World!”的过程 ............................................................ 141 3.2 用带输入参数的过程向表中插入记录.......................................................... 142 3.3 用带输出参数的过程查询表中的记录数...................................................... 144 目录 翰子昂系列理论教材 - III - 3.4 使用带输入输出参数的过程查询记录是否存在.......................................... 144 3.5 使用函数查询部门信息.................................................................................. 145 3.6 使用程序包封装过程和函数.......................................................................... 147 4. 提高............................................................................................................................ 150 4.1 用过程返回结果集.......................................................................................... 150 4.2 在企业管理器中管理过程、函数和程序包.................................................. 151 5. 实验............................................................................................................................ 153 6. 课后作业.................................................................................................................... 153 专题八 触发器............................................................................................................................... 155 1. 教学目标.................................................................................................................... 155 2. 工作任务.................................................................................................................... 155 3. 相关实践知识............................................................................................................ 155 3.1 BEFORE 行级触发器 ...................................................................................... 156 3.2 AFTER 行级触发器 ......................................................................................... 160 3.3 BEFORE 语句级触发器 .................................................................................. 161 3.4 AFTER 语句级触发器 ..................................................................................... 162 3.5 INSTEAD OF 触发器....................................................................................... 164 3.6 DDL 触发器 ..................................................................................................... 167 3.7 数据库启动和关闭触发器.............................................................................. 168 3.8 用户登录和退出触发器.................................................................................. 169 3.9 管理触发器...................................................................................................... 171 4. 实验............................................................................................................................ 172 5. 课后作业.................................................................................................................... 173 专题九 数据库高级管理............................................................................................................... 174 1. 教学目标.................................................................................................................... 174 2. 工作任务.................................................................................................................... 174 3. 相关实践知识............................................................................................................ 174 3.1 用户和角色的创建和使用.............................................................................. 174 3.2 调整数据库从非归档模式到归档模式.......................................................... 179 3.3 数据导入导出.................................................................................................. 184 4. 实验............................................................................................................................ 193 5. 课后作业.................................................................................................................... 193 目录 - IV - 翰子昂系列理论教材 专题一 Oracle 入门 翰子昂系列理论教材 - 1 - 专题一 Oracle 入门 1. 教学目标 1.1 了解 Oracle 10g 的安装和卸载 1.2 学会配置 Oracle 客户端 1.3 学会使用 Oracle 常用工具:企业管理器、iSQL*Plus 和 SQL*Plus 1.4 掌握创建数据库和表空间的方法,了解 Oracle 的体系结构 1.5 掌握 Oracle 中的基本用户管理 2. 工作任务 2.1 在 Windows 环境下安装和卸载 Oracle 10g 2.2 配置 Oracle 客户端 2.3 学习使用 Oracle 常用工具:企业管理器、iSQL*Plus 和 SQL*Plus 2.4 创建数据库和表空间 2.5 创建用户并为之授权,修改用户口令,删除用户 3. 相关实践知识 3.1 Oracle 10g 的安装和卸载 1. 在 Windows 环境下,将 Oracle 10g 安装盘放入光盘驱动器,安装程序会自动运行, 显示如如图 1-1 所示的窗口。 理论知识: Oracle 简介 Oracle 公司,中文翻译成甲骨文公司,是全球最大的信息管理软件及服务供应商。该 公司成立于 1977 年,总部位于美国加州的红木海岸城。目前,Oracle 产品覆盖了大、中、 小型机等几十种机型,Oracle 数据库已成为世界上使用最广泛的数据库系统之一,Oracle 公司已成为这一领域的领军者与标准制订者。经过 30 多年的不懈发展,Oracle 数据库已经 可以应用于从支持成千上万用户的分布式联机事务处理系统到拥有数万亿字节的用于决策支 持数据仓库的广泛领域。Oracle 公司推出的 Oracle 数据库系统始终占据着数据库市场龙 头的地位。 Oracle 公司敢为人先,始终引领着数据库发展的潮流。在号称第三代互联网技术—— “网格计算”技术蓬勃兴起之时,Oracle 公司推出了最新的支持网格环境的数据库解决方 案——Oracle 10g,其中的 g 代表网格(grid)。 Oracle 数据库案例教程_教师用书 - 2 - 翰子昂系列理论教材 Oracle l0g 数据库是第一个为企业级网格计算而设计的数据库。Oracle l0g 在 Ora cle 9i 的基础上,提供了针对网格计算更多的特性,如更大的规模、可管理性、高可用性 及业务智能等。 在过去的几年中,Oracle 数据库环境已成为世界上最流行的数据库平台之一。据有关 资料所述,目前 Oracle 已经占领了 46%以上的数据库市场份额,并且有进一步扩大的趋势。 在高端数据库领域,Oracle 更是无所匹敌,因此,掌握 Oracle 数据库技术是广大 IT 人员 的一项基本要求。尽管 Oracle 数据库的主要目标依旧是服务于那些要求海量数据处理能力 的大型公司或政府部门,但 Oracle 已经推出了许多适合较小组织的新产品,如 Personal Oracle 等。在各种操作系统平台上,都有相应的数据库产品。将 Oracle 集成到各种类型 的企业和组织的计划正在进行中,并已经取得了巨大的成就,这意味着不只是大企业需要 O- racle 数据库,广大中小企业同样需要 Oracle 数据库。 图 1-1 光盘自动安装界面 2. 单击“开始安装”,启动安装向导,进入“选择安装方法”界面,如图 1-2 所示。 专题一 Oracle 入门 翰子昂系列理论教材 - 3 - 图 1-2 选择安装方法 3. 可以选择“基本安装”或“高级安装”,我们选“基本安装”,同时选中创建启动数 据库,输入创建的全局数据库(能唯一标识一个数据库的名称,Oracle 10g 之前的版本要求 全局数据库名为数据库名+数据库域名,Oracle 10g 对此没有要求)的名称(例如“orcl”) 和口令。 图 1-3 选择安装类型 Oracle 数据库案例教程_教师用书 - 4 - 翰子昂系列理论教材 注意: Oracle 10g Release 2 规定,口令不能为“CHANGE_ON_INSTALL”, “MANAGER”,“DBSNMP”,“SYS-MAN”。这些口令分别为“SYS” “SYSTEM”“SYSMAN”“DBSNMP”四个数据库用户的默认口令。 理论知识: Oracle 10g 数据库核心产品:  Oracle 数据库 10g 标准版 1(Oracle Database 10g Standard Edition One)  Oracle 数据库 10g 标准版(Oracle Database 10g Standard Edition)  Oracle 数据库 10g 企业版(Oracle Database 10g Enterprise Edition)  Oracle 数据库 10g 个人版(Oracle Database 10g Personal Edition) 一般的部门级别的应用,比如一个部门的考勤管理,标准版足够用,只有大型的企业级 应用,比如一个大型制造企业的 ERP 系统,需要数据分布式的存储和计算,才选择企业版, 个人版本一般个人学习用。 4. 选择“安装类型”。Oracle 10g Release 2 的安装类型可为:企业版、标准版和个人版, 可根据需要进行选择,默认为企业版,单击“下一步”按钮,开始准备安装。 图 1-4 准备安装 接着安装程序自动进行“产品特定的先决条件检查”,如图 1-5 所示。 5. 安装程序自动对当前系统进行检查,检查其是否符合安装要求,只有最终出现“检查 完成。此次检查的总体结果为:通过”(如图 1-5),才能继续安装,否则检查相应的检查项, 逐一改正,直到“总体结果为:通过”。单击“下一步”按钮,继续安装,显示“概要”对话 框,如图 1-6 所示。 专题一 Oracle 入门 翰子昂系列理论教材 - 5 - 图 1-5 产品特定的先决条件检查 图 1-6 安装概要 Oracle 数据库案例教程_教师用书 - 6 - 翰子昂系列理论教材 6. 单击“安装”按钮,Oracle Universal Installer 将安装 Oracle 系统。在安装过程中,用 户可以看到 Oracle 创建数据,以及对一些服务进行配置,如图 1-7、图 1-8、图 1-9 所示。 图 1-7 安装过程 图 1-8 配置特定服务 专题一 Oracle 入门 翰子昂系列理论教材 - 7 - 图 1-9 复制数据库文件、创建并启动 Oracle 实例 7. 当安装完成后,安装向导将弹出如图 1-10 所示的窗口,显示已经安装的数据库信息。 图 1-10 已安装数据库信息 8. 单击图 1-10 中的“口令管理”,弹出如图 1-11“口令管理”窗口(也可以不点击“口 令管理”,直接点“确定”进入到图 1-12 的安装结束界面,这样的话 SYS、SYSTEM、DB- SNMP、SYSMAN 用户的口令都是在图 1-2 中设置的口令,且 SCOTT 用户默认是被锁定的)。 Oracle 数据库案例教程_教师用书 - 8 - 翰子昂系列理论教材 图 1-11 口令管理 9. 拖动滚动条找到 SCOTT 用户,单击 SCOTT 用户“是否锁定帐户”列上的蓝钩,解除 对 SCOTT 用户的锁定(因为在本书中许多的案例都是在 SCOTT 用户下完成的,所以需要为 其解锁,在实际的安装过程中,用户可以根据实际情况解锁或不解锁),为 SYS 和 SYSTEM 用户设置口令后单击“确定”,安装程序将返回到图 1-10 的窗口。 10. 单击图 1-10 中的“确定”,进入“安装结束”窗口,如图 1-12 所示。 图 1-12 安装结束 专题一 Oracle 入门 翰子昂系列理论教材 - 9 - 11. 单击“退出”,退出安装程序。至此,Oracle 10g 安装完毕。 12. 下面开始演示如何卸载 Oracle 10g。在 Windows 的“开始”菜单中依次选择:“开始” “程序”“Oracle—OraDb10g_home1”“Oracle Installation Products”“Universal Installer”,将显示如图 1-13 所示的窗口。 图 1-13 卸载产品 13. 单击“卸载产品”,出现如图 1-14 所示的窗口。 Oracle 数据库案例教程_教师用书 - 10 - 翰子昂系列理论教材 图 1-14 产品清单 14. 展开节点,选定要卸载的项目,然后单击“删除”,出现如图 1-15 所示的“确认” 窗口,显示选定的卸载项目。 图 1-15 卸载确认 15. 如果单击“是”,程序执行完毕即卸载选定的项目,在此只做演示,所以请点“否” 退出卸载产品。 3.2 用 Net Configuration Assistant 配置客户端服务名 1. 在 Oracle 安装完成之后,客户端要和数据库服务器建立连接,必须进行网络连接配置。 在 Windows 的“开始”菜单中依次选择:“开始”“程序”“Oracle—OraDb10g_hom-e1” “配置和移植工具”“Net Configuration Assistant”,会出现如图 1-16 所示的窗口。 图 1-16 欢迎使用 专题一 Oracle 入门 翰子昂系列理论教材 - 11 - 理论知识: 网络连接配置 Oracle 数据库基于“客户端/服务器”(Client/Server)系统结构,即客户端系统 和服务器系统。服务器系统执行数据库相关的所有活动,客户端系统执行与用户交互的活动, 它们又被称为前端系统和后端系统。 客户端和服务器可以是一台机器,也可以是通过网络连接起来的不同操作系统、不同硬 件平台的机器。 客户端应用程序向数据库服务器发送请求并接收信息,以此种方式与数据库进行交互, 充当用户与数据库之间的接口。 数据库服务器对数据库进行管理,处理来自多个用户的访问,它能够在所有客户端应用 程序访问数据的过程中,全面地保持数据库的完整性,并控制数据库访问权限和其它安全性 需求。 在 Oracle 安装完成之后,客户端要和数据库服务器建立连接,必须进行网络连接配置, 包括服务器端配置和客户端配置。即服务器端配置监听器和客户端配置服务名。可以用 Net Configuration Assistant 或 Net Manager 工具,甚至可以直接修改相关的参数文件。 首先,服务器端的监听配置包括监听协议、端口号以及其它相关信息的参数。监听器配 置存储在一个名叫 listener.ora 的参数文件中,该文件在 Windows 环境下可能的位置是 “C:\oracle\product\10.2.0\db_1\NETWORK\ADMIN\”。 其实我们可以不自己配置监听器,因为在安装 Oracle 的时候系统已经为我们配置好了 一个名叫“LISTENER”的监听器,端口号是 1521,它对应的服务是 OracleOraDb10g_- home1TNSListener。 然后,开始配置客户端服务名。配置服务名的目的是让客户端通过服务名来与远程或本 地的监听器建立连接。客户端用它向服务器发送连接请求。要在一台没有安装数据库服务器 的机器上连接 Oracle 服务器,必须单独安装 Oracle 客户端软件,在服务器上则自动包含 了客户端软件。 安装 Oracle 时用户指定了一个全局数据库名,即 SID 名称。Oracle 用此 SID 名称在 服务器端自动创建了一个服务名,如图 1-18 中的“ORCL”(因此,当服务器和和客户端在 一台机器上时,可以不配置服务名而直接使用系统自动创建的服务名)。在客户端创建服务 名时,需要指定服务器端服务名、网络协议、主机名和监听器端口等。这些配置信息都存储 在 tnsnames.ora 文件中,保存位置与 listener.ora 相同。 2. 在窗口中选择“本地 Net 服务名配置”,点击“下一步”,进入 Net 服务名配置,出现 如图 1-17 所示的窗口。 Oracle 数据库案例教程_教师用书 - 12 - 翰子昂系列理论教材 图 1-17 服务名配置 3. 选中“添加”单选按钮,点击“下一步”,出现如图 1-18 所示的窗口。 图 1-18 服务名 4. 在服务名文本框中输入远程数据库的服务名,如“ORCL”,单击“下一步”,出现 “请选择协议”窗口,如图 1-19 所。 专题一 Oracle 入门 翰子昂系列理论教材 - 13 - 图 1-19 请选择协议 5. 选择“TCP”并单击“下一步”,出现“TCP/IP 协议”窗口,如图 1-20 所示。 图 1-20 TCP/IP 协议 6. 在文本框中输入数据库服务器的主机名“HANDSONSERVER”或服务器的 IP 地址, 选择“使用标准端口号 1521”,单击“下一步”按钮,出现如图 1-21 所示的“测试”窗口。 Oracle 数据库案例教程_教师用书 - 14 - 翰子昂系列理论教材 图 1-21 测试 7. 选择“是,进行测试”,单击“下一步”按钮,出现如图 1-22 所示的窗口,提示“测 试成功”。 图 1-22 正在连接 专题一 Oracle 入门 翰子昂系列理论教材 - 15 - 注意: 如果提示“测试未成功”,请点击“更改登陆”,改变 SYSTEM 用户的 登陆口令为安装时设置的口令。 8. 在图 1-22 所示的窗口中单击“下一步”按钮,出现如图 1-23 所示的窗口。 图 1-23 Net 服务名 9. 输入要创建的本地服务名,如“MYSERVER”,单击“下一步”按钮,出现如图 1-2 4 所示的窗口。 Oracle 数据库案例教程_教师用书 - 16 - 翰子昂系列理论教材 图 1-24 是否配置另一个 Net 服务名 10. 选择“否”,单击“下一步”按钮,出现如图 1-25 所示的窗口,提示“Net 服务名 配置完毕!”。 图 1-25 服务名配置完毕 11. 单击“下一步”按钮,在出现的窗口中单击“完成”,结束本地服务名的配置。 3.3 启动 Oracle 服务 1. 在 Windows 的“开始”菜单中依次选择:“设置”“控制面板”,在打开的窗口中选 择“管理工具”,在双击打开后的窗口中选择“服务”,双击打开,出现“服务”窗口,查看 专题一 Oracle 入门 翰子昂系列理论教材 - 17 - 其中以“Oracle”开头的服务,如图 1-26 所示。 图 1-26 Oracle 服务 2. 如果图 1-26 中的以下几项服务 OracleServiceORCL、OracleOraDb10g_home1TNSList- ener、OracleOraDb10g_home1ISQL*Plus 和 OracleDBConsoleorcl 的状态不是“已启动”,则 选中该项服务,在单击右键后出现的菜单中选择“启动”,以启动该项服务;在某项服务的 右键菜单中选择“属性”,可打开“属性”窗口,在其“常规”选项中可以设置该项服务的 “启动类型”为“自动”、“手动”或“禁用”;完成后关闭窗口退出。 注意: 在实际应用中,并非所有这些 Oracle 服务都必须启动,每项服务的功能 请参见相关理论知识部分。 理论知识: Windows 中的 Oracle 服务 Oracle 数据库作为一项服务呈现给客户,即数据库执行客户端提交的任务。在 Wind- ows 中 Oracle 的每个实例都作为一项服务来启动。服务是在 Windows 注册表中注册的可 执行过程,由 Windows 操作系统管理。 Oracle 服务可以手动启动,也可配置为在计算机启动时自动启动,无需用户干预,从 而简化数据库的启动过程。 Oracle 服务的名称通常是一个包含全局数据库名称和 OracleHOME 名的字符串。 常用的 Oracle 服务有: 1. OracleHOME_NAMETNSListener 服务 如图 1-26 中的 OracleOraDb10g_home1TNSListener 服务。此服务是 Oracle 的 监听程序。要连接到数据库服务器,客户端必须先连接到驻留在数据库服务器上的监听进程。 监听器接收从客户端发出的请求,然后将请求传递给数据库服务器。一旦建立了连接,客户 端和数据库服务器就可以直接通信了。 监听器监听并接受来自客户端的连接请求。若监听器未启动,客户端将无法连接到数据 库服务器。 2. OracleServiceSID 服务 如图 1-26 中的 OracleServiceORCL 服务。此实例是为名为 SID(系统标识符)的 数 Oracle 数据库案例教程_教师用书 - 18 - 翰子昂系列理论教材 据库实例创建的。Oracle 实例由一个系统标识符 SID 唯一地标识,以区别于此计算机上的 其它任何实例。每次新创建一个数据库,系统会自动为该数据库的实例创建一个服务。如果 此服务未启动,数据库客户端应用程序连接到数据库服务器时就会出现错误。 3. OracleHOME_NAMEiSQL*Plus 服务 如图 1-26 中的 OracleOraDb10g_home1iSQL*Plus 服务。要使用 iSQL*Plus,必 须启动该服务。 4. OracleDBConsoleOracle_SID 服务 如图 1-26 中的 OracleDBConsoleorcl 服务。要使用企业管理器必须启动该服务。 每次新创建一个数据库,也会新创建一个此项服务。 3.4 认识 Oracle 常用工具 3.4.1 企业管理器 1. 打开浏览器,在地址栏中输入安装过程中图 1-12 中以“em”结尾的 URL 地址:“http: //houtony:1158/em”,出现如图 1-27 所示的登录界面(如果是第一次登陆企业管理器,会出现 “Oracle Database 10g 许可授予信息”的网页,点击网页右下角的“我接受”按钮,即可进 入到图 1-27 所示的界面)。 图 1-27 企业管理器登录 2. 输入的用户名和口令,例如“SYS”和“SYS”,选择连接身份为“SYSDBA”,点 击“登录”,进入 Oracle 企业管理器的主界面,如图 1-28 所示。 专题一 Oracle 入门 翰子昂系列理论教材 - 19 - 图 1-28 企业管理器主界面 3. 分别点击“主目录”、“性能”、“管理”、“维护”四个主菜单,查看企业管理器的主要 功能。 理论知识: Oracle 企业管理器(Oracle Enterprise Manager),简称 OEM,从 10g 开始, 可以用浏览器的方式来访问企业管理器。它是 Oracle 的集成管理平台,能够管理整个 Or- acle 环境,让用户可以以可视化的方式完成管理数据库对象、监视服务器的实时性能、对数 据库进行备份和恢复、完成作业系统等一系列的功能。 在 Oracle 10g 安装完成后,开始时只有 SYS 和 SYSTEM 用户才能登录到 OEM,且 S- YS 用户只能以 SYSDBA 或 SYSOPER 身份,SYSTEM 用户只能以 NORMAL 身份,其他用户必 须经过相应的授权后才能登录。 3.4.2 SQL*Plus 工具 1. 在 Windows 的开始菜单中选择“运行”,在“打开”文本框中输入“cmd”后,点击 “确定”按钮,进入到命令控制台状态。 2. 在 Dos 提示符下输入命令“ sqlplus /nolog”,按回车后可进入到字符界面的 SQL*Plus, 如图 1-29 所示。 Oracle 数据库案例教程_教师用书 - 20 - 翰子昂系列理论教材 图 1-29 进入到字符界面的 SQL*Plus 3. 在 SQL 提示符下输入命令“conn scott/tiger;”,按回车键系统提示“已连接”后, 再输入命令“SELECT * FROM dept;”,再按回车,执行结果如图 1-30 所示。 图 1-30 在字符界面的 SQL*Plus 中执行查询命令 4. 在 SQL 提示符下输入“exit”命令,按回车后退出 SQL*Plus,回到 Dos 状态。 5. 在 Dos 提示符下输入“sqlplusw”后,按回车,或者在 Windows 的开始菜单中依次选 择“程序”“Oracle—OraDb10g_home1”“应用程序开发”“SQL*Plus”,都会弹出 如图 1-31 所示的图形界面的 SQL*Plus 登录界面。 专题一 Oracle 入门 翰子昂系列理论教材 - 21 - 图 1-31 图形界面的 SQL*Plus 登录 6. 输入用户名“SCOTT”和口令“TIGER”,在主机字符串文本框中输入本地服务名 “MYSERVER”,点击“确定”,可进入到图形界面的 SQL*Plus 的 SQL 提示符状态。 7. 在 SQL 提示符下输入命令“SELECT * FROM dept;”按回车后,运行结果如图 1-32 所示。 图 1-32 图形界面的 SQL*Plus 中执行查询 理论知识: SQL*Plus 是 Oracle 最常用的工具之一,可用于接受和执行 SQL 语句和 PL/SQL 块。 它有两种界面:字符界面和图形界面。使用 SQL*Plus 可以完成打开和关闭数据库、建立与 数据库的连接、查看帮助信息、生成简单报表,以格式化的形式输出查询结果、查询数据字 Oracle 数据库案例教程_教师用书 - 22 - 翰子昂系列理论教材 典、向用户提示信息并接受用户输入等数据库操作。 3.4.3 iSQL*Plus 工具 1. 在浏览器地址栏中输入安装过程中图 1-12 所示的以“isqlplus”结尾的 URL 地址: “http://houtony:5560/isqlplus”,进入到如图 1-33 所示的 iSQL*Plus 的登录页面。 图 1-33 iSQL*Plus 登录 2. 输入用户名“SCOTT”和口令“TIGER”,在连接标识符文本框中输入图 1-33 中的本 地服务名“MYSERVER”,点击“登录”,进入到如图 1-34 所示的 iSQL*Plus 主页面。 专题一 Oracle 入门 翰子昂系列理论教材 - 23 - 图 1-34 iSQL*Plus 主页面 3. 在文本框中输入“SELECT * FROM dept;”,点击“执行”,会查询出 dept 表的信 息并显示在页面下部,如图 1-35 所示。 图 1-35 iSQL*Plus 查询结果 Oracle 数据库案例教程_教师用书 - 24 - 翰子昂系列理论教材 4. 关闭浏览器。 理论知识: iSQL*Plus 也是 Oracle 的常用工具,可以用来完成几乎所有 SQL*Plus 能完成的任 务。该工具的优势在于能通过浏览器访问,还可以自动将查询的结果格式化为简单报表。 3.5 创建数据库和表空间 数据库和表空间是 Oracle 体系结构的重要组件,创建步骤如下: 理论知识: Oracle 的体系结构 数据库的体系结构是指数据库的组成、工作过程与原理,以及数据在数据库中的组织和 管理机制。 Oracle 服务器 Oracle 服务器由 Oracle 数据库和 Oracle 实例组成。Oracle 数据库是一个数据的 集合,它在物理上是由一系列的文件组成,在逻辑上是由一系列的逻辑组件构成的。Orac- le 实例是后台进程与内存结构的集合。 Oracle 组件概述 Oracle 体系结构中包含一系列组件,在图 1-36 中列出了 Oracle 中的主要组件。下 面分别介绍这些主要组件及 Oracle 中的一些重要概念。 图 1-36 Oracle 体系结构的主要组件 专题一 Oracle 入门 翰子昂系列理论教材 - 25 - 1. 实例 后台进程与内存结构的集合称为 Oracle 实例。如果要访问数据库中的数据,就必须启 动一个实例,也就是说,只有通过实例才能访问到数据库中的数据。实例启动时将分配一个 系统全局区(SGA)并启动一系列的后台进程。在任何时候,一个实例只能打开并使用一个 数据库。反之,一个数据库可以同时被多个实例打开。 2. 会话 会话是用户与 Oracle 服务器的单个连接。当用户与服务器建立连接时创建会话。而当 用户与服务器断开连接时关闭会话。当一个数据库用户同时用多个不同的应用程序或从多个 终端连接服务器时,则为该用户创建多个并行会话。 3. 内存 Oracle 的内存结构中包括以下两个主要的内存区域:  系统全局区(SGA):实例启动时分配该内存区,是 Oracle 实例的一个基本组件。  程序全局区(PGA):服务器进程启动时分配该内存区。PGA(Program Globe Area)是在用户进程连接到数据库并创建一个会话时自动分配的,该区内保留每个 与 Oracle 数据库连接的用户进程所需的内存。PGA 为非共享区,只能由单个进程 使用,当一个会话结束后,PGA 释放。 SGA(System Globe Area),又称共享全局区,它用来存储数据库信息,并由多个 数据库进程共享。当数据库实例启动时,SGA 的内存被自动分配。SGA 是数据库中占用服务 器内存最大的一个区域,同时也是影响数据库性能的一个重要指标。 SGA 按其作用不同,可分为共享池、数据缓冲区、日志缓冲区等。 共享池:共享池是对 SQL、PL/SQL 程序进行语法分析、编译和执行的内存区域,它由 库缓存和数据字典缓存组成,其中,库缓存含有最近执行的 SQL、PL/SQL 语句的信息,数 据字典缓存中含有从数据字典中得到的表、索引、字段和权限等信息。如果共享池太小,则 运行 SQL、PL/SQL 程序所需的时间就会较长,数据库的性能就会受到影响。 当用户执行一个查询语句时,Oracle 系统首先在数据字典缓存中查看要查询的表、字 段等在数据库中是否存在,用户是否有相应权限,如果有再在库缓存中查找是否存在该语句 的信息,如果存在则直接执行,如果不存在再对该查询语句进行编译和执行。 数据缓冲区:数据缓冲区用于存储从磁盘数据文件中读入的数据,由所有用户共享。数 据被修改时,首先要从数据文件中取出,存储于数据缓冲区中,修改的数据、插入的数据都 被存储于数据缓冲区,当修改完成或满足其它条件时,数据才被写入到数据文件中。 Oracle 服务器进程在处理一个查询时,首先查找数据缓冲区中是否存在所需的数据块。 如果没有找到,服务器进程才会去从数据文件中读取信息,并保存到数据缓冲区中。当以后 再有进程要读取这些块时,就不用再从数据文件中读取,而是直接从数据缓冲区中读了,这 样就提高了读取速度。因此,数据缓冲区的大小对数据库的读取速度有直接影响。 日志缓冲区:所有对数据库的修改先记录到日志缓冲区,当缓冲区中的数据达到一定数 量时,再由日志写入进程 LGWR 把日志数据写入到日志文件中。数据更改可能来自 INSERT、 UPDATE、DELETE、CREATE、ALTER 和 DROP 等操作。相对于数据缓冲区,日志缓冲区对 数据库性能的影响较小。 Oracle 数据库案例教程_教师用书 - 26 - 翰子昂系列理论教材 4. 进程 在 Oracle 的体系结构中主要有以下几种进程: 用户进程:当数据库用户请求连接服务器时启动。当数据库用户运行一个应用程序准备 向数据库服务器发送请求时,即创建了用户进程,如用户启动 SQL*Plus 时,系统自动建立 了一个用户进程。用户进程不能直接与数据库交互,而必须借助于服务器进程。 服务器进程:在 Oracle 实例启动时启动。它用于处理连接到该实例的用户进程的请求, 当用户建立与数据库的连接时,即产生服务器进程。服务器进程和用户进程通信并为所连接 的用户请求服务。服务器进程直接与 Oracle 数据库交互,实现调用和返回结果。服务器进 程可以仅处理一个用户进程的请求,也可以处理多个用户进程的请求。 后台进程:在 Oracle 数据库中,为了使系统性能更好和协调多个用户,实例系统中使 用了一些附加进程,被称为后台进程。这些后台进程存在于操作系统中,在实例启动时自动 启动。Oracle 常用的后台进程有:  进程监控(PMON):主要作用是在用户进程出现故障时执行进程恢复。  系统监控(SMON):主要完成以下任务:在实例启动时执行实例恢复;整理数据文 件的自由空间;释放不再使用的临时段。  数据写入进程(DBWR):执行下列任务:管理数据缓冲区,以便用户进程能找到空 闲的缓冲区;将所有修改了的缓冲区的数据写入数据文件;使用 LRU(最近最少使 用)算法将最近使用过的块保留在内存中;通过延迟写来优化磁盘 I/O 读写。  日志写入进程(LGWR):此后台进程负责将日志缓冲区的数据写入日志文件。数据 库正在运行时,如果对数据进行修改,则产生日志信息,日志信息首先产生于日志 缓冲区中。此缓冲区按照“先进先出”的原则进行操作,当日志信息达到一定数量 时,由 LGWR 进程将日志数据写入到日志文件。  检查点(CKPT):保 证 所有修改过的数据库缓冲区内的数据都被写入到数据库文件, 在给定的时间内,检查点完成后,CKPT 进程更新数据文件头和控制文件,保存检查 点信息,以保证数据库的同步。这样做主要是为了在数据库恢复时只需要提供从上 一个检查点以来的修改,确定开始恢复数据的位置,即称为检查点。  归档进程(ARCH):当数据库运行在归档日志方式时,才会启动该进程。在日志写 满时将日志信息写到磁盘或磁带,用于磁盘故障时的数据库恢复。在一个日志文件 写满以后,Oracle 服务器就开始将数据写入到下一个日志文件,此过程被称为日 志切换。Oracle 数据库有两种运行方式,即归档日志方式和非归档日志方式。在 非归档日志方式下,日志切换时直接覆盖以前的文件,不产生归档日志。在归档日 志方式下,在日志切换之前,ARCH 进程会对已写满的日志文件进行存档。 Oracle 数据库的物理组件和逻辑组件 Oracle 数据库的物理文件主要有三类:数据文件、控制文件和日志文件。其它还有归 档日志文件、参数文件和口令文件等。 数据文件:是用于存储数据库数据的文件,如表、索引数据等都存储在数据文件中。每 个 Oracle 数据库都有一个或多个数据文件(10g 中默认有 5 个),一个数据文件只能与一 个数据库相关联。 专题一 Oracle 入门 翰子昂系列理论教材 - 27 - 控制文件:是记录数据库物理结构的二进制文件,Oracle 数据库根据它来查找物理文 件的位置,它包含维护和验证数据库完整性的必要信息。每个 Oracle 数据库都有一个或多 个控制文件(10g 中默认有 3 个)。 日志文件:又 被称为联机日志文件或重做日志文件,用于记录对数据库进行的修改信息, 对数据库所做的全部修改都被记录到日志中。每个 Oracle 数据库都有一个或多个日志文件 (10g 中默认有 3 个)。日志文件主要用于在数据库出现故障时实施数据恢复。 2. 逻辑组件 从逻辑的角度来分析,Oracle 数据库的逻辑结构主要包括表空间、段、区、数据块和 模式等。它们的组成关系如图 1-37 所示。 图 1-37 数据库的逻辑结构 表空间(TABLESPACE):数据库可以划分为一个或多个逻辑单位,该逻辑单位被称为 表空间,它是数所库中最大的逻辑单位。每个表空间由一个或多个数据文件组成,一个数据 文件只能与一个表空间关联,这是逻辑上和物理上的统一。数据库管理员可以创建若干个表 空间,创建表空间时可以指定数据文件及其要分配的磁盘空间的大小。 在每个数据库中都有一个名为 SYSTEM 的表空间,即系统表空间,它在创建数据库或安 装数据库时自动创建的,用于存储系统的数据字典表、系统程序单元、过程、函数、包和触 发器等,也可以用于存储用户表、索引等对象。 段(SEGENT):存在于表空间中,是包含于表空间中的一种指定类型的逻辑存储结构, 由一组区组成。按照段中所存数据的特征以及优化系统性能的需要,将段分为 4 类:数据段、 索引段、回退段、临时段。 区(EXTENT):是磁盘空间分配的最小单位。磁盘按区划分,每次至少分配一个区。区 为段分配空间,它由连续的数据块组成。当段创建时,它至少包含一个区。当段中所有空间 已完全使用时,系统自动为该段分配一个新区。区不能跨数据文件存在,只能存在于一个数 据文件中。 数据块(DATA BLOCK):是数据库中最小的数据组织单位与管理单位,是 Oracle 服 务器所能分配、读取或写入的最小存储单元。Oracle 服务器以数据块为单位管理数据文件 的存储空间。数据块的取值范围在 2KB~4KB 之间,10g 中默认大小是 8K。 模式(SCHEMA):是对用户所创建的数据库对象的总称,在 Oracle 中任何数据库对象 都属于一个特定用户,一个用户及其所拥有的对象称为模式。一个用户与相同名称的模式相 关联,因此,模式又称为用户模式。 1. 在 Windows 的开始菜单中依次选择“程序”“Oracle—OraDb10g_home1”“配 置和移植工具”“Database Configuration Assistant”,将弹出如图 1-38 所示的窗口(直接 在 Dos 中执行命令“dbca”,也可进入到该窗口)。 Oracle 数据库案例教程_教师用书 - 28 - 翰子昂系列理论教材 图 1-38 选择操作 2. 选择“创建数据库”,点击“下一步”,出现如图 1-39 所示的“数据库模板”窗口。 图 1-39 数据库模板 3. 选择“一般用途”的数据库模板,点击“下一步”,出现如图 1-40 所示的“数据库标 识”窗口。 专题一 Oracle 入门 翰子昂系列理论教材 - 29 - 图 1-40 数据库标识 4. 输入全局数据库名如“MYDB”,在 SID 文本框中自动出现的名称与全局数据库名相 同,也可以更改 SID 名称使其不同,点击“下一步”,出现如图 1-41 所示的“管理选项”窗 口。 图 1-41 管理选项 5. 选择“使用 Enterprise Manager 配置数据库”,点击“下一步”,出现如图 1-42 所示 的“数据库身份证明”窗口。 Oracle 数据库案例教程_教师用书 - 30 - 翰子昂系列理论教材 图 1-42 数据库身份证明 6. 选择“所有帐户使用同一口令”或“使用不同的口令”,输入口令和确认口令后,点 击“下一步”,出现如图 1-43 所示的“网络配置”窗口。 图 1-43 网络配置 专题一 Oracle 入门 翰子昂系列理论教材 - 31 - 7. 选择“将此数据库注册到所有监听程序”后点击“完成”,出现如图 1-44 所示的窗口, 让用户确认要创建的数据库的详细资料。 图 1-44 操作确认 8. 点击“确定”,出现如图 1-45 所示的窗口,开始创建数据库。 图 1-45 创建数据库 Oracle 数据库案例教程_教师用书 - 32 - 翰子昂系列理论教材 9. 数据库创建完成后,会弹出如图 1-46 所示的窗口,向用户提示数据库创建完成的信息, 点击“退出”,完成数据库的创建。 图 1-46 数据库创建完成 10. 打开 SQL*Plus,在 SQL 提示符下,输入“CONN SYS/SYS@MYDB AS SYSDBA” 命令,以 SYSDBA 身份登录到新创建的数据库,再输入以下的命令创建表空间: CREATE TABLESPACE mytablespace DATAFILE 'D:\ORACLE\PRODUCT\10.2.0\ORADATA\MYDB\MYTABLESPACE.DBF' SIZE 10M AUTOEXTEND ON; 理论知识: 创建表空间语法如下: CREATE TABLESPACE tablespace_name DATAFILE file_name [SIZE int_num [K|M ]] [AUTOEXTEND [ON|OFF]]; 其中:tablespace_name 是要创建的表空间的名称,DATAFILE 指定组成表空间的一 个或多个数据文件(当有多个数据文件时使用逗号分隔),file_name 是数据文件的路径及 名称,SIZE 指定文件的大小,AUTOEXTEND 子句用来启用或禁用数据文件的自动扩展。 11. 系统提示“表空间已创建”后,打开“我的电脑”,找到上面创建表空间的路径, 可以看到新创建的“mytablespace.dbf”文件,在该文件夹下还有其它一些文件,分别是以“D BF”、“CTL”、“LOG”为后缀,它们是数据库的主要物理组件。 3.6 Oracle 中的用户管理 Oracle 中的用户管理包括创建用户、为用户授权、修改用户口令、删除用户等。 理论知识: 在 Oracle 数据库中,系统通过安全措施防止非法用户对数据库进行操作。因此,要连 专题一 Oracle 入门 翰子昂系列理论教材 - 33 - 接到 Oracle,就需要一个用户帐号。数据库管理员负责管理允许访问数据库的用户帐号, 包括创建、授权、删除等。 在安装数据库时,Oracle 将创建一些默认的数据库用户模式,如 SYS、SYSTEM、SY- SMAN、DBSNMP 和 SCOTT 等。下面先简单介绍一下 SYS、SYSTEM 和 SCOTT 用户。 SYS 用户是 Oracle 中的一个超级用户。数据库中所有的数据字典和视图都存储在 SY- S 模式中。数据字典存储了用来管理数据库对象的所有信息,是 Oracle 数据库中非常重要 的系统信息。SYS 用户主要用来维护系统信息和管理实例,SYS 用户只能以 SYSOPER 或 S- YSDBA 角色登陆。 SYSTEM 用户是 Oracle 默认的系统管理员,它拥有 DBA 权限。该用户拥有 Oracle 管 理工具使用的内部表和视图。通常通过 SYSTEM 用户管理 Oracle 数据库的用户、权限和存 储等。不建议在 SYSTEM 用户模式中创建用户表。 SYS 和 SYSTEM 用户都是 Oracle 系统用户,它们使用 SYSTEM 表空间存储模式对象。 SCOTT 用户是 Oracle 数据库的一个示范用户。在数据库安装时创建(在 10g 中默认情 况下,该用户是被锁定的,可在安装时解锁)。SCOTT 用户模式下有四个示范表,其中有两 个是 dept(部门表)和 emp(员工表),在本书后面的许多示例中要用到。如果安装时不更 改其口令,其默认口令是 TIGER。 1. 以 SYSDBA 身份登录到 SQL*Plus 的 SQL 提示符状态,输入以下的命令创建用户: CREATE USER user1 IDENTIFIED BY pwd1 DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP; 理论知识: 在 Oracle 中可以用 CREATE USER 命令来创建新用户。每个用户都有一个默认表空间 和一个临时表空间,在创建时可以为它们指定,如果不指定,Oracle 就把 SYSTEM 设为默 认表空间,TEMP 设为临时表空间。 创建新用户的语法如下: CREATE USER user_name IDENTIFIED BY password [DEFAULT TABLESPACE tablespace_name1] [TEMPORARY TABLESPACE tablespace_name2]; 2. 按回车后系统提示“用户已创建”,表明创建用户成功。该用户的用户名是“user1”, 口令是“pwd1”,默认表空间是“USERS”,临时表空间是“TEMP”。 3. 在 SQL 提示符下,输入以下的命令,为用户 user1 授予 CONNECT 权限。 GRANT CONNECT TO user1; 理论知识: 权限指的是用户执行特定类型的 SQL 命令或访问其他对象的权利。如连接数据库、创建 表、执行过程等都是一些权限。若没有任何权限,新创建的用户将无法登录到数据库。 Oracle 数据库案例教程_教师用书 - 34 - 翰子昂系列理论教材 Oracle 用户权限有两种类型:系统权限和对象权限。  系统权限是指允许用户执行某些数据库操作。如创建表空间就是一个系统权限。  对象权限指允许对某一特定对象(如表、视图等)执行特定操作。用户必须是对象 的所有者或者是已经拥有了 GRANT OPTION 对象权限才能为其他用户授予对象权 限。 为用户授权的语法为: GRANT popedom_name [ON object_name] TO user_name; 其中:popedom_name 为权限名或角色名,object_name 为对象名,user_name 为 用户名。 4. 在 SQL 提示符下输入“CONN SCOTT/TIGER”,以 SCOTT 用户身份登录后,继续输 入下面的授权命令,为用户 user1 授予查看表 dept 的权限,user1 同时还拥有了把此查看权限 授予其他用户的权限。 GRANT SELECT ON emp TO user1 WITH GRANT OPTION; 5. 再输入“CONN/AS SYSDBA”命令以 SYSDBA 身份登录后,在 SQL 提示符下输入 以下命令,修改用户 user1 的口令。 ALTER USER user1 IDENTIFIED BY pwd2; 理论知识: Oracle 中用 ALTER USER 命令修改用户口令,语法如下: ALTER USER user_name IDENTIFIED BY new_password; 6. 系统提示“用户已更改”,表明口令修改成功。在 SQL 提示符下输入下面的命令来删 除用户 user1。 DROP USER user1 CASCADE; 理论知识: Oracle 中的 DROP USER 命令用于删除用户。当删除一个用户时,如果该用户模式中包 含模式对象时,必须使用 CASCADE 选项以删除模式对象。 删除用户的语法为: DROP USER user_name [CASCADE]; 7. 系统提示“用户已删除”,表明用户 user1 删除成功。 4. 实验 按照相关实践知识部分依次练习: 专题一 Oracle 入门 翰子昂系列理论教材 - 35 - 1. 配置 Oracle 客户端服务名,参见 3.2(20 分钟)。 2. 查看 Oracle 服务,参见 3.3(10 分钟)。 3. 认识 Oracle 常用工具,参见 3.4(20 分钟)。 4. 创建数据库和表空间,参见 3.5(20 分钟)。 5. 用户的创建、修改口令和删除以用及为用户授权,参见 3.6(20 分钟)。 5. 课后作业 1. 分别用 SQL*Plus 和 iSQL*Plus 查看 SCOTT 用户模式下 emp 表的内容。 2. 新建一个数据库 TESTDB,并在该数据库中新建一个表空间 testspace。 3. 先新建一个用户 testuser,口令为“test”,然后赋予他 CONNECT 权限,再修改其口 令为“testuser”,最后删除该用户。 Oracle 数据库案例教程_教师用书 - 36 - 翰子昂系列理论教材 专题二 锁和表分区 1. 教学目标 1.1 理解锁定的概念 1.2 会使用锁和时间戳机制来避免数据并发时造成的数据不一致 1.3 理解和使用表分区 2. 工作任务 2.1 用锁和时间戳来保证并发操作时数据的一致性 2.2 使用表级锁 2.2 使用表分区,创建分区表 3. 相关实践知识 3.1 使用行级锁和时间戳来保证数据完整性 3.1.1 两个会话同时修改某一员工工资 从开始菜单中打开 SQL*Plus 工具,以 SCOTT 用户的身份登录到数据库;然后再重新从 开始菜单打开 SQL*Plus 工具,仍以 SCOTT 用户的身份登录到数据库。这样就创建了两个会 话。 雇员编号为 7934 的雇员,由于工作业绩表现好,工资需要在现工资 1300 元的基础上加 500 元,作为新的工资。人事部门把通知下发到财务处,在财务处正好有两个人员处理有关 工资变动的工作。这两个财务人员操作如下: 1. 在第一个会话中的 SQL 提示符下,输入以下代码: SELECT sal FROM EMP WHERE empno=7934; 按回车键,系统显示当前工资为 1300 元。 2. 在第二个会话的 SQL 提示符下,输入以下代码: SELECT sal FROM emp WHERE empno=7934; 按回车键,系统显示当前工资为 1300 元。 3. 在第一个会话中,第一个财务人员执行修改工资操作,输入代码如下: 专题二 锁和表分区 翰子昂系列理论教材 - 37 - UPDATE empo SET sal=sal+500 WHERE empno=7934; COMMIT; 系统显示修改成功。 4. 在第二个会话中,由于不知道另一个财务人员已经修改了工资,而且由于它的查询是 在修改之前,所以他以为工资没有修改,因此,他也执行修改工资操作,输入代码如下: UPDATE empo SET sal=sal+500 WHERE empno=7934; COMMIT; 系统显示修改成功。 5. 上面这种情况下,雇员编号为 7934 的雇员,基本工资已经变成了 1300+500+500=230 0 元,这会对公司造成损失。 大家考虑,凡是涉及到多个用户同时修改一个数据时,就会出现类似上面的问题,对于 这种问题,我们需要通过锁或时间戳来处理。 理论知识: 当多个事务并发的情况下,会造成数据的不一致,主要有如下几个问题: 丢失更新:当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生 丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更 新,这将导致前一个修改被后一个修改覆盖,从而出现丢失更新的问题。 例如,两个编辑人员打开了同一电子文档。每个编辑人员独立地更改其复本,然后提交 更改后的复本,这样就覆盖了原始文档。最后保存其更改复本的编辑人员覆盖了第一个编辑 人员所做的更改。如果在第一个编辑人员完成之后第二个编辑人员才能进行更改,则可以避 问题。 脏读:当第二个事务选择其它事务正在更新的行时,会发生脏读的问题。第二个事务正 在读取的数据还没有确认并且可能由更新此行的事务所再次更改或回滚。 非重复读:当第二个事务多次访问同一行而且每次读取不同的数据时,会发生非重复读 问题。非重复读与脏读类似,因为其它事务也是正在更改第二个事务正在读取的数据。 幻像读:当对某行执行插入或删除操作,而该行属于某个事务正在读取的行的范围时, 会发生幻像读问题。事务第一次读的行范围显示出其中一行已不复存在于第二次读或后续读 中,因为该行己被其它事务删除。同样,由于其它事务的插入操作,事务的第二次或后续读 显示有一行己不存在于原始读中。 3.1.2 使用行级锁来保护数据的一致性 1. 在第一个会话中的 SQL 提示符下,输入以下代码: SELECT sal FROM emp WHERE empno=7934 FOR UPDATE; 按回车键,系统显示当前工资为 1300 元。 Oracle 数据库案例教程_教师用书 - 38 - 翰子昂系列理论教材 2. 在第二个会话的 SQL 提示符下,输入以下代码: SELECT sal FROM emp WHERE empno=7934 FOR UPDATE; 按回车键,由于当前记录已经被会话一加锁,会话二等待。 3. 在第一个会话中,第一个财务人员执行修改工资操作,输入代码如下: UPDATE empo SET sal=sal+500 WHERE empno=7934; COMMIT; 系统显示修改成功。 4. 在第二个会话中,由于第一个会话通过 COMMIT 命令,释放了锁。第二个会话停止 等待,并获得锁,系统显示当前用户工资已经变成 1500,于是没有进一步操作。由于第二个 会话占有了锁,需要释放锁。在 SQL 提示符下,输入如下代码: COMMIT; 5. 通过行级锁,保证了数据的一致性,避免了 3.1.1 中出现的问题。 理论知识: 为了确保并发用户在存取同一数据库对象时的正确性(即无丢失更新、脏读、非重复读、 幻像读),数据库中引入了锁机制。基本的锁类型有两种:排它锁(Exclusive locks 记 为 X 锁)和共享锁(Share locks 记为 S 锁)。 排它锁:若事务 T 对数据 D 加 X 锁,则其它任何事务都不能再对 D 加任何类型的锁,直 至 T 释放 D 上的 X 锁;一般要求在修改数据前要向该数据加排它锁,所以排它锁又称为写锁。 共享锁:若事务 T 对数据 D 加 S 锁,则其它事务只能对 D 加 S 锁,而不能加 X 锁,直至 T 释放 D 上的 S 锁;一般要求在读取数据前要向该数据加共享锁,所以共享锁又称为读锁。 根据保护对象的不同,Oracle 数据库锁可以分为以下几大类: 1. DML 锁(data locks,数据锁):用于保护数据的完整性; 2. DDL 锁(dictionary locks,字典锁):用于保护数据库对象的结构(例如表、 视图、索引的结构定义); 3. 内部锁与闩(internal locks 和 latches):保护内部数据库结构; 4. DISTRIBUTED locks(分布式锁):用于 OPS(并行服务器)中; 5. PCM locks(并行高速缓存管理锁):用于 OPS(并行服务器)中。 本文主要讨论 DML 锁。从封锁粒度(封锁对象的大小)的角度看,Oracle 中的 DML 锁 共有两个层次,即行级锁和表级锁。 Oracle 的 TX 锁(行级锁、事务锁) 许多对 Oracle 不太了解的技术人员可能会以为每一个 TX 锁代表一条被封锁的数据行, 其实不然。TX 的本义是 Transaction(事务),当一个事务第一次执行数据更改(INSE- 专题二 锁和表分区 翰子昂系列理论教材 - 39 - RT、UPDATE、DELETE)或使用 SELECT… FOR UPDATE 语句进行查询时,它即获得一个 TX(事务)锁,直至该事务结束(执行 COMMIT 或 ROLLBACK 操作)时,该锁才被释放。所 以,一个 TX 锁,可以对应多个被该事务锁定的数据行(在我们用的时候多使其一个事务,然 后 SELECT … FOR UPDATE NOWAIT)。数据行上的锁标志一旦被置位,就表明该行数据被 加 X 锁,Oracle 在数据行上没有 S 锁。 在 Oracle 的每行数据上,都有一个标志位来表示该行数据是否被锁定,不象其它一些 DBMS(数据库管理系统)那样,建立一个链表来维护每一行被加锁的数据,这样就大大减小 了行级锁的维护开销,也在很大程度上避免了其它数据库系统使用行级封锁时经常发生的锁 数量不够的情况。 3.1.3 使用时间戳来保证数据完整性 1. 在 SQL 提示符下输入以下代码,修改 emp 表的结构,为表增加一个字段 stamp,数据 类型为 TIMESTAMP。 ALTER TABLE emp ADD stamp TIMESTAMP; 2. 给新增加的字段赋值,在 SQL 提示符下,输入如下代码: UPDATE emp SET stamp=systimestamp; 通过上面两步,完成了对表 emp 结构的修改,并为时间戳赋了初值。下面我们同样模拟 两个会话同时修改员工工资,看看如何通过使用时间戳来避免由于并发带来的问题。 3 在第一个会话中的 SQL 提示符下,输入以下代码: SELECT stamp FROM emp WHERE empno=7934; 按回车键,系统显示“06-10 月-07 10.20.48.140000 上午”。 2. 在第二个会话的 SQL 提示符下,输入以下代码: SELECT stamp FROM EMP WHERE empno=7934 ; 按回车键,系统显示“06-10 月-07 10.20.48.140000 上午”。 3. 在第一个会话中,第一个财务人员执行修改工资操作,输入代码如下: UPDATE empo SET sal=sal+500,stamp=systimestamp WHERE empno=7934 AND stamp=’ 06-10 月-07 10.20.48.140000 上午’; COMMIT; 系统显示修改成功。 4. 在第二个会话中,另一个财务人员也执行相同的操作。但由于第一个财务人员已经修 Oracle 数据库案例教程_教师用书 - 40 - 翰子昂系列理论教材 改成功了记录,并更新了时间戳,所以更新没有成功,在 SQL 提示符下,输入如下代码: UPDATE empo SET sal=sal+500,stamp=systimestamp WHERE empno=7934 AND stamp=’ 06-10 月-07 10.20.48.140000 上午’; COMMIT; 系统显示“已更新 0 行”。 5. 通过时间戳的使用,保证了数据的一致性,避免了 3.1.1 中出现的问题。 理论知识: 首先为表增加一个列,不过这次这个列是采用 TIMESTAMP 型,存储数据最后更新的时 间。在 Oracle 9i 以后可以采用新的数据类型,也就是 TIMESTAMP WITH TIME ZONE 类 型来做时间戳。这种 TIMESTAMP 的数据精度在 Oracle 的时间类型中是最高的,精确到微 秒(还没与到纳秒的级别),一般来说,加上数据库处理时间和人的思考动作时间,微秒级 别是非常非常够了,其实只要精确到毫秒甚至微秒都应该没有什么问题。在更新提交的时候 检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则 OK,否则 就是版本冲突。如果不想把代码写在程序中或者由于别的原因无法把代码写在现有的程序中, 也可以把这个时间戳逻辑写在触发器或者过程中。 3.2 使用表级锁 行级锁是锁定表中的一行或多行,而表级锁锁定整个表。 1. 以 SCOTT 用户身份登录到数据库后,在 SQL 提示符下输入以下代码: LOCK TABLE emp in share mode; 按回车键,系统显示表已锁定。 2. 从开始菜单,新开一个 SQL*Plus 窗口,使用 SCOTT/tiger 登录。在 SQL 提示符下, 输入以下代码: LOCK TABLE emp in share mode; 按回车键后,系统显示表已锁定。多个会话可以对同一个表加共享锁,保证数据的安全。 3. 在第二个窗口中,在 SQL 提示符,输入以下代码: COMMIT; 按“Enter”键后,释放第二个窗口中的共享锁。 4. 在第二个窗口中,在 SQL 提示符,输入以下代码: update emp set sal=1000; 按回车键后系统没有反应,会话被阻塞。因为第一个会话已经以共享模式锁定了表,只 允许查询,不允许修改。第二个会话想修改表中的数据,执行 update 语句需要在表上加 row exclusive 锁,此锁与共享锁不兼容,只能等待。 专题二 锁和表分区 翰子昂系列理论教材 - 41 - 5. 在第一个窗口中,在 SQL 提示符,输入以下代码: COMMIT; 按回车键后,释放第一个会话对 EMP 表的共享锁。 6.在第二个会话窗口中,由于第一个会话释放了锁,第二个会话获得了相应的锁,系统 显示“已更新 14 行”。然后在 SQL 提示符下,输入以下代码: Rollback; 按回车键后,系统显示“回退已完成”。 理论知识: 意向锁的引出 表是由行组成的,当我们向某个表加锁时,一方面需要检查该锁的申请是否与原有的表 级锁相容;另一方面,还要检查该锁是否与表中的每一行上的锁相容。比如一个事务要在一 个表上加 S 锁,如果表中的一行已被另外的事务加了 X 锁,那么该锁的申请也应被阻塞。如 果表中的数据很多,逐行检查锁标志的开销将很大,系统的性能将会受到影响。为了解决这 个问题,可以在表级引入新的锁类型来表示其所属行的加锁情况,这就引出了“意向锁”的 概念。 意向锁的含义是如果对一个结点加意向锁,则说明该结点的下层结点正在被加锁;对任 一结点加锁时,必须先对它的上层结点加意向锁。如:对表中的任一行加锁时,必须先对它 所在的表加意向锁,然后再对该行加锁。这样一来,事务对表加锁时,就不再需要检查表中 每行记录的锁标志位了,系统效率得以大大提高。 意向锁的类型 由两种基本的锁类型(S 锁、X 锁),可以自然地派生出两种意向锁: 意向共享锁(Intent Share Lock,简称 IS 锁):如果要对一个数据库对象加 S 锁, 首先要对其上级结点加 IS 锁,表示它的后裔结点拟(意向)加 S 锁。 意向排它锁(Intent Exclusive Lock,简称 IX 锁):如果要对一个数据库对象加 X 锁,首先要对其上级结点加 IX 锁,表示它的后裔结点拟(意向)加 X 锁。 另外,基本的锁类型(S、X)与意向锁类型(IS、IX)之间还可以组合出新的锁类型, 理论上可以组合出 4 种,即:S+IS,S+IX,X+IS,X+IX,但稍加分析不难看出,实际上只 有 S+IX 有新的意义,其它三种组合都没有使锁的强度得到提高(即:S+IS=S,X+IS=X, X+IX=X,这里的“=”指锁的强度相同)。所谓锁的强度是指对其它锁的排斥程度。 这样我们又可以引入一种新的锁的类型: 共享意向排它锁(Shared Intent Exclusive Lock,简称 SIX 锁):如果对一个 数据库对象加 SIX 锁,表示对它加 S 锁,再加 IX 锁,即 SIX=S+IX。例如:事务对某个表 加 SIX 锁,则表示该事务要读整个表(所以要对该表加 S 锁),同时会更新个别行(所以要 对该表加 IX 锁)。 这样数据库对象上所加的锁类型就可能有 5 种:即 S、X、IS、IX、SIX。 具有意向锁的多粒度封锁方法中任意事务 T 要对一个数据库对象加锁,必须先对它的上 Oracle 数据库案例教程_教师用书 - 42 - 翰子昂系列理论教材 层结点加意向锁。申请封锁时应按自上而下的次序进行;释放封锁时则应按自下而上的次序 进行;具有意向锁的多粒度封锁方法提高了系统的并发度,减少了加锁和解锁的开销。 Oracle 的 TM 锁(表级锁) Oracle 的 DML 锁(数据锁)正是采用了上面提到的多粒度封锁方法,其行级锁虽然只 有一种(即 X 锁),但其 TM 锁(表级锁)类型共有 5 种,分别称为共享锁(S 锁)、排它 锁(X 锁)、行级共享锁(RS 锁)、行级排它锁(RX 锁)、共享行级排它锁(SRX 锁), 与上面提到的 S、X、IS、IX、SIX 相对应。需要注意的是,由于 Oracle 在行级只提供 X 锁,所以与 RS 锁(通过 SELECT … FOR UPDATE 语句获得)对应的行级锁也是 X 锁(但是 该行数据实际上还没有被修改),这与理论上的 IS 锁是有区别的。 表 2-1 为 Oracle 数据库 TM 锁的相容矩阵(Y=Yes,表示相容的请求;N=No,表示不 相容的请求;-表示没有加锁请求): T2 T1 S X RS RX SRX - S Y N Y N N Y X N N N N N Y RS Y N Y Y Y Y RX N N Y Y N Y SRX N N Y N N Y - Y Y Y Y Y Y 表 2-1 Oracle 数据库 TM 锁的相容矩阵 一方面,当 Oracle 执行 SELECT…FOR UPDATE、INSERT、UPDATE、DELETE 等 D- ML 语句时,系统自动在所要操作的表上申请表级 RS 锁(SELECT…FOR UPDATE)或 RX 锁 (INSERT、UPDATE、DELETE),当表级锁获得后,系统再自动申请 TX 锁,并将实际锁定 的数据行的锁标志位置位(指向该 TX 锁);另一方面,程序或操作人员也可以通过 LOCK TABLE 语句来指定获得某种类型的 TM 锁。表 2-2 总结了 Oracle 中各 SQL 语句产生 TM 锁 的情况: SQL 语句 表锁模式 允许的锁模式 SELECT * FROM table_name 无 RS、RX、S、SRX、X INSERT INTO table_name …… RX RS、RX UPDATE table_name …… RX RS、RX DELETE FROM table_name…… RX RS、RX SELECT * FROM table_name FOR UPDATE RS RS、RX、S、SRX LOCK TABLE table_name IN ROW SHARE MODE RS RS、RX、S、SRX LOCK TABLE table_name IN ROW EXCLUSIVE MODE RX RS、RX LOCK TABLE table_name IN SHARE MODE S RS、S LOCK TABLE table_name IN SHARE ROW EXCLUSIVE MODE SRX RS LOCK TABLE table_name IN EXCLUSIVE MODE X 无 表 2-2 Oracle 数据库 TM 锁小结 专题二 锁和表分区 翰子昂系列理论教材 - 43 - 我们可以看到,通常的 DML 操作(SELECT…FOR UPDATE、INSERT、UPDATE、DELETE), 在表级获得的只是意向锁(RS 或 RX),其真正的封锁粒度还是在行级;另外,Oracle 数据 库的一个显著特点是,在缺省情况下,单纯地读数据(SELECT)并不加锁,Oracle 通过回 滚段(Rollback segment)来保证用户不读“脏”数据。这些都极大地提高了系统的并发 程度。 由于意向锁及数据行上锁标志位的引入,极大地减小了 Oracle 维护行级锁的开销,这 些技术的应用使 Oracle 能够高效地处理高度并发的事务请求。 3.3 使用表分区 1. 在 SQL 提示符下输入以下代码,创建一个分区表,存储某公司产品的销售记录,由于 公司的销售记录很多,放在一个普通表中,会影响 DML 操作的效率,因此创建一个分区表, 根据销售日期分区。 CREATE TABLE t_sales ( product_id VARCHAR2(5), sales_date DATE NOT NULL, sales_cost NUMBER(10) ) PARTITION BY RANGE (sales_cost) ( PARTITION P1 VALUES LESS THAN (1000), PARTITION P2 VALUES LESS THAN (2000), PARTITION P3 VALUES LESS THAN (3000) ); 理论知识: 为了简化数据库大表的管理,例如在数据仓库中一般都是 TB 级的数量级。Oracle 8 以后推出了分区选项,分区将表分离在若干不同的表空间上,用分而治之的方法来支撑无限 膨胀的大表,提高大表在物理一级的可管理性。将大表分割成较小的分区可以改善表的维护、 备份、恢复、事务及查询性能。 分区的优点: 1. 增强可用性:如果表的一个分区由于系统故障而不能使用,表的其余好的分区仍可以 使用; 2. 减少关闭时间:如果系统故障只影响表的一部分分区,那么只有这部分分区需要修复, 这样能比整个大表修复花的时间更少; 3. 维护轻松:独立管理每个分区比管理单个大表要轻松得多; 4. 均衡 I/O:可以把表的不同分区分配到不同的磁盘来平衡 I/O,改善性能; 5. 改善性能:对大表的查询、增加、修改等操作可以分解到表的不同分区来并行执行, 可使运行速度更快,在数据仓库的 TP 查询中特别有用; 6. 分区对用户透明,最终用户感觉不到分区的存在。 Oracle 数据库案例教程_教师用书 - 44 - 翰子昂系列理论教材 范围分区 固名思义就是按一定范围来分区,语法为: PARTITION BY RANGE (column_name) ( PARTITION part1 VALUE LESS THAN(range1) [TABLESPACE tbs1], PARTITION part2 VALUE LESS THAN(range2) [TABLESPACE tbs2], ... [PARTITION partN VALUE LESS THAN(MAXVALUE) [TABLESPACE tbsN]] ); 相信只要对 Oracle 有点熟,就能知道上面的范围分区的意思了。 两个字段以上的范围分区大同小异,请看下面的代码: CREATE TABLE t_part ( part_id NUMBER PRIMARY KEY, part_date DATE, part_dec VARCHAR2(100) ) PARTITION BY RANGE(part_id,part_date) ( PARTITION part_01 VALUES LESS THAN(1,to_date('2006-01-01','yyyy-mm-dd')) TABLESPACE dw, PARTITION part_02 VALUES LESS THAN(10,to_date('2007-01-01','yyyy-mm-dd')) TABLESPACE dw, PARTITION part_03 VALUES LESS THAN(MAXVALUE,MAXVALUE) TABLESPACE dw ); 2. 按回车键系统提示表创建成功。这样就创建了一个范围分区的表,商品价值小于 1000 的存储在第一个分区 P1,商品价值大于等于 1000 小于 2000 的存储在 P2 分区,大于等于 2000 小于 3000 的数据存储在 P3 分区。对分区表的操作和普通表是一样的。 3. 向表中插入数据,在 SQL 提示符下,输入如下代码: INSERT INTO t_sales VALUES (‘1233’,TO_DATE(‘2007-10-1’,’yyyy-mm-dd’),800); 按回车键,系统提示“已创建 1 行”。 4. 再向表中插入一条记录,在 SQL 提示符下,输入如下代码: INSERT INTO t_sales VALUES 专题二 锁和表分区 翰子昂系列理论教材 - 45 - (‘1234’,TO_DATE(‘2006-10-1’,’yyyy-mm-dd’),1000); 按回车键,系统提示“已创建 1 行”。 请同学们思考,上面这条记录存放在哪个分区? Oracle 数据库会自动将用户插入的数据根据范围存储到合适的分区。 5. 向表中插入数据,在 SQL 提示符下,输入如下代码: COMMIT; 按回车键,系统提示“提交完成”。 6. 查询表中的数据,在 SQL 提示符下,输入如下代码: SELECT * FROM t_sales; 按回车键,系统显示如下内容: PRODU SALES_DATE SALES_COST ----- ---------- ---------- 1233 01-10 月-07 800 1234 01-10 月-06 1000 7. 查询特定分区中的数据,在 SQL 提示符下,输入如下代码: SELECT * FROM t_sales PARTITION(P2); 按回车键,系统显示如下内容: PRODU SALES_DATE SALES_COST ----- ---------- ---------- 1234 01-10 月-06 1000 8. 向表中插入数据,在 SQL 提示符下,输入如下代码: INSERT INTO t_sales VALUES (‘1235’,TO_DATE(‘2003-10-1’,’yyyy-mm-dd’),3100); 按回车键,系统提示“插入的分区关键字未映射到任何分区”。 因为根据表分区的定义,商品价值小于 3000 的存储在 P3 分区,而对于大于等于 3000 的 数据无法存储到相应的分区。 在第三个分区后面再添加一个分区是否能解决这个问题呢? 理论知识: 增加分区:指向现有的最后一个分区后增加新的分区。分区范围只能往上增,不能增加 一个小于原有分区值的分区,增加分区的语法如下: ALTER TABLE table_name Oracle 数据库案例教程_教师用书 - 46 - 翰子昂系列理论教材 ADD PARTITION partition_name VALUES LESS THAN(MAXVALUE); 9. 为 sales 表再添加一个分区,在 SQL 提示符下,输入如下代码: ALTER TABLE t_sales ADD PARTITION P4 VALUES LESS THAN (4000); 按回车键,系统提示“表已更改”。 重复步骤 8 中的插入操作,系统提示“已创建 1 行”。 如果下面要插入一个商品价值是 4090 的商品,又会出现“插入的分区关键字未映射到任 何分区”的错误。 怎样才能一劳永逸呢? 10. 为 sales 表再添加一个分区,在 SQL 提示符下,输入如下代码: ALTER TABLE t_sales ADD PARTITION P5 VALUES LESS THAN (MAXVALUE); 按回车键,系统提示“表已更改”。 在上面语句中,我们没有使用具体的值,而是使用了 Oracle 提供给的关键字“MAXVA- LUE”,从而保证了在向表中插入任何数据的时候都能成功。 11. 在 t_sales 表中,由于大部分的商品价值小于 1000,使得分区 P1 太大,从而使我们 表分区的目的不明显,需要将这个分区在 500 这个点拆分为两个分区,在 SQL 提示符下,输 入如下代码: ALTER TABLE t_sales SPLIT PARTITION P1 AT(500) INTO (PARTITION p1,PARTITION P6); 按回车键,系统提示“表已更改”。 理论知识: 拆分分区:语法如下: ALTER TABLE 表名 SPLIT PARTITION p2 AT (1500) INTO (PARTITION p21, PARTITION p22); 12. 在 t_sales 表中,由于 P3、P4 两个分区中数据较少,需要合并这两个分区。在 SQL 提示符下,输入如下代码: ALTER TABLE t_sales MERGE PARTITIONS P3,P4 INTO PARTITION P4; 专题二 锁和表分区 翰子昂系列理论教材 - 47 - 按回车键,系统提示“表已更改”。 理论知识: 合并分区:合并后的分区必须指向最后一个大 value 的分区,语法如下: ALTER TABLE table_name MERGE PARTITIONS part_01,part_02 INTO PARTITION part_02; 13. 在 t_sales 表中,由于分区 P5 中没有数据,需要删除分区。在 SQL 提示符下,输入 如下代码: ALTER TABLE t_sales DROP PARTITION P5; 按回车键,系统提示“表已更改”。 理论知识: 删除一个分区的语法为: ALTER TABLE table_name DROP PARTITION partition_name; 14. 由于范围分区可能造成分区数据不平均,从而降低了分区表的效果,因此提出了散 列分区。在 SQL 提示符下,输入如下代码,创建一个散列分区表。 CREATE TABLE t_employee ( emp_id NUMBER(4), emp_nameMP_NAME VARCHAR2(14), emp_address VARCHAR2(15), department VARCHAR2(10) ) PARTITION BY HASH (department) PARTITIONS 4; 理论知识: 散列分区是通过指定分区编号来均匀分布数据的一种分区类型,因为通过在 I/O 设备上 进行散列分区,使得这些分区大小一致。散列分区的语法如下: PARTITION BY HASH (column_name) PARTITIONS number_of_partitions [STORE IN (table_space_list)]; 或 PARTITION BY HASH (column_name) ( PARTITION part1 [TABLESPACE tbs1], PARTITION part2 [TABLESPACE tbs2], ... Oracle 数据库案例教程_教师用书 - 48 - 翰子昂系列理论教材 PARTITION partN [TABLESPACE tbsN] ); 15. 按回车键系统提示表创建成功。这样就创建了一个散列分区的表。表中的数据会根 据散列键 department 进行散列,表中的数据会均匀的散列在 4 个分区中。 16. 可以为表创建复合分区,即先按范围进行分区,然后在范围分区内部进行散列分区。 在 SQL 提示符下,输入如下代码: CREATE TABLE t_sales2 ( product_id VARCHAR2(5), sales_date DATE NOT NULL, sales_cost NUMBER(10) ) PARTITION BY RANGE (sales_date) SUBPARTITION BY HASH (product_id) SUBPARTITIONS 5 ( PARTITION S1 VALUES LESS THAN (TO_DATE(‘01/4 月/2001','DD/MON/YYYY')), PARTITION S2 VALUES LESS THAN (TO_DATE(‘01/7 月/2001','DD/MON/YYYY')), PARTITION S3 VALUES LESS THAN (TO_DATE(‘01/9 月/2001','DD/MON/YYYY')), PARTITION S4 VALUES LESS THAN (MAXVALUE) ); 该代码演示了如何创建表 t_sales2,该表会被分区为复合分区。它先按 sales_date 列创建 了 4 个范围分区,再按 product_id 创建了 5 个子分区,因此,总共会为该表创建 20 个分区。 理论知识: 根据范围分区后,每个分区内的数据再散列地分布在几个表空间中,这样我们就要使用 复合分区。复合分区是先使用范围分区,然后在每个分区同再使用散列分区的一种分区方法。 复合分区的语法如下: PARTITION BY RANGE (column_name1) SUBPARTITION BY HASH (column_name2) SUBPARTITIONS number_of_partitions ( PARTITION part1 VALUE LESS THAN(range1), PARTITION part2 VALUE LESS THAN(range2), ... PARTITION partN VALUE LESS THAN(MAXVALUE) ); 17. 此外,还可以为表创建列表分区,在 SQL 提示符下输入如下的代码: CREATE TABLE t_user 专题二 锁和表分区 翰子昂系列理论教材 - 49 - ( user_id NUMBER(5), --用户 ID user_name VARCHAR2(20), --用户名 user_pwd VARCHAR2(20), --密码 user_city VARCHAR2(20) --用户所在城市 ) PARTITION BY LIST (user_city) ( PARTITION north VALUES (‘北京’,’天津’,’长春’), PARTITION center VALUES(‘济南’,’郑州’), PARTITION south VALUES(‘广州’,’深圳’), PARTITION other VALUES (DEFAULT) ); 在上面的代码中,创建了一个用户表 t_user,它被分区为列表分区。当向表中插入记录时, Oracle 系统会自动按照记录的 user_city 的值的不同,把记录放到不同的分区中,如 user_city 的值为“ 北京”,则记录被放到 north 分区中;如 user_city 的值为“ 广州”,则记录被放到 south 分区中;如 user_city 的值为“成都”,因为前面的列表中不存在该值,则该记录被放到分区 other 中。 理论知识: 列表分区允许用户将不相关的数据组织在一起。列表分区的语法如下: PARTITION BY LIST (column_name) ( PARTITION part1 VALUES (values_list1), PARTITION part2 VALUES (values_list2), ... PARTITION partN VALUES (DEFAULT) ); 其中:values_list 是对应的分区键值的列表。DEFAULT 关键字指允许存储前面的分 区不能存储的记录。 4. 提高 封锁机制的监控 1. v$lock 视图 v$lock 视图列出当前系统持有的或正在申请的所有锁的情况,其主要字段的说明如表 2- 3 所示: 字段名称 类型 说明 SID NUMBER 会话(SESSION)标识 Oracle 数据库案例教程_教师用书 - 50 - 翰子昂系列理论教材 字段名称 类型 说明 TYPE VARCHAR(2) 区分该锁保护对象的类型 ID1 NUMBER 锁标识 1 ID2 NUMBER 锁标识 2 LMODE NUMBER 锁模式:0-None; 1-null; 2-row share;3-row exclusive;4 -share;5-share row exclusive;6-exclusive REQUEST NUMBER 申请的锁模式:具体值同上面的 LMODE CTIME NUMBER 已持有或等待锁的时间 BLOCK NUMBER 是否阻塞其它锁申请 表 2-3 v$lock 视图主要字段说明 其中在 TYPE 字段的取值中,本文只关心 TM、TX 两种 DML 锁类型; 2. v$locked_object 视图 v$locked_object 视图列出当前系统中哪些对象正被锁定,其主要字段的说明如表 2-4 所 示: 字段名称 类型 说明 XIDUSN NUMBER 回滚段号 XIDSLOT NUMBER 槽号 XIDSQN NUMBER 序列号 OBJECT_ID NUMBER 被锁对象标识 SESSION_ID NUMBER 持有锁的会话(SESSION)标识 Oracle_USERNAME VARCHAR2(30) 持有该锁的用户的 Oracle 用户名 OS_USER_NAME VARCHAR2(15) 持有该锁的用户的操作系统用户名 PROCESS VARCHAR2(9) 操作系统的进程号 LOCKED_MODE NUMBER 锁模式,取值同表三中的 LMODE 表 2-4 v$locked_object 视图中各字段取值说明 5. 实验 按照第 3 部分相关实践知识及提高部分依次练习,主要掌握: 1. 通过使用行级锁和时间戳来保证数据一致性,参见 3.1(30 分钟)。 2. 表级锁(共享锁、排它锁),参见 3.2(20 分钟)。 3. 创建分区表,参见 3.3(25 分钟)。 4. 查看数据库中被锁定的对象,参见提高部分(15 分钟)。 6. 课后作业 以 SCOTT 身份登陆到 Oracle 数据库后完成以下作业: 1. 如何防止用户使用 SELECT … FOR UPDATE 锁定要更新的行时进入无限期的等 专题二 锁和表分区 翰子昂系列理论教材 - 51 - 待?限制用户的最大等待时间为 10 秒,并进行测试。 2. 创建一个名为 t_stock 的表,其中包括 stock_id,stock_date,cost 列。根据 stock_date 列为该表创建 3 个范围分区,然后根据 cost 将表分为 8 个散列分区,共创建 24 个分区。 Oracle 数据库案例教程_教师用书 - 52 - 翰子昂系列理论教材 专题三 SQL 语句和 SQL 函数 教学目标 了解 Oracle 数据类型 掌握数据定义语言和数据操纵语言 了解事务控制语言和数据控制语言 掌握 SQL 操作符和常用的 SQL 函数 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 53 - 案例一 Oracle 数据库中的 DDL、DML 和 DCL 1. 教学目标 1.1 了解 Oracle 数据类型 1.2 掌握数据定义语言和数据操纵语言 1.3 掌握数据控制语言和事务控制语言 2. 工作任务 使用 Oracle 数据库中的 DDL、DML、DCL 和 TCL 语句来操作数据库对象。 3. 相关实践知识 在 Oracle 数据库中有学生成绩管理系统,系统主要由下面两张表组成: 表名 tbstudent 作用 存储学生信息 主键 Sno 列名 数据类型 长度 是否允许为空 字段说明 SName VARCHAR2 10 否 学生姓名 Sno VARCHAR2 10 否 学号 SBirthday DATE 是 学生生日 SSex NUMBER 1 是 性别(0 表示女,1 表示男) 表 3-1 表 tbstudent 的结构 表名 tbscore 作用 存储学生各科考试成绩 联合主键 Sno,Subject 列名 数据类型 长度 是否允许为空 字段说明 Sno VARCHAR2 10 否 学号 Subject VARCHAR2 10 否 考试科目 Score NUMBER 是 考试成绩 表 3-2 表 tbscore 的结构 两张表中存在如下记录: 表 tbstudent: Sno Sname SBirthday SSex Y20101 张一某 1982-12-15 男 Y20102 王强 1983-9-21 男 Y20103 赵敏 1984-5-8 女 Y20104 李斯 1982-3-9 男 表 3-3 表 tbstudent 的记录 Oracle 数据库案例教程_教师用书 - 54 - 翰子昂系列理论教材 表 tbscore: Sno Subject Score Y20101 XML 88 Y20102 XML 66 Y20103 XML 55 Y20104 XML 78 Y20101 Oracle 64 Y20102 Oracle 58 Y20103 Oracle 44 表 3-4 表 tbscore 的记录 从开始菜单中打开 SQL*Plus 工具,以 SCOTT 用户的身份登录到数据库。 1. 在 SQL 提示符下,输入以下代码用来创建表 tbstudent。 CREATE TABLE tbstudent ( SName VARCHAR2(10) NOT NULL, Sno VARCHAR2(10), SBirthday DATE, SSex CHAR(2), Constraint pri_tbstudent_sno PRIMARY KEY(sno) ); 按回车键,系统提示“表已创建”,表示表创建成功。 2. SQL 提示符下,输入如下的代码来创建表 tbscore: CREATE TABLE tbscore ( Sno VARCHAR2(10) NOT NULL, Subject VARCHAR2(10) NOT NULL, Score NUMBER, PRIMARY KEY (Sno,Subject), Constraint ref_tbstudent_sno FOREIGN KEY(Sno) references tbstudent(sno) ); 按回车键,系统提示“表已创建”,表示表创建成功。 至此,学生成绩管理系统中的两个表已经创建成功。下面向表中插入数据。注意:现在 已经成功创建了两张表,表之间有主外健的关系,这种表一般称之为主从表。要分清哪是主、 哪是从。如果没有学生表,成绩表有意义吗?哪个学生的成绩?所以在这里学生表是主表, 成绩表是从表。 请学生思考对这种类型表的数据插入应该从哪个表开始? 理论知识: 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 55 - 字符数据类型用来存储字符串或字符数据。当你在 Oracle 中定义一个字符数据时,通 常需要指定字段的长度,它是该字段的最大长度。Oracle 提供以下几种字符数据类型:  CHAR 数据类型 CHAR 数据类型是一种有固定长度和最大长度的字符串。存储在数据类型为 CHAR 字段中 的数据将以空格的形式补到最大长度。长度定义在 1——2000 字节之间。 当你创建一个 CHAR 型字段,数据库将保证在这个字段中的所有数据都是定义长度,如 果某个数据比定义长度短,那么将用空格在数据的右边补到定义长度。如果长度大于定义长 度将会触发错误信息。  VARCHAR 数据类型 VARCHAR 数据类型数据是 VARCHAR2 型数据的快照。  VARCHAR2 数据类型 VARCHAR2 数据类型是一种可变长度的、有最大长度的字母数字型数据。VARCHAR2 类 型的字段长度可以达到 4000 字节,VARCHAR2 类型的变量长度可以达到 32676 字节。 一个空的 VARCHAR2(2000)字段和一个空的 VARCHAR2(2)字段所占用的空间是一样 的。  NCHAR 数据类型和 NVARCHAR2 数据类型 NCHAR 数据类型和 NVARCHAR2 数据类型分别与 CHAR 和 VARCHAR2 数据类型是相同的, 只不过它们用来存储 NLS(National Language Support)数据。  LONG 数据类型 LONG 数据类型是一个遗留下来的而且在将来不会被支持的数据类型。它将被 LOB(La- rge Object)数据类型所代替。 比较规则 VARCHAR2 和 CHAR 数据类型根据尾部的空格有不同的比较规则。对 CHAR 型数据,尾 部的空格将被忽略掉,对于 VARCHAR2 型数据尾部带空格的数据排序比没有空格的要大些。 比如: CHAR 型数据:‘YO’=‘YO ’ VARCHAR2 型数据:‘YO’<’YO ’ 数字数据类型 数字数据类型可用来存储负的和正的定点数、浮点数和零等数字,最常用的是 NUMBER 数据类型。 NUMBER 数据类型,可用来存储整数、定点数和浮点数。最高精度是 38 个十进制位。如 果不指定精度,默认为 38 位。范围在-1*10130 和 9.999…99*10125 之间,如果标识一个数 据超出这个范围时就会出错。 该数据类型的格式为: NUMBER(p,s):它表示 NUMBER 数据类型存储一个有 p 位精确度的 s 位等级的数据。 p 是精度(precision),表示数字的总位数。 Oracle 数据库案例教程_教师用书 - 56 - 翰子昂系列理论教材 s 是等级(scale),表示小数点右边数字的位数,取值范围在-84 至 127 之间。 可以同时指定 p 和 s,也可以只指定 p 不指定 s,或者是 p 和 s 都不指定。只能用整数 指定 p 和 s。 日期和时间数据类型  DATE 数据类型 DATE 数据类型用来存储日期和时间格式的数据。这种格式可以转换为其他格式的数据去 浏览,而且它有专门的函数和属性用来控制和计算。以下的几种信息都包含在 DATE 数据类 型中:Century、Year、Month、Day、Hour、Minute、Second。 通过如下语句可以获得系统的当前日期。 SELECT sysdate FROM dual; 系统显示如下信息: SYSDATE ---------- 04-11 月-07 通过如下语句可以获得按用户要求的格式输出的系统当前日期和时间。 SELECT TO_CHAR(sysdate,’YYYY-MM-DD HH24:MI:SS’) FROM dual; 系统显示如下信息: TO_CHAR(SYSDATE,'YY ------------------- 2007-11-04 19:34:13 两个日期相减,得到的是天数的小数形式。  TIMESTAMP 数据类型 TIMESTAMP 数据类型是 Oracle 在 DATE 数据类型上扩展出来的,它包括了所有 DATE 数据类 型的年月日时分秒的信息,而且包括了小数秒的信息。使用 TIMESTAMP 数据类型可以足够的区分出 事件的先后顺序。 获取系统的当前 TIMESTAMP 时间形式的语句如下。 SELECT systimestamp FROM dual; 系统显示如下信息: SYSTIMESTAMP ----------------------------------------- 04-11 月-07 07.40.01.875000 下午 +08:00 请大家分析上面时间形式中的信息? 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 57 - LOB 数据类型 随着社会的发展,在现代信息系统的开发中,需要存储的已不仅仅是简单的文字信息, 同时还包括一些图片和音像资料或者是超长的文本。比如开发一套旅游信息系统,每一个景 点都有丰富的图片、音像资料和大量的文字介绍。这就要求后台数据库要有存储这些数据的 能力。Oracle 公司在其从 Oracle 8i 以后的数据库中通过提供 LOB 字段实现了该功能。 LOB(Large Object)数据类型存储非结构化数据,比如二进制文件,图形文件,或 其他外部文件。LOB 可以存储到 4G 字节大小。数据可以存储到数据库中也可以存储到外部 数据文件中。LOB 数据的控制通过 DBMS_LOB 包实现。BLOB、NCLOB、和 CLOB 数据可以存 储到不同的表空间中,BFILE 数据存储在服务器上的外部文件中。LOB 数据类型有以下几种:  BLOB 代表 Binary LOB(二进制 LOB),它可以存储较大的二进制对象,如图形、视频剪辑 和声音剪辑等。  CLOB 代表 Character LOB(字符 LOB),它能够存储大量字符数据。该数据类型可以存储 单字节字符数据和多字节字符数据。CLOB 可用于存储非结构化的 XML 文档。  BFILE 代表 Binary File(二进制文件),它能够将二进制文件存储在数据库外部的操作系 统文件中。BFILE 列存储一个 BFILE 定位器,它指向位于服务器文件系统上的二进制文件。 支持的文件最大为 4GB。  NCLOB 用来将大型 NCHAR 数据存储在数据库中。NCLOB 数据类型同时支持固定宽度字符和可 变符(Unicode 字符数据)。NCLOB 类型的使用方法与 CLOB 类似。 Oracle 中的伪列 伪列就像 Oracle 中的一个表列,但实际上它并未存储在表中。伪列可以从表中查询, 但是不能插入、更新或删除它们的值。在此讨论两个伪列 ROWID 和 ROWNUM。 数据库中的每一行都有一个行地址,ROWID 伪列返回该行地址。可以使用 RowID 值来 定位表中的一行。通常情况下,ROWID 值可以唯一地标识数据库中的一行。ROWID 伪列有以 下重要的用途:  能以最快的方式访问表中的一行(索引中应用)。  能显示表的行是如何存储的。  可以作为表中行的唯一标识。 使用如下 SELECT 语句查看 ROWID 值。 SELECT ROWID FROM emp WHERE empno=7369; 系统显示如下信息: ROWID Oracle 数据库案例教程_教师用书 - 58 - 翰子昂系列理论教材 ------------------ AAAHW7AABAAAMUiAAA 请同学思考如果表中有多条重复记录,如何能只保留其中一条,而把其他记录删除。 对于一个查询返回的每一行,伪列 ROWNUM 返回一个数值代表行的次序。返回的第一行 的 ROWNUM 值为 1,第二行的 ROWNUM 值为 2,依次类推。通过使用 ROWNUM 伪列,可以限 制查询返回的行数。 注意: ROWNUM 的值是 Oracle 读取数据的顺序值,与记录的内容无关,不排 序。注意与 SQL SERVER 中的 TOP 关键字相区分。 使用如下语句可以从 dept 表中提取前 2 条记录。 SELECT * FROM dept WHERE ROWNUMBER<3; 数据定义语言(DDL) 数据定义语言用于改变数据库结构,包括创建、修改和删除数据库对象。例如,该语言 可以用来创建、修改和删除表。 表是一个以行和列的形式存放数据的存储单元。用来定义表的数据定义语言命令有:  CREATE TABLE(创建表)  ALTER TABLE(更改表)  TRUNCATE TABLE(截断表)  DROP TABLE(删除表) 创建表 创建表的语法如下: CREATE TABLE [schema].table_name (column_name DATATYPE [,column_name DATATYPE...]); 其中:schema 表示对象的所有者即模式的名称;table_name 表示表的名称; column_name 表示列的名称;DATATYPE 表示该列的数据类型及其长度。 创建表时,需要指定下列内容:  唯一的表名称  表内唯一的列名称  列的数据类型及其宽度 表名应该严格遵循下列命名规则:  表名首字符应该为字母;  不能使用 Oracle 保留字来为表命名; 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 59 -  表名的最大长度为 30 个字符;  同一用户模式下的不同表不能具有相同的名称;  可以使用下划线、数字和字母,但不能使用空格和单引号; Oracle 中的表名(还有列名、用户名和其他对象名)不区分大小写,系统会自动转换 成大写。 修改表 表创建后,有时根据情况需要,可能需要更改表的结构。这种更改可能是修改现有的列, 可能是添加新列。 用于更改现有列定义的语法如下: ALTER TABLE < table_name > MODIFY ( column definition … ); 用于向表中添加新列的语法如下: ALTER TABLE < table_name > ADD ( column definition … ) ; 用于删除表中现有列的语法如下: ALTER TABLE < table_name > DROP COLUMN column; ALTER TABLE 命令用于下列几种情况:  添加新列;  更改列的数据类型或数据类型的宽度;  添加或删除完整性约束条件;  删除现有列。 只有当列中现有的数据宽度都小于指定宽度时,才可以缩小该列的宽度。增加列的宽度 时则不受此限制。 截断表 如果存储在表的数据不再有用,可以只删除表中的记录而不删除表结构。使用 TRUNCA- TE TABLE 命令将删除表中的所有行,并释放此表使用的存储空间。 此命令的语法如下: TRUNCATE TABLE < tablename >; TRUNCATE 与 DELETE 命令的区别在于,前者快速删除记录并释放空间,不使用事务处 理,因此无法回滚,而 DELETE 命令可在执行删除之后,通过 ROLLBACK 撤销删除。如果确 定表中的数据不再有用,使用 TRUNCATE 命令效率更高。 删除表 DROP TABLE 命令用于从数据库中删除表及其全部数据。DROP TABLE 命令的语法如下: DROP TABLE < tablename >; Oracle 数据库案例教程_教师用书 - 60 - 翰子昂系列理论教材 注意: 所有 DDL 语句都是自动提交的。而且如果在执行 DDL 语句之前存在没 有提交的数据,也会自动提交。 3. 在 SQL 提示符下,输入以下代码用来向表 tbstudent 中添加数据: INSERT INTO tbstudent (Sno,SName,SBirthday,SSex) VALUES (‘Y20101’,’张一某’,TO_DATE(‘1982-12-15’,’YYYY-MM-DD’),1); 按回车键,系统提示“已创建 1 行”,表示已向表中插入一条记录。在 SQL 提示符下, 输入以下代码来提交刚才添加的数据: COMMIT; 重复步骤 3,将表 3-3 中的数据全部插入数据库。 理论知识: 事务是一个最小的工作单元,不论成功与否都作为一个整体进行处理。所以不会有部分 完成的事务。由于事务是由几个任务组成的,因此,如果一个事务作为一个整体是成功的, 则事务中的每个任务都必须成功。如果事务中有一部分失败,则整个事务失败。当事务失败 时,系统返回到事务开始前的状态。这个取消所有变化的过程称为“回滚”(ROLLBACK)。 例如,如果一个事务成功更新了两个表,在更新第三个表时失败,则系统将前两次更新恢复 原状,并返回到原始状态。保证事务的整体成功或失败的完整性,称为事务控制。事务只有 在提交(COMMIT)后,对数据库的更改才可以永久保持。 在 Oracle 数据库中,事务隐式地以执行一条非 SELECT 的 DML 语句开始,并显式地以 ROLLBACK 或 COMMIT 语句结束,但使用 DDL 语句时,事务处理将隐式地自动结束。现在看 一下如何使用 COMMIT 和 ROLLBACK 等命令控制数据库的事务处理。 COMMIT 命令用于提交并结束事务处理。只有使用 COMMIT 命令,才可对数据库执行永 久性的事务更改。其语法如下: COMMIT; SAVEPOINT 保存点类似于标记,它将很长的事务处理划分为较小的部分。它们用来标记 事务中可以应用回滚的点。因此,SAVEPOINT 和 ROLLBACK 一起用于回滚当前事务的一部 分。SAVEPOINT 的一般语法为: SAVEPOINT savepoint_id; ROLLBACK 命令用来撤销在当前事务中已完成的操作。可以回滚整个事务处理,以便撤 销由 SQL 语句做出的所有修改;也可以将事务回滚到某个保存点,以回滚该保存点后的修改。 ROLLBACK 的语法为: 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 61 - ROLLBACK; 如果要求回滚到事务中某个特定的阶段,即保存点,需执行以下命令 ROLLBACK TO SAVEPOINT savepoint_id; 4. 在 SQL 提示符下,输入以下代码用来向表 tbstudent 中添加数据: INSERT INTO tbscore (Sno,Subject,Score) VALUES (‘Y20101’,’XML’,86); 按回车键,系统提示“已创建 1 行”,表示已向表中插入一条记录。在 SQL 提示符下, 输入以下代码来提交刚才添加的数据: COMMIT; 重复步骤 4,将表 3-4 中的数据全部插入数据库。 理论知识: 表一旦创建,就需要填入数据,INSERT 命令用于向表添加记录。使用该命令时,用逗 号将各个值隔开,并用单引号将如 VARCHAR2、CHAR、RAW、LONG 和 DATE 等数据类型的 列值引起来,而 NUMBER 类型的值不需要用单引号引起来。输入值的顺序必须与在表中定义 的顺序或者在表名后列出的列的顺序相同。INSERT 语句的语法如下: INSERT INTO table[(column [,column…])] VALUES (value[,value…]); 向表中插入日期值时需要用单引号将其引起来。Oracle 日期类型的标准格式为 “DD-MON-YY”,2005 年 12 月 6 日按此格式表示为‘06-12 月-05’正常插入。也可以使 用 TO_DATE 函 数将给定的字符串按指定的格式进行转换。如 TO_DATE(‘2006-12-05’,’YYYY-MM-DD’) ,将字符串‘ 2006-12-05 ’ 按 格式 ‘YYYY-MM-DD’转换为日期。 INSERT 命令可以用来复制现有表中的记录到另一个表中。语法为: INSERT INTO new_table(column_list) SELECT columns_list FROM old_table; 其中:column_list 是字段列表,new_table 指新表,old_table 指原表。 注意: 当使用 INSERT 命令来复制表中数据的时候,两个表的结构要么完全相 同,要么制定两个表中数据类型相兼容的类型进行数据复制。 5. 学号为“Y20104”的学生由于某种原因,办理了退学手续。需要将该生的信息从数据 库中删除。删除不仅要删学生基本信息,还要删除成绩信息。大家注意这里的顺序,删除正 Oracle 数据库案例教程_教师用书 - 62 - 翰子昂系列理论教材 好和插入相反,先从从表开始,然后是主表。为什么? DELETE FROM tbscore WHERE Sno=’Y20104’; DELETE FROM tbstudent WHERE Sno=’Y20104’; COMMIT; 理论知识: 在表中插入行后,如果不再需要该数据,则可以删除记录。DELETE 命令用于从表中删 除行,其语法如下: DELETE FROM table [WHERE condition]; 用 DELETE 命令可以删除一行或多行。要从表中删除特定的行,可以将 WHERE 子句与 DELETE 命令一起使用。 6. 由于教师在录入成绩时不小心,将学号为“Y20101”的“XML”课程成绩录入错误, 需要将此成绩改正过来,正确成绩为 88 分,输入如下的代码: UPDATE tbscore SET score=88 WHERE SNO=’Y20101’ AND subject=’XML’; COMMIT; 理论知识: 有时候需要修改存储在数据库表中的值。使用 UPDATE 命令可以更新表中的行,此命令 可以同时更新一个列或多个列。用 WHERE 子句来限制只对特定行进行更新。 UPDATE 命令的语法如下: UPDATE table SET column = value [,column = value,…] [WHERE condition ]; 在上述语法中,WHERE 子句和 SET 子句还可以包含查询。假设符合 WHERE 子句的条件, UPDATE 语句则以提供的值来设置每个字段。 7. 这两个表是由 SCOTT 用户创建,这时在数据库中还有一个普通用户 test,test 想访问 这两个表中的数据,但不能修改、增加、删除表中的数据。在这种情况下,就需要 SCOTT(表 的拥有者)给 test 授予可以访问表的权限,在 SQL 提示符下,输入如下代码(假定用户 test 已经创建,并具有所必需的权限)。 GRANT SELECT ON tbstudent To test; GRANT SELECT ON tbscore To test; 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 63 - 按回车键,系统提示“授权成功”。 理论知识: 数据控制语言为用户提供权限控制命令。数据库对象(比如表)的所有者对这些对象拥 有独有的控制权限。所有者可以根据自己的意愿决定其他用户如何访问对象,授予其他用户 权限(INSERT、SELECT、UPDATD …),使他们可以在其权限范围内执行操作。例如, 如果一个用户被授予对某个表的 SELECT 权限,那么他只可以查看数据,而不能对该表执行 其他任何 DML 操作。授予的权限还可以由所有者随时撤销。 授权命令 如果用户在自己的模式中创建了一个表,则不需要将此表的任何权限授予该用户。作为 对象的所有者,用户拥有对该对象的所有操作权限。如果用户要与其他用户共享某个对象, 则可以将该对象的适当权限授予其他用户。对象是指表、视图、序列和同义词等逻辑数据存 储结构。可以使用 GRANT 命令授予对象权限,其语法如下: GRANT privalige ON subject_name TO USERNAME [WITH GRANT OPTION]; 如果用“WITH GRANT OPTION”授予用户权限,则接受该权限的用户可以将此权限授 予其他用户。 权限分为对象权限和系统权限: 对象权限:对象权限就是指在表、视图、序列、过程、函数或包等对象上执行特殊动作 的权利。有几种不同类型的权限可以授予给用户或角色。如下表: 权限 ALTER DELET E EXECUT E INDEX INSERT SELECT UPDAT E function no no yes no no no no procedure no no yes no no no no package no no yes no no no no Libary no no yes no no no no Sequence yes no no no no no no Table yes yes no yes yes yes yes Type no no yes no no no no View no yes no no yes yes yes 表 3-5 不同数据库对象的权限 对象有不止一个权限,特 殊权限 ALL 可以被授予或撤销,如 TABLE 的 ALL 权限就包括: SELECT、INSERT、UPDATE 和 DELETE。 系统权限:系统权限需要授予者有进行系统级活动的能力,如连接数据库,更改用户会 话、建立表或建立用户等等。你可以在数据字典视图 SYSTEM_PRIVILEGE_MAP 上获得完整 的系统权限。对象权限和系统权限都通过 GRANT 语句授予用户或角色。需要注意的是在授予 对象权限时语句应该是 WITH GRANT OPTION 子句,但 在 授予系统权限时语句是 WITH AD- MIN OPTION。 8. 当 test 用户不需要再访问表中数据的时候,为了保证数据库的安全性,需要收回 test Oracle 数据库案例教程_教师用书 - 64 - 翰子昂系列理论教材 用户对两个表的访问权限,在 SQL 提示符下输入如下代码: REVOKE SELECT ON tbstudent FROM test; REVOKE SELECT ON tbscore FROM test; 按回车键,系统提示“撤销成功”。 理论知识: 要撤销已授予用户的权限,可以使用 REVOKE 命令。此 命令在格式上与 GRANT 命令非常 相似,其语法如下: REVOKE privalige ON subject_name FROM USERNAME; 9. 显示课程“Oracle”的成绩,要求成绩按降序排列,显示的列包括:姓名、课程、成 绩三列,在 SQL 提示符下,输入如下语句: SELECT s1.sname,s2.subject,s2.score FROM tbstudent s1,tbscore s2 WHERE s1.sno=s2.sno AND s2.subject='Oracle' ORDER BY s2.score DESC; 执行结果如图 3-1 所示。 图 3-1 显示课程“Oracle”的成绩 10. 查询所有姓“王”的同学的信息,要求信息显示“姓名”、“学号”、“生日”三 列,在 SQL 提示符下,输入如下语句: SELECT sname,sno,sbirthday FROM tbstudent WHERE sname LIKE ‘王%’; 执行结果如图 3-2 所示。 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 65 - 图 3-2 查询所有姓“王”同学的信息 请同学回答,如果要查询名字中包含“王”这个字的所有同学的信息,查询条件应如何 写? 理论知识: 可以通过 SELECT 命令来提取表中的信息。通常将这种操作称为“查询”表。SELECT 命令是最常用的命令。SELECT 语句的语法如下: SELECT *|{[DISTINCT] column } expression [ alias 」, … } FROM table_name [WHERE condition ] [ ORDER BY columns ]; 其中:*表示选择表中所有列;colunln 是列名,可以选择多个列;expression 是列 名和常数组成的表达式;alias 是列的别名;DISTINCT 关键字限制只返回不同的列值。 table_name 是表名。 SELECT 语句的特殊用法,利用现有的表创建新表,语法如下: CREATE TABLE AS SELECT {*|columns} FROM [WHERE ]; 此命令可以把现有表中所有记录复制到新表,也可以仅复制选定的列或只复制结构而不 复制数据。 11. 查询所有学生的年龄,要求显示“姓名”、“年龄”两列,在 SQL 提示符下,输入如 下语句: SELECT sname, TO_NUMBER(TO_CHAR(sysdate,’YYYY’))-TO_NUMBER(TO_CHAR(sbirthday,’YYYY’)) AGE FROM tbstudent; 执行结果如图 3-3 所示。 Oracle 数据库案例教程_教师用书 - 66 - 翰子昂系列理论教材 图 3-3 查询所有学生的年龄 12. 查询某科目成绩在 60 分以上的学生个数,并显示该科目,在 SQL 提示符下输入如下 的代码: SELECT SUBJECT,COUNT(*) FROM TBSCORE WHERE SCORE>=60 GROUP BY SUBJECT; 执行结果如图 3-4 所示。 图 3-4 查询某科目成绩 60 分以上的学生个数 13. 查询出参加考试的各科成绩都及格的学员学号、平均成绩,在 SQL 提示符下输入如 下的代码: SELECT SNO,AVG(SCORE) FROM TBSCORE GROUP BY SNO HAVING MIN(SCORE)>=60; 执行结果如图 3-5 所示。 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 67 - 图 3-5 查询出参加考试的各科成绩都及格的学员 4. 实验 按照第 3 部分相关实践知识练习(90 分钟)。 5. 课后作业 以 SCOTT 身份登陆到 Oracle 数据库后完成以下作业: 1. 用 SQL 语句创建一个表 t_table,表中包含两个字段,分别为(id NUMBER,name VARCHAR2(20)),向表中插入数据(1234,’abc’),(1234,’abc’),(1234,’abc’), (3456,’bcd’),(3456,’bcd’)。写出删除表中所有重复记录的 SQL 语句。 2. 现有学生表和学生缴费表,其中学生表中(学号(PRIMARY KEY),姓名,班级), 缴费表(学号,时间,金额),一个学生一天可能多次缴费,试写出查询某一天缴费学生的 信息,查询结果应包括:学号,姓名,班级和缴费金额的和。 Oracle 数据库案例教程_教师用书 - 68 - 翰子昂系列理论教材 案例二 Oracle 数据库中的函数及子查询 1. 教学目标 1.1 掌握 SQL 操作符 1.2 掌握 SQL 函数 2. 工作任务 2.1 综合使用各种函数 2.2 使用字符处理函数 2.3 使用数字函数 2.4 使用日期函数 2.5 使用转换函数 3. 相关实践知识 从开始菜单中打开 SQL*Plus 工具,以 SCOTT 用户的身份登录到数据库。 3.1 综合使用各种函数 3.1.1 查询 emp 表中员工的工资和 1. 查询 emp 表中每个员工的工资和,每个员工的工资由两部分组成(sal、comm),在 SQL 提示符下,输入以下代码: SELECT empno,sal+comm FROM emp; 2. 按“Enter”键,系统显示如下信息: EMPNO SAL+COmm ---------- ---------- 7369 7499 1900 7521 1750 7566 7654 2650 7698 7782 7788 7839 7844 1500 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 69 - 7876 7900 7902 7934 已选择 14 行。 3. 令人吃惊的结果,只有少数人有工资,而实际上每员工都有工资(sal),只是有的员 工没有奖金(comm),而两项相加的结果是 comm 列有值的和也有值,而 comm 列没有值的 和也没有值为 null。 注意: 任何一个数和 NULL 相加,结果将是 NULL。在这里 NULL 可以理解为 “不知道”。 需要一个函数来将 NULL 值转换为 0 来进行运算。 理论知识: 算术操作符 要执行基于数值的计算,可以在 SQL 命令中使用算术表达式。算术表达式由 NUMBER 数 据类型的列名、数 值常量和连接它们的算术操作符组成。算术操作符包括+(加)、-(减)、 *(乘)和/(除)等。 如果算术表达式中有多个操作符,则必须知道每个操作符的优先级。*和/具有同等优 先级,十和一具有同等优先级,*和/的优先级高于+和一。可以使用括号来控制计算顺序。 比较操作符 比较操作符用于比较两个表达式的值。比较操作符包括=、!=、<、>、<=、>=、 BETWEEN…AND、IN(检查是否在两个值之间)、LIKE(与列表中的值相匹配)和 IS NU- LL(检查是否为空)。上述的最后 4 个操作符还可以与 NOT(逻辑非)运算符一起使用来检 查“非”条件,如 NOT BETWEEN、NOT LIKE 和 IS NOT NULL 等。 使用 IN 操作符搜索字符值时,列值必须与列表中出现的值完全匹配。如 果不知道确切的 字符值,可以用 LIKE 操作符搜索字符模式,进行模糊匹配。LIKE 操作符识别特殊的字符, 如%和_。前者可以与任意个(包括零个)字符匹配,而后者只匹配一个字符。 逻辑操作符 逻辑操作符用于组合多个比较运算的结果以生成一个或真或假的结果。逻辑操作符包括 AND(与)、OR(或)和 NOT(非)。 集合操作符 在中学都学习过集合运算,集合运算主要有交、并、差三种操作。并集将两个集合中的 元素合并到一起;交集是求两个集合中的公共元素;差集就是第一个集合减去交集后剩余的 元素。当两个集合进行并和交时,没有先后顺序,但对于差集是有先后顺序的。 将集合中的元素进行扩展,变成一条记录,那集合的操作就转换为了数据库中的集合操 Oracle 数据库案例教程_教师用书 - 70 - 翰子昂系列理论教材 作。同样数据库中的集合操作也包含上述三种运算(交、并、差),并做了一定的扩展。  UNION(并):将两个集合中重复的元素,只保留一份  UNION ALL(并):简单的将两个集合合并,不考虑是否有重复元素  INTERSECT(交):返回两个集合中的公共元素  MINUS(差):返回第一个集合中减去交集,剩余的元素 使用集合操作符连接起来的 SELECT 语句中的列遵循以下规则:  通过集合操作符联接的各个查询具有相同的列数,而且对应列的数据类型必须相同  这种查询不应含有 LONG 类型的列  列标题来自第一个 SELECT 语句 在两个 SELECT 语句中指定的列名不必相同,但数据类型必须匹配。也可以对联合查询 的结果进行排序,使用 ORDER BY 子句时,它必须放在最后一个 SELECT 语句之后,而且必 须指定列索引来排序,而不是指定列名。列索引是一个从 1 开始的整数,SELECT 字句中第 一列索引号应该是 1,依此类推。 连接操作符 连接操作符用于将两个或多个字符串合并成一个字符串,或者将一个字符串与一个数值 合并在一起。 在 Oracle 中连接操作符为“||”。 NVL 函数 将空值转换为指定的值,语法为: NVL(表达式 1,表达式 2) 当表达式 1 为空(NULL)时,返回表达式 2;否则返回表达式 1。 NVL2 函数 将空值转换为指定值,语法为: NVL(表达式 1,表达式 2,表达式 3) 当表达式 1 不为空(NULL)时,返回表达式 2;当表达式 1 为空(NULL)时,否则返 回表达式 3。 NULLIF 函数 语法为: NULLIF(表达式 1,表达式 2) 当两个表达式相等时,返回空;否则返回表达式 1。 4. SQL 提示符下,输入如下的代码: SELECT empno, sal + nvl(comm,0) FROM emp; 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 71 - 5. 按回车键,系统显示如下信息: EMPNO SAL+NVL(comm,0) ---------- --------------- 7369 800 7499 1900 7521 1750 7566 2975 7654 2650 7698 2850 7782 2450 7788 3000 7839 5000 7844 1500 7876 1100 7900 950 7902 3000 7934 1300 已选择 14 行。 从结果来看,正确显示了工资和奖金两项的和。 请同学们试着用 NVL2 这个函数将上面查询改写一下。 3.1.2 按职位查询员工信息 1. 查询工作类型为“SALESMAN”和“ANALYST”的员工的信息。SQL 提示符下,输 入如下的代码: SELECT * FROM emp WHERE job IN (‘SALESMAN’,’ANALYST’); 2. 按回车键,系统显示信息如图 3-6 所示。 图 3-6 查询工作类型为“SALESMAN”和“ANALYST”的员工的信息 请同学们用另外一种写法,来完成上述查询要求。 Oracle 数据库案例教程_教师用书 - 72 - 翰子昂系列理论教材 提示:使用逻辑运算符“OR”。 3.1.3 查询显示 emp 表中工龄最长的员工信息 1. 查询显示 emp 表中工龄最长的员工信息,工龄用“月”表示。SQL 提示符下,输入如 下的代码: SELECT MAX(MONTHS_BETWEEN(sysdate,hiredate)) FROM emp; 2. 按回车键系统提示: MAX(MONTHS_BETWEEN(SYSDATE,HIREDATE)) ------------------------------------- 322.700491 在这里 MAX 是一个分组函数,从所有值中取最大值。其他常用分组函数包括:MIN, AVG,COUNT、SUM 等。 3. 上面的查询结果精确到小数,通过如下语句可以获得取整后的值: SELECT ROUND(MAX(MONTHS_BETWEEN(sysdate, hiredate))) FROM emp; 4. 按回车键,系统提示: ROUND(MAX(MONTHS_BETWEEN(SYSDATE,HIREDATE))) -------------------------------------------- 323 5. 如果要求不能四舍五入,要求向下取整,可以使用如下语句: SELECT FLOOR(MAX(MONTHS_BETWEEN(sysdate, hiredate))) FROM emp; 6. 按回车键,系统提示: ROUND(MAX(MONTHS_BETWEEN(SYSDATE,HIREDATE))) -------------------------------------------- 322 理论知识: FLOOR(n)函数 对给定的数字取整数。 MONTHS_BETWEEN(date1,date2) 计算两个日期之间的时间间隔,以月为单位。 ROUND(m,n)和 TRUNC(m,n) ROUND 按照指定的精度 n 对 m 进行四舍五入; TRUNC 舍弃指定位数 n 后的小数部分,如果没有指定舍弃所有小数部分。 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 73 - 3.1.4 按月查询销售额 假设有商品表 sale,表结构为: month CHAR(6) --月份 sell NUMBER(10,2) --月销售金额 表中数据如下: month sell 200001 1000 200002 1100 200003 1200 200004 1300 200005 1400 200006 1500 200007 1600 200101 1100 200202 1200 200301 1300 表 3-6 t_sale 表内的数据 想将数据转化为如下结构: Year CHAR(4) --年份 month1 NUMBER(10,2) --1 月销售金额 month2 NUMBER(10,2) --2 月销售金额 month3 NUMBER(10,2) --3 月销售金额 month4 NUMBER(10,2) --4 月销售金额 month5 NUMBER(10,2) --5 月销售金额 month6 NUMBER(10,2) --6 月销售金额 month7 NUMBER(10,2) --7 月销售金额 month8 NUMBER(10,2) --8 月销售金额 month9 NUMBER(10,2) --9 月销售金额 month10 NUMBER(10,2) --10 月销售金额 month11 NUMBER(10,2) --11 月销售金额 month12 NUMBER(10,2) --12 月销售金额 可以使用如下 SQL 语句: SELECT SUBSTR(month,1,4), SUM(DECODE(SUBSTR(month,5,2),'01',sell,0)), SUM(DECODE(SUBSTR(month,5,2),'02',sell,0)), SUM(DECODE(SUBSTR(month,5,2),'03',sell,0)), Oracle 数据库案例教程_教师用书 - 74 - 翰子昂系列理论教材 SUM(DECODE(SUBSTR(month,5,2),'04',sell,0)), SUM(DECODE(SUBSTR(month,5,2),'05',sell,0)), SUM(DECODE(SUBSTR(month,5,2),'06',sell,0)), SUM(DECODE(SUBSTR(month,5,2),'07',sell,0)), SUM(DECODE(SUBSTR(month,5,2),'08',sell,0)), SUM(DECODE(SUBSTR(month,5,2),'09',sell,0)), SUM(DECODE(SUBSTR(month,5,2),'10',sell,0)), SUM(DECODE(SUBSTR(month,5,2),'11',sell,0)), SUM(DECODE(SUBSTR(month,5,2),'12',sell,0)) FROM sale GROUP BY SUBSTR(month,1,4); 理论知识: DECODE 函数 DECODE(条件,值 1,翻译值 1,值 2,翻译值 2,...值 n,翻译值 n,缺省值) 条件可以是表达式也可以是字段名;当条件值为“值 1”,显示“翻译值 1”;当值为“值 2”,显示“翻译值 2”,依次类推,如果没有满足条件的返回缺省值。 SUBSTR(string,start,count) 取子字符串,从 start 开始,取 count 个。 3.1.5 员工按工资排名 1. 显示员工工资在部门内的排名,要求工资相同排名相同,排名要求连续,在 SQL 提示 符下,输入如下代码: SELECT empno,ename,deptno, dense_rank() OVER (PARTITION BY deptno ORDER BY sal DESC) as rank FROM emp; 2. 按回车将,系统显示如下信息: EMPNO ENAME DEPTNO RANK ---------- ---------- ---------- ---------- 7839 KING 10 1 7782 CLARK 10 2 7934 MILLER 10 3 7788 SCOTT 20 1 7902 FORD 20 1 7566 JONES 20 2 7876 ADAMS 20 3 7369 SMITH 20 4 7698 BLAKE 30 1 7499 ALLEN 30 2 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 75 - 7844 TURNER 30 3 7654 MARTIN 30 4 7521 WARD 30 4 7900 JAMES 30 5 已选择 14 行。 3. 同学都参加过运动会,那大家是否清楚运动会是如何排名的呢?如果成绩相同,则排 名相同,而后一个排名要看成绩相同的人数。比如一个第一、两个第二、三个第三,那他们 的排名是一个第一、两个第二、而没有第三,三个成绩第三的现在排名为第四,如果后面还 有那排名应为第七。如何实现这样的排名,可以模拟对 emp 表的工资进行排序,在 SQL 提示 符下,输入如下代码: SELECT deptno,sal,RANK() OVER (ORDER BY sal) FROM emp; 4. 按回车键,系统显示如下信息: DEPTNO SAL RANK()OVER(ORDERBYSAL) ---------- ---------- ---------------------- 20 800 1 30 950 2 20 1100 3 30 1250 4 30 1250 4 10 1300 6 30 1500 7 30 1600 8 10 2450 9 30 2850 10 20 2975 11 20 3000 12 20 3000 12 10 5000 14 已选择 14 行。 理论知识: DENSE_RANK 的行排位相同,计算一个行在一组有序行中的排位,排位是以 1 开头的连 续整数。如果值相等则排名相同,并且排位是连续的。 DENSE_RANK 函数的语法为: DENSE_RANK() OVER ([PARTITION BY column] ORDER BY column) ROW_NUMBER 为查询中的每一行返回一个唯一的排名值,序号根据 ORDER BY 子句指定, Oracle 数据库案例教程_教师用书 - 76 - 翰子昂系列理论教材 从 1 开始。 ROW_NUMBER 的语法为: ROW_NUMBER() OVER ([PARTITION BY column] ORDER BY column) RANK 函数计算一个值在一组值中的排位,排位是以 1 开头的连续整数。具有相等值的 行排位相同,序数随后跳跃相应的数值,即如果两行的序数为 1,则没有序数 2,下一行的序 号为 3。 RANK 函数的语法为: RANK() OVER ([PARTITION BY column] ORDER BY column) 3.1.6 分页 随着网络技术的发展,大量信息都是以网页的形式呈现给用户,当大量信息出现在网页 上时,给用户的浏览和导航带来了极大的不方便,在大多数情况下,都是以分页的形式来解 决。根据用户的请求,返回表中根据某一列排序后的记录。 1. 以 emp 表为例,要求返回根据 empno 排序后的 3-5 记录,也就是第三到第五。可以 利用以前的 ROWNUM 伪列来实现,在 SQL 提示符下,输入如下代码: SELECT * FROM (SELECT ROWNUM num,empno FROM (SELECT empno FROM emp ORDER BY empno desc)) WHERE num>=3 AND num<=5; 2. 按回车键,系统显示如下信息: NUM EMPNO ---------- ---------- 3 7900 4 7876 5 7844 3. 可以注意到上面的查询比较繁琐,有没有简便的方法呢,可以使用分析函数 ROW_ NUMBER()来简化操作,可以书写如下 SQL 语句: SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY empno DESC) num,empno FROM emp) WHERE num>=3 AND num<=5; 4. 按回车键,系统提示如下信息: NUM EMPNO ---------- ---------- 3 7900 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 77 - 4 7876 5 7844 3.1.7 使用 HAVING 子句 1. 查询部门平均工资大于 1000 的部门的编号和平均工资,在 SQL 提示符下,输入如下 代码: SELECT deptno,AVG(sal) avg_sal FROM emp GROUP BY deptno HAVING AVG(sal)>1000; 2. 按回车键,系统提示如下: DEPTNO AVG_SAL -------- ---------- 30 1566.66667 20 2175 10 2916.66667 HAVING 用于对分组后的结果进行处理。 理论知识: 分组函数也叫集合函数,返回基于多个行的单一结果。行的准确数量无法确定,除非查 询被执行,并且所有的结果都被包含在内。与单行函数不同的是,在解析时所有的行都是已 知的。由于这种差别使分组函数与单行函数在要求和行为上有微小的差异。 Oracle 提供了丰富的基于分组的,多行的函数。这些函数可以在 SELECT 或 SELECT 的 HAVING 子句中使用,当用于 SELECT 子串时常常都和 GROUP BY 一起使用。  AVG([{DISYINCT|ALL}]) 返回数值的平均值,缺省设置为 ALL。  COUNT({*|DISTINCT|ALL}) 返回查询中行的数目,缺省设置是 ALL,*表示返回所有的行。  MAX([{DISTINCT|ALL}]) 返回选择列表项目的最大值,如果 x 是字符串数据类型,它返回一个 VARCHAR2 数据类 型,如果 x 是一个 DATA 数据类型,返回一个日期,如果 x 是 number 数据类型,返回一个 数字。注意 distinct 和 all 不起作用,因为最大值与这两种设置是相同的。  MIN([{DISTINCT|ALL}]) 返回选择列表项目的最小值。  STDDEV([{DISTINCT|ALL}]) 返回选择的列表项目的标准差,所谓标准差是方差的平方根。 Oracle 数据库案例教程_教师用书 - 78 - 翰子昂系列理论教材  SUM([{DISTINCT|ALL}]) 返回选择列表项目的数值的总和。  VARIANCE([{DISTINCT|ALL}]) 返回选择列表项目的统计方差。 3.2 字符函数 1. 返回与指定的字符对应的十进制数: SELECT ASCII('A') A, ASCII ('a') a, ASCII('0') zero, ASCII(' ') space FROM dual; 按回车键,系统提示如下信息: A A ZERO SPACE ---------- ---------- ---------- ---------- 65 97 48 32 理论知识: 函数 ASCII(char c)返回与指定的字符对应的十进制数。 2. 给出整数,返回对应的字符: SELECT CHR(54740) zhao,CHR(65) chr65 FROM dual; 按回车键,统提示如下信息: ZH C -- - 赵 A 理论知识: 函数 CHR(number n)给出整数,返回对应的字符。 3. 连接两个字符串: SELECT CONCAT('010-','88888888')||'转 23' 电话 FROM dual; 按回车键,统提示如下信息: 电话 ---------------- 010-88888888 转 23 理论知识: 函数 CONCAT(char1,char2)连接两个字符串。 4. 返回字符串并将字符串的第一个字母变为大写: SELECT INITCAP('TOm') upp FROM dual; 按回车键,统提示如下信息: 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 79 - UPP ----- Tom 理论知识: 函数 INITCAP(char)返回字符串并将字符串的第一个字母变为大写。 5. 在一个字符串中搜索指定的字符,返回指定字符的位置: SELECT INSTR('oracle traning','ra',1,2) instring FROM dual; 按回车键,系统提示如下: INSTRING ---------- 9 理论知识: 函数 INSTR(c1,c1,i,j)在一个字符串中搜索指定的字符,返回发现指定字符的位 置。  c1:被搜索的字符串  c2:希望搜索的字符串  i:搜索的开始位置,默认为 1  j:出现的位置,默认为 1 6. 求字符串的长度: SELECT ename,LENGTH(ename),sal,LENGTH(TO_CHAR(sal)) FROM emp WHERE empno=7369; 按回车键,系统提示如下: ENAME LENGTH(ENAME) SAL LENGTH(TO_CHAR(SAL)) ---------- ------------- ---------- -------------------- SMITH 5 800 3 理论知识: 函数 LENGTH(string)返回字符串的长度。 7. 将字符串所有的字符小写: SELECT LOWER('AaBbCcDd') AaBbCcDd FROM dual; 按回车键,系统提示如下: AABBCCDD -------- aabbccdd Oracle 数据库案例教程_教师用书 - 80 - 翰子昂系列理论教材 理论知识: 函数 LOWER(string)返回字符串,并将所有的字符小写。 8. 将字符串所有的字符大写: SELECT UPPER('AaBbCcDd') upper FROM dual; 按回车键,系统提示如下: UPPER -------- AABBCCDD 理论知识: 函数 UPPER(string)返回字符串,并将所有的字符大写。 9. 使用指定字符,填充字符: SELECT LPAD(rpad('gao',10,'*'),17,'*') FROM dual; 按回车键,系统提示如下: LPAD(RPAD('GAO',1 ----------------- *******gao******* 填充为指定长度的字符,用*来填满。 理论知识: 字符填充函数 RPAD 和 LPAD  RPAD:在列的右边填充字符。  LPAD:在列的左边填充字符。 10. 删除指定字符串: SELECT LTRIM(RTRIM(' gao qian jing ',' '),' ') FROM dual; 按回车键,系统提示如下: LTRIM(RTRIM(' ------------- gao qian jing 理论知识: LTRIM:删除左边出现的指定字符串。 RTRIM:删除右边出现的指定字符串。 11. 从字符串“13088888888”中取子字符串,从第 3 个开始,取 8 个: SELECT SUBSTR('13088888888',3,8) FROM dual; 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 81 - 按回车键,系统提示如下: SUBSTR(' -------- 08888888 理论知识: 取子字符串函数 SUBSTR(string,start,count),从 start 开始,取 count 个。 12. 使用指定字符替换某些字符: SELECT REPLACE('he love you','he','i') FROM dual; 按回车键,系统提示如下信息: REPLACE('H ---------- i love you 理论知识: 函数 REPLACE('string','s1','s2')将字符串 string 中的子字符串 s1 替换为 s2。 string:希望被替换的字符或变量 s1:被替换的字符串 s2:要替换的字符串 3.3 数字函数 1. 求指定值的绝对值: SELECT ABS(100),ABS(-100) FROM dual; 按回车键,系统提示如下: ABS(100) ABS(-100) ---------- ---------- 100 100 理论知识: 函数 ABS(n)返回指定值的绝对值。 2. 求反余弦的值: SELECT ACOS(-1) FROM dual; 按回车键,系统提示如下: ACOS(-1) ---------- Oracle 数据库案例教程_教师用书 - 82 - 翰子昂系列理论教材 3.14159265 理论知识: 函数 ACOS(n)给出反余弦的值。 3. 求大于或等于给出数字的最小整数: SELECT CEIL(3.1415927) FROM dual; 按回车键,系统提示如下: CEIL(3.1415927) --------------- 4 理论知识: 函数 CEIL(n)返回大于或等于给出数字的最小整数。 4. 对给定的数字取整数: SELECT FLOOR(2345.67) FROM dual; 按回车键,系统提示如下: FLOOR(2345.67) -------------- 2345 理论知识: 函数 FLOOR(n)对给定的数字取整数。 5. 返回一个给定数字的余弦: SELECT COS(-3.1415927) FROM dual; 按回车键,系统提示如下: COS(-3.1415927) --------------- -1 理论知识: 函数 COS(n)返回一个给定数字的余弦。 6. 返回一个 n1 除以 n2 的余数: SELECT MOD(10,3),MOD(3,3),MOD(2,3) FROM dual; 按回车键,系统提示如下: MOD(10,3) MOD(3,3) MOD(2,3) 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 83 - ---------- ---------- ---------- 1 0 2 理论知识: 函数 MOD(n1,n2)返回一个 n1 除以 n2 的余数。 7. 返回 n1 的 n2 次方根: SELECT POWER(2,10),POWER(3,3) FROM dual; 按回车键,系统提示如下: POWER(2,10) POWER(3,3) ----------- ---------- 1024 27 理论知识: 函数 POWER(n1,n2)返回 n1 的 n2 次方根。 8. 按照指定的精度进行舍入,舍弃指定位数后的小数部分,如果没有指定则舍弃所有小 数部分: SQL> SELECT ROUND(55.5),ROUND(-55.4),TRUNC(55.5),TRUNC(-55.5) FROM dual; ROUND(55.5) ROUND(-55.4) TRUNC(55.5) TRUNC(-55.5) ----------- ------------ ----------- ------------ 56 -55 55 -55 理论知识: 函数 ROUND(m,n)和 TRUNC(m,n)ROUND 按照指定的精度 n 对 m 进行四舍五入。 9. 取数字 n 的符号,大于 0 返回 1,小于 0 返回-1,等于 0 返回 0: SQL> SELECT SIGN(123),SIGN(-100),SIGN(0) FROM dual; SIGN(123) SIGN(-100) SIGN(0) ---------- ---------- ---------- 1 -1 0 理论知识: 函数 SIGN(n)取数字 n 的符号,大于 0 返回 1,小于 0 返回-1,等于 0 返回 0。 10. 求一个数字的平方根: SQL> SELECT SQRT(64),SQRT(10) FROM dual; SQRT(64) SQRT(10) ---------- ---------- 8 3.16227766 Oracle 数据库案例教程_教师用书 - 84 - 翰子昂系列理论教材 理论知识: 函数 SQRT(n)返回数字 n 的平方根。 3.4 日期函数 1. 求某个日期增加或减去月份后的日期: SQL>SELECT TO_CHAR(ADD_MONTHS(TO_DATE('199912','YYYYmm'),2),'YYYYmm') FROM dual; TO_CHA ------ 200002 SQL>SELECT TO_CHAR(ADD_MONTHS(TO_DATE('199912','YYYYmm'),-2),'YYYYmm') FROM dual; TO_CHA ------ 199910 理论知识: 函数 ADD_MONTHS 返回给指定的日期增加或减去指定的月数后的日期值。 2. 求某个日期所在月的最后一天: SQL>SELECT TO_CHAR(sysdate,'YYYY.mm.dd'),TO_CHAR((sysdate)+1,'YYYY.mm.dd') FROM dual; TO_CHAR(SY TO_CHAR((S ---------- ---------- 2007.11.14 2007.11.15 SQL> SELECT LAST_DAY(sysdate) FROM dual; LAST_DAY(SYSDA -------------- 30-11 月-07 理论知识: 函数 LAST_DAY 返回指定日期所在月的最后一天。 3. 求两个日期之间相差的月数: SQL> SELECT MONTHS_BETWEEN('19-12 月-1999','19-3 月-1999') MON_BETWEEN FROM dual; MON_BETWEEN 专题三 SQL 语句和 SQL 函数 翰子昂系列理论教材 - 85 - ----------- 9 理论知识: 函数 MONTHS_BETWEEN(date1,date2)计算两个日期之间的时间间隔,以月为单位。 4. 求下一个星期五的日期: SQL> SELECT NEXT_DAY('18-5 月-2001','星期五') next_day FROM dual; NEXT_DAY -------------- 25-5 月 -01 理论知识: 函数 NEXT_DAY(DATE,'day')给出日期 DATE 和星期 day 之后计算下一个星期的日 期,即用 day 指定的下一个星期几的日期。 3.5 转换函数 1. 将日期按照指定的格式转化为字符串: SQL> SELECT TO_CHAR(sysdate,'YYYY/mm/dd hh24:mi:ss') FROM dual; TO_CHAR(SYSDATE,'YY ------------------- 2007/11/14 19:08:06 理论知识: TO_CHAR(date,'format') 将日期按照指定的格式转化为字符串。 TO_DATE(string,'format') 将字符串转化为 Oracle 中的一个日期。 TO_NUMBER 将给出的字符转换为数字。 BFILENAME(dir,file) 在数据库中存储指定文件的指针,指向一个操作系统文件,可以通过相关过程或函数以 只读方式访问外部文件,首先在创建表示创建一个字段为 BFILE 类型。 EMPTY_BLOB()和 EMPTY_CLOB() 这两个函数都是用来对大数据类型字段进行初始化操作的函数,给字段赋予非空值。 2. 将字符串转化为 Oracle 中的一个日期: SQL> SELECT TO_DATE('2007-10-12','YYYY-MM-DD') FROM dual; Oracle 数据库案例教程_教师用书 - 86 - 翰子昂系列理论教材 TO_DATE('2007- -------------- 12-10 月-07 3. 将给出的字符转换为数字: SQL> SELECT TO_NUMBER('1999') year FROM dual; YEAR ---------- 1999 4. 在数据库中存储指定文件的指针,指向一个操作系统文件,可以通过相关过程或函数 以只读方式访问外部文件,首先创建一个表 file_tbl,表中有一个字段为 BFILE 类型。再创建 一个目录 DIRECTORY 指向 C 盘根目录,然后利用函数 BFILENAME 向表中插入一条记录。 语句如下所示: CREATE TABLE file_tbl(c_file BFILE); CREATE DIRECTORY lob_dir AS ‘c:\’; INSERT INTO file_tbl VALUES(BFILENAME('lob_dir','image1.gif')); 4. 实验 按照第 3 部分相关实践知识依次练习: 1. 按照第 3.1 部分提供的实践知识综合练习各种函数(40 分钟)。 2. 按照第 3.2 部分提供的实践知识练习字符函数的使用(15 分钟)。 3. 按照第 3.3 部分提供的实践知识练习数字函数的使用(10 分钟)。 4. 按照第 3.4 部分提供的实践知识练习日期函数的使用(10 分钟)。 5. 按照第 3.5 部分提供的实践知识练习转换函数的使用(15 分钟)。 5. 课后作业 以 scott 身份登陆到 Oracle 数据库后完成以下作业: 1. 使用一条 SQL 语句创建表 emp 的副本,表结构和 emp 表相同,但不包含任何数据。 2. 使用一条 SQL 语句将 emp 表中的数据插入到刚才创建的表的副本中。 专题四 数据库对象 翰子昂系列理论教材 - 87 - 专题四 数据库对象 1. 教学目标 1.1 掌握同义词的创建和使用 1.2 掌握序列的创建和使用 1.3 创建视图 1.4 理解索引原理 1.5 会根据系统需要创建相应类型的索引 2. 工作任务 2.1 使用同义词简化 SQL 语句,并隐藏对象的所有者 2.2 使用序列实现自增主键 2.3 使用视图隐藏数据的复杂性和简化 SQL 语句,提供数据的安全性 2.4 使用索引加快表的查询 3. 相关实践知识 3.1 使用同义词 在 SCOTT 用户下存在一个 emp 表,由于应用程序的需要,需要将对表 emp 表的访问权 限授权给 test 用户,但不能让 test 用户知道表名是 emp 和表的所有者是 SCOTT,这些信息需 要保密。 1. 以 SYS 身份登录数据库,创建 test 用户并赋予相应的权限,在 SQL 提示符下输入如 下代码: CREATE USER test IDENTIFIED BY test; GRANT CONNECT ,RESOURCE,CREATE SYNONYM TO test; 按回车键,系统显示用户创建成功和授权成功。 2. 以 test 用户的身份登录到数据库,在 SQL 提示符下输入如下代码: CREATE SYNONYM staff FOR SCOTT.EMP; 按回车键,系统显示同义词创建成功。 3. 完成以上操作后,系统管理员把 test 帐号分配给其他的数据库用户使用。 4. 其他用户以 test 身份登录到数据库。在 SQL 提示符下输入如下代码: Oracle 数据库案例教程_教师用书 - 88 - 翰子昂系列理论教材 SELECT * FROM staff; 系统将显示 SCOTT 用户下的表 emp 中的数据。而此时的 test 用户却并不知情,他以为 操作的是自己模式下的一个名叫 staff 的表。 5. 假如现在 SCOTT 用户下的 emp 表需要被所有数据库中的用户访问、使用。相当于应 用程序中的全局变量。要解决这种问题,需要创建公有同义词。SCOTT 用户没有创建公有同 义词的权限,需要用 SYS 或 SYSTEM 用户来创建。 从开始菜单中打开 SQL*Plus 工具,以 SYS 用户的身份登录到数据库,在 SQL 提示符下 输入如下代码: CREATE PUBLIC SYNONYM EMP FOR SCOTT.EMP; GRANT SELECT ON EMP to PUBLIC; 通过上面两句代码,便创建了一个名为 EMP 公用同义词,这样所有用户都可以直接访问 EMP 这个同义词了。 注意: 所有的共有同义词都属于 PUBLIC 模式。虽然可以通过 SYS 创建,但查 找 SYS 的数据库字典 USER_SYNONYMS 中没有。通过查看 DBA_SYNON- YMS,显示创建的共有同义词的 OWNER 为 PUBLIC。 理论知识: 同义词是现有对象的一个别名。同义词可以简化 SQL 语句的书写,隐藏对象的名称和所 有者,并提供对对象的公共访问。 同义词分为:私有同义词和公有同义词。普通用户可以创建私有同义词,只有具有 CR- EATE PUBLIC SYNONYM 权限的用户才能创建公有同义词。所有创建的公有同义词属于 PU- BLIC 模式。 公有同义词可被所有的数据库用户访问。 私有同义词只能在其模式内访问,且不能与当前模式的对象同名。 当在某一模式下,当私有同义词和某一公有同义词重名时,系统优先使用私有同义词。 创建和替换同义词的语法为: CREATE [OR REPLACE] [PUBLIC] SYNONYM sysnonym_name FOR [SCHEMA.]OBJECT; 其中,sysnonym_name 是同义词的名称,SCHEMA 是模式,OBJECT 是要创建同义词 的对象的名称。 删除同义词的语法为: DROP [PUBLIC] SYNONYM sysnonym_name; 当删除公有同义词时需要加关键字 PUBLIC。 专题四 数据库对象 翰子昂系列理论教材 - 89 - 3.2 使用序列实现自增主键 在 SQL SERVER 数据库中,创建表的时候,可以很方便地将表的主键设置为自增主键, 这样我们在对表操作时,就不用担心主键的问题。在 Oracle 中如何实现呢?看下面步骤: 从开始菜单中打开 SQL*Plus 工具,以 SCOTT 用户的身份登录到数据库。 1. 在 SQL 提示符下输入如下代码: CREATE TABLE student ( xh number primary key, Name varchar2(10) not null ); 按回车键,系统提示“表已创建”。 2. SQL 提示符下,输入以下代码: CREATE SEQUENCE student_seq START WITH 1 INCREMENT BY 1; 按回车键,系统提示“序列已创建”。这样就创建了一个自增的序列,此序列从 1 开始, 每次增长 1。 3. 向 student 表中插入数据,输入代码如下: INSERT INTO student VALUES (student_seq.nextval,’zhangsan’); 按回车键,系统提示“已创建 1 行”。 4. 重复步骤 3,向表中在插入一条记录。 5. 为了保存上面的插入,需要执行提交上面的修改,在 SQL 提示符下,输入如下代码: COMMIT; 按回车键,系统提示“提交完成”。 6. 查看表中的数据,在 SQL 提示符下,输入如下代码: SELECT * FROM student; 系统显示如图 4-1 所示。 Oracle 数据库案例教程_教师用书 - 90 - 翰子昂系列理论教材 图 4-1 使用序列向表中插入记录后表中的数据 7. 查看序列的当前值,在 SQL 提示符下,输入如下代码: SELECT student_seq.currval FROM dual; 系统显示如图 4-2 所示。 图 4-2 显示序列的当前值 8. 在 SQL Server 中创建好自增主健,一般在插入数据时,对主键列不需要插入,系统会 自动完成,在 Oracle 中是否可以呢? 在 SQL 提示符下,输入如下代码: INSERT INTO student values(‘hhhh’); 这样的语句在 SQL Server 中,可以执行成功,主键将列自动插入值。 但是在 Oracle 中,输入上面的语句后,按回车键,系统提示如下信息: INSERT INTO student values('hhhh') * 第 1 行出现错误: ORA-00947: 没有足够的值 出现这个问题的原因是:表里面有两个字段,而插入语句中只给出了一个值,并且没有 指明语句中的值是给哪个字段。 9. 修改上面的语句,在表名的后面,写上相应的字段名。在 SQL 提示符下,输入如下代 码: INSERT INTO student(name) values(‘hhhh’); 专题四 数据库对象 翰子昂系列理论教材 - 91 - 按回车键,系统提示如下信息: INSERT INTO student(NAME) values('hhhh') * 第 1 行出现错误: ORA-01400: 无法将 NULL 插入 ("SCOTT"."STUDENT"."XH") 因为表的主键列不能为空。 从上面的测试,可以发现,到目前为止不能象 SQL Server 中那样插入数据。那如何才能 达到那种效果呢? 在同学们学习完触发器之后,请同学们想办法解决。 理论知识: 序列是用于生成唯一、连续整数的对象,创建序列时可以指定是升序的或是降序的。序 列可以用来生成自增的主键。 序列是一种数据库对象,可以被多个用户访问用于生成唯一的整数。 当一个序列值生成后,序列就已经增长了,且不能回滚,也就是说只要序列值增长后, 不管用户是提交还是回滚事务,序列值都已经增长了。假如两个用户并发的访问同一个序列, 每个用户得到的序列值可能是不连续的,因为另一个用户生成了序列的值。其中某个用户永 远不能获取另一个用户产生的序列值。 序列产生的值独立于表,所以一个或多个表可以使用同一个序列。即使使用同一序列, 序列的值也不能保证连续,比如虽然产生了序列值,但用户最终回滚了该事务。 当一个序列创建好之后,可以在 SQL 语句中使用 nextval 和 currval 伪列来访问数据。 创建序列的语法 CREAGE SEQUENCE 序列名称 [InCrement By 间隔] [Start With 起始数] [MaxValue 最大值] [MinValue 最小值] [Cycle|nocycle ] [Cache n|nocache] 要点:  InCrement By:序列每次增长值,可以是正数或负数(递减序列),但不能为 0, 默认为 1。  Start With:序列的第一个值,对增长序列,默认为序列的最小值,对下降序列, 默认为序列的最大值。  MaxValue:序列可以产生的最大值,如果忽略,则为 NUMBER 数据类型的最大值, 为 1027。  MinValue:序列生成的最小值,如果是增长序列,最小值等于开始值。 Oracle 数据库案例教程_教师用书 - 92 - 翰子昂系列理论教材  Cycle:序列到达最大值后是否从头开始。  Cache:提前产生指定个数的序列值存放在缓冲区中,当需要时直接从缓冲区获得, 用于提高性能。 序列的使用 对序列的操作主要是通过在 SQL 语句中使用 nextval 和 currval 伪列来完成的。可以 从伪列中选择值,但不能操作它们的值。  nextval:创建序列后第一次使用 nextval 时,将返回序列的初始值。以后再引 用 nextval 时,将使用 INCREMENT BY 子句的值来增加序列值,并返回这个新值。  currval:返回序列的当前值,即最后一次引用 nextval 时返回的值。 序列的更改 通过 ALTER SEQUENCE 命令来修改序列的定义,但不能更改序列的 START WITH 值, 语法如下: ALTER SEQUENCE 序列名称 [InCrement By 间隔] [MaxValue 最大值] [MinValue 最小值] [Cycle|nocycle] [Cache n|nocache]; 用户可以通过查询 USER_SEQUENCES 的数据字典来获取当前用户创建的序列信息。 3.3 使用视图 1. 经常要对 SCOTT 用户下的 emp 和 dept 表进行联合查询,每次都要做表的连接,写同 样的一串语句,同时由于工资列的数据比较敏感,对外要求不可见。对这样的问题可以通过 创建视图来解决。 在 SQL 提示符下,输入如下代码: CREATE VIEW dept_emp AS SELECT dept.deptno,dept.dname,dept.loc, emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate FROM emp,dept WHERE emp.deptno=dept.deptno; 按回车键,系统提示如下信息: CREATE VIEW DEPT_EMP * 第 1 行出现错误: ORA-01031: 权限不足 专题四 数据库对象 翰子昂系列理论教材 - 93 - 说明 SCOTT 用户没有创建视图的权限,需要授予创建视图的权限。使用 SYS 或 SYST- EM 登陆数据库,进入 SQL 提示符下,输入如下命令: GRANT CREATE VIEW TO SCOTT; 按回车键,系统提示“授权成功”。 然后重新用 SCOTT 登录数据库,将创建视图的语句复制到 SQL 提示符下,按回车键, 系统提示“视图已创建”。 关于系统权限的几个数据字典:USER_SYS_PRIVS、USER_role_PRIVS、role_sys_privs。 其中 USER_SYS_PRIVS 表示当前用户具有的系统权限,在 SQL 提示符下,输入如下代码: select * from USER_SYS_PRIVS; 按回车键,系统提示如下信息: USERNAME PRIVILEGE ADM ------------------------------ ---------------------------------------- --- SCOTT CREATE VIEW NO SCOTT UNLIMITED TABLESPACE NO SCOTT CREATE PUBLIC SYNONYM NO 表示当前用户具有三项系统权限,其中有两项是在前面的操作中授予的。字段 ADM 表 示是否允许将权限在赋予其他用户,是否指定了 WITH ADMIN OPTION。 查看当前用户被授予的角色(权限的集合),输入如下代码: select * from USER_role_PRIVS; 按回车键,系统提示如下信息: USERNAME GRANTED_ROLE ADM DEF OS_ ------------------------------ ------------------------------ --- --- --- SCOTT CONNECT NO YES NO SCOTT RESOURCE NO YES NO 表明当前用户具有两项角色,对于普通用户只要具有这两项角色就可以了。一般在刚创 建完用户后,需要赋予这两个角色。那每个角色包含什么权限呢? select * from role_sys_privs where role='RESOURCE'; 按回车键,系统提示如下信息: ROLE PRIVILEGE ADM ------------------------------ ---------------------------------------- --- RESOURCE CREATE SEQUENCE NO RESOURCE CREATE TRIGGER NO RESOURCE CREATE CLUSTER NO Oracle 数据库案例教程_教师用书 - 94 - 翰子昂系列理论教材 RESOURCE CREATE PROCEDURE NO RESOURCE CREATE TYPE NO RESOURCE CREATE OPERATOR NO RESOURCE CREATE TABLE NO RESOURCE CREATE INDEXTYPE NO 此角色包含了创建一些数据库对象的权限,注意到这里没有 CEATE VIEW 的权限,因 此需要单独授权。 select * from role_sys_privs where role='CONNECT'; 按回车键,系统提示如下信息: ROLE PRIVILEGE ADM ------------------------------ ---------------------------------------- --- CONNECT CREATE SESSION NO CONNECT 角色只包含一项权限,即创建会话(连接到数据库服务器上)的权限。 注意: 以上操作是在 Oracle 10g Release 2 版本中完成的,如果使用低版本的 Oracle,则可能无需此授权操作。 2. 查询部门名称为“SALES”的部门的所有员工信息,在 SQL 提示符下,输入如下代码: SELECT empno,ename,job,mgr,hiredate FROM dept_emp WHERE dname=’SALES’; 按回车键,系统显示如图 4-3 所示。 图 4-3 显示对视图的查询结果 3. 公司财务人员经常要查询公司中每个部门的平均工资,要求显示部门名称和平均工资 两列,对于这样的操作,可以先创建一个视图,然后所有操作基于视图来做,这样就简化了 专题四 数据库对象 翰子昂系列理论教材 - 95 - SQL 语句,提高了效率。 在 SQL 提示符下,输入如下语句: CREATE VIEW dept_sal_avg AS SELECT dname,avg(sal) avg_sal FROM dept,emp WHERE dept.deptno=EMP.DEPTno GROUP BY dname; 按回车键,系统提示“视图已创建”。 4. 查询部门平均工资,SQL 提示符下,输入以下代码: select * from dept_sal_avg; 按回车键,系统显示如图 4-4 所示: 图 4-4 显示对视图的查询结果 5. 对上面创建的视图(dept_sal_avg)进行修改,修改部门名称是“SALES”的平均工资 为 2000,在 SQL 提示符下,输入如下代码: UPDATE dept_sal_avg SET avg_sal=2000; 按回车键,系统提示“此视图的数据操纵操作非法”。 大家考虑,为什么不能对这个视图修改。视图本身不存储数据,视图是存储的查询,换 句话说视图存储的是一条 SQL 语句。视图中的平均工资是通过计算本部门的工资得出的,直 接修改平均工资,是没有意义的,而且是不允许的。 6. 对第一个视图 dept_emp 进行修改,更新雇员编号是 7934 的雇员的姓名为“TOM”, 在 SQL 提示符下,输入如下代码: UPDATE dept_emp SET ename=’TOM’ WHERE empno=7934; Oracle 数据库案例教程_教师用书 - 96 - 翰子昂系列理论教材 按回车键,系统显示“已更新 1 行”。 7. 查看修改后的结构,首先看视图中的内容,在 SQL 提示符下,输入如下代码: SELECT ename FROM dept_emp WHERE empno=7934; 按回车键,系统显示如下: ENAME ---------- TOM 8. 查看基表中的数据是否改变,在 SQL 提示符下,输入如下代码: SELECT ename FROM emp WHERE empno=7934; 按回车键,系统显示如下: ENAME ---------- TOM 说明:对视图的修改本质上对基表的修改,因为视图本身不存储数据。对视图修改会转 化为相应对基表的操作。 9. 取消上面对数据的修改,在 SQL 提示符下,输入如下代码: ROLLBACK; 10. 对第一个视图 DEPT_EMP 进行修改,更新部门编号是“10”的部门的部门名称为 “finance”,在 SQL 提示符下,输入如下代码: UPDATE DEPT_EMP SET dname=’ finance’ WHERE DEPTno=10; 按回车键,系统显示“无法修改与非键值保存表对应的列”。 同样是对视图的修改,修改雇员名称可以,修改部门名称却不可以。因为在 DEPT_EMP 视图中,EMP 表是键保留表,可以修改,而 DEPT 表不是,不能修改。 11. 如何知道视图中,哪些列是可以修改的?哪些列是不可以修改的呢?通过查询数据字 典表 user_updatable_columns 来得知,在 SQL 提示符下,输入如下代码: SELECT column_name,updatable,insertable,deletable FROM user_updatable_columns 专题四 数据库对象 翰子昂系列理论教材 - 97 - WHERE table_name=’DEPT_EMP’; 注意:这里 table_name 字段的值一定要大写。 按回车键,系统显示结果如图 4-5 所示。 图 4-5 数据字典的查询结果 理论知识: 视图是一种数据库对象,用户可以象查询普通表一样查询视图。视图内其实没有存储任 何数据,它只是对表的一个查询。在以查询为主要操作的大多数情况下可以使用视图来代替 普通表。视图的定义保存在数据字典内。创建视图所基于的表为“基表”。 视图具有以下优点:  可以限制对表中敏感记录和字段的访问,从而提供了除权限管理外的另一种安全级 别。  视图隐藏了数据的复杂性,如表中有几十个字段,但视图中只选择常用的几个。  视图简化了用户的查询命令,如定义一个连接视图(视图中的数据来自多个基表), 然后用户的操作基于视图来完成。这样用户在写 SQL 语句时只需要从视图中取数据, 而不用再向没有视图前写表和表之间的连接语句。  视图将应用程序与基表定义的修改隔离开来,如一个视图引用了一个拥有 5 列表中 的 4 个列,当用户向该表中添加第 6 个列时,应用程序将不受影响,而且对应用程 序来说此修改是透明的。  视图通过重命名列,从另一个角度(相对于基表)提供了数据,并且不会影响基表。 创建视图的语法如下: CREATE [OR REPLACE][FORCE|NOFORCE] VIEW 视图名 [(alias [,alias ] … )] Oracle 数据库案例教程_教师用书 - 98 - 翰子昂系列理论教材 AS 查询语句 [WITH CHECK OPTION [CONSTRAINT 约束名]] [WITH READ ONLY]; 下面对语法中的关键字进行解释。  OR REPLACE:用新的视图定义替换原有视图的定义。  FORCE:创建带有错误的视图,比如查询语句中的基表或字段都可以不存在。  NOFORCE:默认值,只有基表存在时才能创建视图。  alias:在查询语句中选择的字段,可以在视图中给出别名,需要与查询语句中字 段的个数一致。  查询语句:视图对应的 SELECT 语句,此 SELECT 语句可以包含各种函数、表达式, 也可以包含 ORDER BY、GROUP BY 等子句,也可以是连接查询。  WITH CHECK OPTION:此选项指定只能插入或更新视图可以访问的行,换句话说 就是对视图内记录的修改,不会改变视图内记录的个数。术语 constraint 表示为 CHECK OPTION 约束指定的名称,就是视图查询语句中的 WHERE 条件。  WITH READ ONLY:此选项确保不能在此视图上执行任何修改操作。 如果在 CREATE VIEW 语句中使用 FORCE 选项,即使存在下列情况,Oracle 也会创建 视图。  视图定义的查询引用了一个不存在的表。  视图定义的查询引用了现有表中无效的列。  视图的所有者没有所需的权限。 在这些情况下,Oracle 仅检查 CREATE VIEW 语句中的语法错误。如果语法正确,将 会创建视图,并将视图的定义存储在数据字典中。但是,该视图却不能使用。这种视图被认 为“带错误创建”的。 等视图依赖的相关资源创建后,需要使用 ALTER VIEW 命令手动编译视图。 ALTER VIEW 视图名 COMPILE; 视图的修改 视图主要用作查询,应尽可能少地对视图进行增删改操作。如果需要对视图进行增删改 操作,建议使用 INTTEAD OF 触发器来完成。下面简要地讨论关于视图的修改: 用于修改表数据的 INSERT、DELETE 和 UPDATE 语句也可以用于视图。因为视图是一 个虚拟的表,所以这些语句也可与视图一同使用。如果一个视图基于单个基表,那么可以在 此视图中进行 INSERT、DELETE 和 UPDATE 操作,系统会自动将对视图的修改转换为对基 表的修改。一般情况下不通过视图修改数据,而是直接修改基表,因为那样条理更清晰。在 视图上使用 DML 语句有如下限制(相对于表)。  在视图中使用 DML 语句只能修改一个的基表  如果对记录的修改违反了基表的约束条件,则无法更新视图,比如违反了主键约束 专题四 数据库对象 翰子昂系列理论教材 - 99 -  如果创建的视图包含连接运算符、DISTINCT 运算符、集合运算符、聚合函数和 G- ROUP BY 子句,则将无法更新视图  如果创建的视图包含伪列或表达式,则将无法更新视图 联接视图是在 FROM 子句中指定了多个表或视图的视图。在联接视图中使用 DML 语句只 能修改单个基表,如果修改多个基表,SQL 就会显示错误。Oracle 提供了在视图上应用的 “INSTEAD OF 触发器”,使用该触发器,可以通过视图同时对多个基表执行 DML 操作。就 是将对视图的修改,根据相关业务规则转换为对基表的修改。 在联接视图中,如果视图中某列是一个基表的主键,并且这个基表的主键也可以作为视 图的主键,则称这个键被保留了,包含这个主键的表称为键保留表。Oracle 可以通过此视 图对键保留表进行修改,包括增删改操作。包含外部联接的视图通常不包含键保留表,除非 外部联接生成非空的值。Oracle 可以确定哪些表是键保留表,只有键保留表才能使用 DML 语句。 通过数据字典视图 USER_UPDATABLE_COLUMNS,可 以 确定联接视图中哪些列是可以更 新的列。例如,要查看视图 VIEWNAME 中的哪些列可以更新,请使用以下命令: SELECT * FROM USER_UPDATABLE_COLUMNS WHERE TABLE_NAME = 'VIEWNAME’; 注意: 要识别键保留表必须确保基表的主键正确创建,否则 Oracle 不能识别键 保留表,不允许更新联接视图中的任何列。 3.4 使用索引加快表的查询 1. 创建一般索引。由于总是要根据 ename 查询某个员工的信息,所以在 emp 表的 ename 列上创建索引。以 SCOTT 身份登录到数据库后,在 SQL 提示符下输入以下代码: CREATE INDEX idx_name ON emp(ename); 按回车键,系统提示“索引已创建”。 创建好索引之后,是不是所有的 SQL 语句都会使用索引呢? 理论知识: 索引是与表关联的可选结构。通过有目的的创建索引,可以加快对表执行 SELECT 语句 的速度。就像书的目录(索引)可以帮助我们更快地查找信息一样,Oracle 中的索引也提 供了一种更快地访问表数据的途径。当索引列用于 SELECT 语句的 WHERE 子句中时,该索引 将通过 ROWID 直接指向包含这些值的行的位置。合理使用索引是减少磁盘 I/O 的主要方法。 不管索引是否存在,都无需修改任何 SQL 语句的书写方式。索引只是一种快速访问数据的途 径,它只影响查询执行的效率。可以使用 CREATE INDEX 命令在一列或若干列的组合上创建 索引。 给定一包含若干条记录的数据集,如果数据集是没有规则排列的,也就是无序的,对这 个数据集的查找只能是从头到尾的遍历整个数据集,当数据集很大的时候,遍历的效率是很 低的。但如果数据集是根据某关键字排序的,而且查找正好是根据关键字来查询,那就可以 Oracle 数据库案例教程_教师用书 - 100 - 翰子昂系列理论教材 使用二分法查找来提高效率。Oracle 数据库就是根据以上原理来提高查询效率的。 索引和表是单独存储的,更好的情况是和表存储在不同的表空间(硬盘),这样就可以 并行执行,从而较少磁盘竞争,提高查询效率。 创建普通索引的语法如下: CREATE INDEX index_name ON table_name(column_list) [TABLESPACE tablespace_name]; 其中,index_name 指所创建的索引的名称;table_name 表示为之创建索引的表名; column_list 是在其上创建索引的列名列表,可以基于多列创建索引。tablespace_name 为索引指定表空间。 创建索引时,Oracle 将获取要创建索引的列,并对其进行排序。然后,将 ROWID 连同 每一行的索引值存储起来,组成键值对(目录名和页码)。使用索引时,Oracle 首先通过 已排序的列值执行快速搜索,然后使用相关联的 ROWID 值来定位具有所要查找值的行。 索引在逻辑上和物理上都独立于关联表中的数据,在任何时候都可以创建或删除索引, 而不会影响基表或其它索引。如果删除索引,所有的应用程序都将继续运行,但在访问原先 被索引的数据时,速度可能会降低。与视图不同的是,索引是独立的结构,因此需要存储空 间。 一旦创建了索引,Oracle 会自动维护和使用它们。只要修改了数据,如添加新行、更 新现有行或删除行,Oracle 都会自动更新索引。但是为表创建过多的索引会降低更新、删 除以及插入的性能,因为 Oracle 还必须更新与该表关联的索引。 问题: 请大家思考一个问题,现在要将几百万条数据导入某个表中,是在导入 数据之前先为表建好索引,还是等数据导入完成后再给表创建索引? 在 Oracle 中,通过查询获取数据有两种方式,一种是全表扫描(TABLE SCAN),一种 是索引扫描(INDEX SCAN)。何时使用全表扫描,何时使用索引扫描一般情况下是由 Oracle 数据库决定的。 即使在表中的某列上创建了索引,并且在 WHERE 条件中显示地包含此列,Oracle 也不 一定会使用索引。对于小表来说,通过 Oracle 的一次读操作可以全部放入内存中,就没有 必要建索引,使用索引反而会降低效率。并且索引应该建立在高基数列上,列的区分度较高。 一般情况下当查询出的数据占总数据的比例<2%时,索引效果最佳。 索引的类型主要有:唯一索引、组全索引、反向键索引、位图索引等。 2. 为了测试查询语句是否使用索引,做如下操作。首先创建 PLAN_TABLE。SQL 提示 符下输入以下代码: @%ORACLE_HOME%/RDBMS/ADMIN/UTLXPLAN.SQL 按“Enter”键后,系统提示“表已创建”。 专题四 数据库对象 翰子昂系列理论教材 - 101 - 3. 为了只显示执行计划,不显示查询结果。当表很大时,显示查询结果很慢。在 SQL 提 示符,输入以下代码: SET AUTOTRACE TRACE EXPLAIN; 4. 在 SQL 提示符,输入以下代码: SELECT * FROM emp; 按“Enter”键后系统显示如图 4-6 所示。 图 4-6 执行计划 1 从上可以看出,系统采用全表扫描,而不是索引查找。 5. 在 SQL 提示符,输入以下代码: SELECT * FROM emp WHERE ename='dsfsdfds'; 按“Enter”键后系统显示如图 4-7 所示。 Oracle 数据库案例教程_教师用书 - 102 - 翰子昂系列理论教材 图 4-7 执行计划 2 从执行计划可以看出,数据库使用索引来查找数据。 通过 4、5 的操作说明,只有在查询条件中显示地使用索引列时,才会使用索引。 6. 在 SQL 提示符,输入以下代码: SELECT * FROM emp WHERE ename<>'dsfsdfds'; 按“Enter”键后系统显示如图 4-8 所示。 图 4-8 执行计划 3 从执行计划可以看出,数据库使用全表扫描来查询数据,而没有使用索引。 专题四 数据库对象 翰子昂系列理论教材 - 103 - 请同学们思考,为什么步骤 6 没有使用索引而步骤 5 使用索引。 7. 要求 emp 表的 ename 列不允许有重复值,可以在 ename 列上创建唯一性索引,在 S- QL 提示符下,输入以下代码: CREATE UNIQUE INDEX ind_ename ON EMP(ename); 按“Enter”键后,系统显示“此列列表已索引”。说明在一列上只能建一个索引。 理论知识: 创建索引时,使用的列的值可以是唯一的、也可以是非唯一的。唯一索引可以确保在此 列中,表的任意两行的值不能相同。非唯一索引没有在列值上规定此限制。Oracle 自动为 表的主键列创建唯一索引。 创建在非主键列上的唯一性索引,如果没有创建 NOT NULL 约束,可以在列中使用 NU- LL,但在所有列上只能由一个 NULL。 使用唯一索引可以保证创建索引的列上不会出现重复值。 可以使用 CREATE UNIQUE INDEX 命令创建唯一索引。 8. 要求在 SQL 提示符下,输入以下代码: DROP INDEX idx_name; 按“Enter”键后,系统显示“索引已丢弃”。 重新操作步骤 7,系统提示“索引已创建”。这样就保证了 ename 字段没有重复值。 9. 如果用户在查询 emp 表时,总是根据 job 和 deptno 两列进行查询,那就可以在 emp 表的这两列上建立组合索引,输入以下代码: CREATE INDEX COMP_INDEX ON emp(job,deptno); 按回车键,系统提示“索引已创建”。 理论知识: 组合索引是在表中的多个列上创建的索引。组合索引中列的顺序是任意的,可以是相邻 的列,也可以是不相邻的列。 如果 SELECT 语句中的 WHERE 条件中引用了组合索引中的所有列或按顺序前面的某些 列,则组合索引可以提高数据检索的速度。创建索引时,应注意定义中使用的列的顺序。通 常,最经常访问的列应放置在列表的最前面。 10. 在 SQL 提示符下,输入以下代码: SELECT * FROM emp WHERE deptno=10 and job='dsfa'; 按“Enter”键后,系统提示如图 4-9 所示。 Oracle 数据库案例教程_教师用书 - 104 - 翰子昂系列理论教材 图 4-9 执行计划 4 以上信息显示在查询表的时候使用了刚刚创建的组合索引。 11. 在 SQL 提示符下,输入以下代码: SELECT * FROM emp WHERE deptno=10; 按“Enter”键后,系统提示如图 4-10 所示。 图 4-10 执行计划 5 以上信息显示在查询表的时候是全表扫描,而没有使用索引。 12. 在 SQL 提示符下,输入以下代码: 专题四 数据库对象 翰子昂系列理论教材 - 105 - SELECT * FROM emp WHERE job='dsfa'; 按“Enter”键后,系统提示如图 4-11 所示。 图 4-11 执行计划 6 以上信息显示在查询表的时候使用了刚刚创建的组合索引。 通过对操作 11、12 的比较,可以发现虽然在 job 和 deptno 列上创建了组合索引,但并不 是只要在查询条件中有列的信息就使用索引,而是有先后顺序的。在创建索引时的顺序应该 是最频繁访问的列放置在列表的最前面。 13. 当数据的查询或插入修改正好分布在某一个较小的连续区域,往往会由于数据过于 密集而降低读取效率。这时就可以创建反向键索引,将集中在一个小区域上的查询平均分布 在整个索引上,从而提高效率。 CREATE INDEX rev_index ON emp(deptno) REVERSE; 按回车键,系统提示“索引已创建”。 14. 使用关键字 NOREVERSE 可以将反向键索引重建为标准索引。在 SQL 提示符下,输 入以下代码: ALTER INDEX rev_index REBUILD NOREVERSE; 按回车键,系统提示“索引已更改”。 理论知识: 反向键索引是一种特殊类型的索引,当索引基于某种有规则的序数时是非常有用的,如 果一个标准索引基于一个含有这种数据的列,当操作集中于序数的某一部分时,往往会因为 操作频繁而降低读取性能,因为每次数据操作都会带来对索引的维护,而其他操作则要等待 维护完成后再进行。比如在建有自增主键的列上。当插入操作特别频繁的时候,所有插入数 据的关键字都集中在索引的末端,都要向末端插入数据,就需要排队等待,从而降低了系统 Oracle 数据库案例教程_教师用书 - 106 - 翰子昂系列理论教材 的效率。 反向键索引通过简单的反向被索引的列中的数据来解决问题,首先反向每个列键值的字 节,然后在反向后的新数据上进行索引,而新数据在值的范围上的分布通常比原来的有序数 更均匀。比如向表中插入关键字为 1234、1235、1236 的列,如果是标准索引,这三个关键 字肯定会被存放在一起,但反转后变为 4321、5321、6321,从而使这些关键字均匀的分布 在索引上。因此,反向键索引通常建立在一些值连续增长的列上,例如列中的值是由序列产 生的情况。 可以在 CREATE INDEX 语句中指定关键字 REVERSE 创建反向键索引。 也可以将反向键索引转换为标准索引,语法如下: ALTER INDEX 索引名 REBUILD NOREVERSE; 15. 前面使用的索引都是创建在高基数列上,也就是在此列上重复值较少。对低基数列 上可以创建位图索引,来提高查询效率。 CREATE BITMAP INDEX bit_index ON emp(job); 按回车键,系统提示“索引已创建”。 理论知识: 要想提高索引的效率,索引一般建立在高基数列上,但如果用户的查询总是基于某一个 低基数列来进行,有没有适合与低基数列的索引呢? 使用位图索引的优点在于,它最适用于低基数列,也就是不同值的数目比表的行数少的 列。如果某个列的值重复了超过一百次,则可以考虑在该列上创建位图索引。例如,如果一 个表有 100 万行,其中一个列有小于 1000 个不同的值,则可以考虑在该列上创建位图索引。 可以使用 CREATE BITMAP INDEX 命令来创建位图索引。 在位图索引中使用每个键值的位图,而不使用 ROWID 列表。位图中的每个位对应一个可 能的 ROWID,如果设置了这个位,则意味着拥有此 ROWID 的行包含该键值。映射函数将位 的位置转换为实际的 ROWID,虽然位图索引内部使用的表示方法与普通索引不同,但提供的 功能却与普通索引相同。如果不同键值的数目很少,位图索引的空间效率会很高。位图索引 具有下列优点:  对于大批量查询,可以减少响应时间。  相比其它索引技术,占用空间明显减少。  即使在配置很低的终端硬件上,也能获得显著的性能。 使用标准索引对一个大型表进行完全索引极其浪费空间,因为索引可能比表中的数据大 几倍,而位图索引的大小通常仅是表中被索引数据的一小部分。在即席查询或类似情况下, 位图索引可以显著地提高查询性能。在将所生成的位图转换成 ROWID 之前,通过在位图上直 接执行相应的布尔运算,可以快速地解析查询的 WHERE 子句中的 AND 和 OR 条件。如果查询 结果中的行的数目很少,可以很快地响应查询,而不必对表进行完全扫描。 位图索引不应当用在频繁发生 INSERT,UPDATE,DELETE 操作的表上,这些 DML 操作 在性能方面的代价很高。它们会引起位图级的加锁发生,而且要求动态重建所有可能值的位 专题四 数据库对象 翰子昂系列理论教材 - 107 - 图。位图索引最适合于数据仓库和决策支持系统。 16. 前面建立的各种索引都是与表关联的可选结构,查询时先在索引中查找,然后根据 ROWID 到表中取数据。每次查询都要重复这样的操作。如果表不是太大,并且主要以查询操 作为主时,能不能考虑将表的数据也存储在索引中,从而提高性能。Oracle 提出了索引组织 表的概念。数据存储在索引中原来存放 ROWID 的位置。 在 SQL 提示符下,输入以下代码: CREATE TABLE ind_org_table (id number(5) primary key, Name varchar2(10) ) ORGANIZATION INDEX; 按回车键,系统提示“表已创建”。 索引组织表要求一定要有主键,查询是基于主键列的。 对索引组织表的操作和普通表一样。 理论知识: 索引组织表与普通表的不同之处在于,该表的数据存储在与其关联的索引中。对表数据 进行的修改,如添加新行、更新行或删除行,只会导致对索引的更新。 索引组织表与在一个或多个列上建立索引的普通表相似,但它无需为表和索引维护两个 单独的存储空间,数据库系统仅维护一个索引,该索引包含相应行的已编码键值和与其关联 的列值。 行的实际数据存储在索引中,而不是将行的 ROWID 作为索引条目的第二个元素。可以使 用带有 ORGANIZATION INDEX 子句的 CREATE TABLE 命令来创建索引组织表。 索引组织表适合于通过主键来访问数据。与唯一索引一样,索引组织表没有重复的键值, 因为只有非键列的值与该键存储在一起。 应用程序可以使用 SQL 语句来操纵索引组织表,就像操纵普通表一样。但是,数据库系 统通过操纵相应的索引来执行所有操作。 索引组织表的优点如下:  由于行存储在索引中,对于要求精确匹配的查询和基于范围的搜索,索引组织表提 供了一种更快的、基于键的表数据访问方法;  由于键列在表和索引中不重复,因此降低了存储需求;  与键一起存储的数据行仅包含非键列的值。而且将数据行同键存储在一起,还可以 消除 ROWID 所需的额外存储空间,而对于普通表的索引,需要使用 ROWID 将键值 与相应的行链接起来。 17. 下面的几步用来比较索引创建前后对表执行查询的效率。 以 SCOTT 身份登录到数据库,用如下的语句创建一个表 t_testidx。 CREATE TABLE t_testidx ( Oracle 数据库案例教程_教师用书 - 108 - 翰子昂系列理论教材 id NUMBER, name VARCHAR2(10) ); 18. 用如下的 PL/SQL 程序向表 t_testidx 中插入 100 万条记录。(PL/SQL 将在后面专题 5 中介绍)。 BEGIN FOR l_temp IN 1..1000000 LOOP INSERT INTO t_testidx VALUES(student_seq.nextval,'abcde'); END LOOP; END; 其中,student_seq 是 3.2 中创建的序列。 19. 在 SQL 提示符下输入如下的语句以显示程序的执行时间。 SET TIMING ON 20. 执行如下的查询命令。 SELECT * FROM t_test WHERE id=28900; 系统显示的执行时间是 0.48 秒。(不同的机器时间会有所不同) 21. 用如下的代码为表 t_testidx 设置索引。 CREATE INDEX idx_t_testidx_id ON t_testidx(id); 22. 重新执行第 20 步的查询命令,系统显示执行的时间是 0.04 秒,可见数据量大的表创 建索引后,程序执行查询的效率大幅提高。 4. 实验 按照第 3 部分相关实践知识及提高部分依次练习,主要掌握: 1. 同义词的使用,参见 3.1(20 分钟)。 2. 序列的使用,参见 3.2(20 分钟)。 3. 视图的使用,参见 3.3(25 分钟)。 4. 索引的使用,参见 3.4(25 分钟)。 5. 课后作业 以 SCOTT 身份登陆到 Oracle 数据库后完成以下作业: 1. 基于 dept 和 emp 创建联合视图,包含两个表中的所有列,但不包含重复的列,要求视 图是根据 deptno 列排序的。 专题四 数据库对象 翰子昂系列理论教材 - 109 - 2. 创建一个序列,该序列的起始值为 100,每次减少 1,直到 0,然后重新从 100 开始。 3. 为 emp 表的 ename 和 deptno 列创建唯一组合索引。 Oracle 数据库案例教程_教师用书 - 110 - 翰子昂系列理论教材 专题五 PL/SQL 编程 1. 教学目标 1.1 了解 PL/SQL 语言的特征及优点 1.2 掌握 PL/SQL 块的基本结构 1.3 掌握 PL/SQL 中常用的数据类型 1.4 理解并掌握 PL/SQL 中控制结构的用法 1.5 理解并掌握 PL/SQL 的异常处理机制 2. 工作任务 2.1 用 PL/SQL 块来查询表内信息 2.2 用条件控制语句来实现为员工加薪 2.3 用循环控制语句来输出 1 到 100 之间能被 9 整除的整数 2.4 用顺序控制语句实现更新员工工资 2.5 按员工编号查询员工姓名——掌握 PL/SQL 的异常处理机制 3. 相关实践知识 从开始菜单中打开 SQL*Plus 工具,以 SCOTT 用户的身份登录到数据库,用 PL/SQL 完 成各项工作任务。 理论知识: PL/SQL 简介 PL/SQL(Procedural Language/Structured Query Language),即过程语言 /SQL,是结合了 Oracle 过程语言和结构化查询语言(SQL)的一种扩展语言,是 Oracle 对标准 SQL 的扩展。PL/SQL 支持多种数据类型,可以使用条件语句和循环语句等控制结构, 是过程控制结构和 SQL 的数据处理能力无缝结合形成的功能强大的编程语言。 PL/SQL 的优点 PL/SQL 是一种高性能的基于事务处理的语言,它支持面向对象编程,并为用户提供良 好的性能和高效的处理能力。它的优点主要有: 1. 支持 SQL PL/SQL 支持所有 SQL 数据类型和所有 SQL 函数,用户可以轻松地操纵数据库中的数据。 同时,PL/SQL 还支持动态 SQL,利用这种高级编程技术可以在程序运行过程中动态生成和 运行 SQL 命令,使应用程序更加灵活和通用。 专题五 PL/SQL 编程 翰子昂系列理论教材 - 111 - 2. 支持面向对象编程 面向对象的编程技术是现在软件业公认的一种较为先进的编程技术,使用这种技术开发 应用程序可以大减少建立复杂应用程序所需的时间和成本。PL/SQL 全面支持面向对象的编 程。 3. 可移植性 PL/SQL 程序可移植到场支持 ORACLE 和 PL/SQL 的任何操作系统或平台上。 4. 更好的性能 标准 SQL 是一种非过程语言,每次只能处理一条 SQL 语句,而 PL/SQL 可以则可以一次 处理整个语句块,这样就明显减少了应用程序和 Oracle 服务器之间的通信和调用,从而提 高了性能。 5. 更高的安全性 通过 PL/SQL 的过程可以对客户端和服务器之间的应用程序逻辑进行分隔,阻止客户端 操纵敏感数据。 PL/SQL 的语言特征 PL/SQL 是一种编程语言,与其它的编程语言大同小异,下面介绍一些 PL/SQL 特殊的 语言特征: 1. PL/SQL 对大小写不敏感,但 是 建议每个开发团队应该选择一个合适的编码标准,这 样可以更好地使用共享池。 2. PL/SQL 中的一些特殊符号: 符号 说明 := 赋值操作符 || 连接操作符 -- 单行注释 /* */ 多行注释 << >> 标签 .. 范围操作符 <>,!= 不等于 表 5-1 PL/SQL 中的特殊符号 3.1 用 PL/SQL 块来查询表内信息 用户输入部门编号,要查询表 dept 中对应的部门名称,用一个匿名的 PL/SQL 块来实现 此功能。 理论知识: PL/SQL 是一种块结构语言,它将一组逻辑上相关的声明和语句放在一个逻辑块中。PL /SQL 块可以分为匿名块(未在数据库中命名的 PL/SQL 块)和命名块(如过程和函数等), 本专题只讨论匿名块,过程和函数等将在以后的专题中学习。 在 PL/SQL 块中可以使用 SELECT、INSERT、DELETE、UPDATE 等 DML 语句、事务控 Oracle 数据库案例教程_教师用书 - 112 - 翰子昂系列理论教材 制语句及 SQL 函数等,但不能直接使用 CREATE、DROP 等 DDL 语句,要想使用 DDL 语句可 以借助于动态 SQL。 PL/SQL 块分为 3 个部分:  声明部分:在此可以声明块在要使用的变量、游标和自定义异常等,这些声明的作 用域仅限于块内。如果不需要声明变量或常量等,也可以忽略这一部分  执行部分:该部分是 PL/SQL 块的主体,由关键字 BEGIN 开始,所有的可执行语句 都放在这一部分,其他的 PL/SQL 块也可以嵌套在这一部分。该部分是改选项,即 每个 PL/SQL 块都必须包含一个执行部分。  异常处理部分:处理执行块时可能引发的异常。 PL/SQL 块的语法如下: [DECLARE declarations] BEGIN executable statements [EXCEPTION execption handlers] END; 1. 在 SQL 提示符下,输入以下代码: DECLARE l_deptno NUMBER(2):=10; l_dept dept%ROWTYPE; BEGIN l_deptno:=&部门编号; SELECT * INTO l_dept FROM dept WHERE deptno=l_deptno; DBMS_OUTPUT.PUT_LINE(' 部 门 编 号是'||l_deptno||' 的 部 门名称 是 '||l_dept.dname); END; / 理论知识: 声明变量:PL/SQL 中声明变量时必须指定变量的数据类型,且每条语句只能声明一个 变量。 变量声明的语法如下: variable_name DATATYPE[(SIZE)][{:=|DEFAULT }init_value]; 其中:variable_name 是变量名称,DATATYPE 表示变更的数据类型(可以是 Oracle 或 PL/SQL 数据类型),SIZE 指定变量的范围,init_value 指变量的初始值。 为变量赋值:有两种方式:一是通过赋值操作符,二是通过 SELECT…INTO 语句从数据 库中提值赋给变量。 专题五 PL/SQL 编程 翰子昂系列理论教材 - 113 - PL/SQL 中的 SELECT 语句与标准 SQL 中的 SELECT 语句有所不同:在 PL/SQL 中,每 个 SELECT 语句中都必须有 INTO 关键字(游标中的 SELECT 语句除外),用于把从数据库 中取出的值赋给变量。需要注意的是 SELECT…INTO 语句的结果必须有且只能有一行,即每 次只能从数据库的一行中提取记录,而且 SELECT 出的字段数要与 INTO 后的变量数相匹配。 如果查询没有返回行,PL/SQL 就会抛出 NO_DATA_FOUND 异常;如果查询返回多行,PL/ SQL 就会抛出 TOO_MANY_ROWS 异常(关于这两种异常在本专题后面部分有介绍)。 声明常量:常量用 CONSTANT 关键字声明,且在声明时必须被赋予初始值。语法如下: variable_name CONSTANT DATATYPE {:=|DEFAULT }value 输出语句:DBMS_OUTPUT.PUT_LINE 是一个最常用的系统过程,用于将一条信息及一 个行结束标记存储在缓冲区中。 要显示缓冲区的内容,可以先在 SQL 提示符下执行 SET SERVEROUT ON 命令 该过程有一个输入参数,以接收要显示的信息。 PL/SQL 块的执行:匿名 PL/SQL 块的执行:可以用反斜杠“/”或 RUN 命令来执行, 过程和函数等的执行在后面的专题中讨论。 常用数据类型 PL/SQL 除了支持前面专题二中介绍过的 Oracle 数据类型外(对原 Oracle 数据类型 有的在精度上有所变化),还支持一些附加类型,如布尔型、属性类型等。概括来讲,PL/- SQL 提供的数据类型主要有:标量数据类型、属性数据类型、LOB 数据类型、复合数据类型 和引用数据类型等。本书主要讨论前面两种。 1. 标量数据类型 标量数据类型用来存储单个值,没有内部组件。 标量数据类型可分为四类:数字数据类型、字符数据类型、日期时间数据类型和布尔数 据类型。  数字数据类型 数字数据类型存储的数据为数字,用此数据类型存储的数据可用于计算。 表 5-2 中列出常用的数字数据类型。 数据类型 描述 NUMBER[(precision,scale)] 可用来存储整数、定点数和浮点数。Precision 是精度,scale 是小数位数, 最高精度是 38 个十进制位。如果不指定精度,默认为 38 位。 DECIMAL NUMBER 的子类型,用于存储最高精度 38 位的定点数。 FLOAT NUMBER 的子类型,用于存储最高精度 38 位的浮点数。 REAL NUMBER 的子类型,用于存储最高精度 18 位的浮点数。 INTEGER,INT NUMBER 的子类型,用于存储最高精度 38 位的整数。 BINARY_FLOAT 10g 中新增的数据类型,用来存储最高精度 32 位的浮点数,一般比 NUMBER 类型速度更快,占用 5 个字节的存储空间。 BINARY_FLOAT 10g 中新增的数据类型,用来存储最高精度 64 位的浮点数,一般比 Oracle 数据库案例教程_教师用书 - 114 - 翰子昂系列理论教材 NUMBER 类型速度更快,占用 9 个字节的存储空间。 BINARY_INTEGER 用于存储带符号的整数,大小范围介于-231-1 和 231-1 之间。 NATURAL BINARY_INTEGER 的子类型,可用于存储非负整数,即自然数。 NATURALN BINARY_INTEGER 的子类型,可用于存储自然数,且不能为空。 POSITIVE BINARY_INTEGER 的子类型,可用于存储正整数。 POSITIVEN BINARY_INTEGER 的子类型,可用于存储正整数,且不能为空。 SIGNTYPE BINARY_INTEGER 的子类型,只能存储-1、0 和 1。 PLS_INTEGER 用于存储带符号的整数,大小范围介于-231-1 和 231-1 之间。它类似于 BINARY_INTEGER,但它所需的存储空间更小,运算速度更快,因此建 议在 PLS_INTEGER 数值范围之内的所有计算都使用此类型。 表 5-2 常用的数字数据类型  字符数据类型 字符数据类型用于存储字符串或字符数据。常用的字符数据类型见表 5-3。 数据类型 描述 CHAR[(max_size[CHAR|BYTE])] 用于存储固定长度的字符数据。最大长度 32767 个字节。如 果不指定最大长度,默认值为 1。如果没有写 CHAR或 BYTE, 默认为 BYTE。需注意与 Oracle 中 CHAR 类型的区别,向 Oracle 数据库表中插入 CHAR 类型的值时,其长度不要超出 2000 个字节。 VARCHAR2(max_size[CHAR|BYTE]) 用于存储可变长度的字符数据。最大长度 32767 个字节。如 果没有写 CHAR 或 BYTE,默认为 BYTE。需注意与 Oracle 中 VARCHAR2 类型的区别,向 Oracle 数据库表中插入 VARCHAR2 类型的值时,其长度不要超出 4000 个字节。 STRING(max_size[CHAR|BYTE]) 类似于 VARCHAR2 LONG 类似于 VARCHAR2,最大长度为 32760 个字节,注意 Oracle 中的该类型最大长度为 2GB。 RAW(max_size) 用于存储固定长度的二进制数据,最大长度为 32760 个字节。 如有必要可在字符集之间自动转换。注意 Oracle 中的该类型 最大长度为 2000 个字节。 LONG RAW 类似于 RAW,最大长度为 32760 个字节。不能在字符集之 间自动转换。注意 Oracle 中的该类型最大长度为 2GB。 表 5-3 常用的字符数据类型  日期时间数据类型 日期时间数据类型用于存储日期和时间值。此类型的数据类型主要表 5-4 中的两种。 数据类型 描述 DATE 用于存储固定长度的日期和时间数据。支持的日期范围为: 公元前 4712 年 1 月 1 日到公元 9999 年 12 月 31 日。日期函 数 SYSDATE 能返回当前的日期和时间。 TIMESTAMP[(precision)] 从 9i 开始新增的类型,也用于存储日期和时间,比 DATE 类 专题五 PL/SQL 编程 翰子昂系列理论教材 - 115 - 数据类型 描述 型存储的时间更精确。Precision 代表秒的小数部分的位数, 取值范围 0 到 9,默认为 6。日期函数 SYSTIMESTAMP 返 回当前的日期时间信息。 表 5-4 常用的日期和时间数据类型  布尔数据类型 布尔数据类型用于存储逻辑值,它只有一种类型即 BOOLEAN 类型,它的取值只能是 T- RUE、FALSE 和 NULL,需注意在 Oracle 数据列中不能使用该类型。 2. 属性类型 属性类型是用于引用变量或数据库表列的数据类型,以及表示表中一行的记录类型。P- L/SQL 支持两种属性类型:  %TYPE:引用某个变量或数据库列的数据类型来声明变量。声明的语法如下: variable_name other_var_name%TYPE|table_name.column_name%TYPE; 如下面的代码: DECLARE l_num1 NUMBER; l_num2 l_num%TYPE; l_char dept.dname%TYPE; … … 该段代码先声明了一个 NUMBER 类型的变量 l_num1,然后声明了一个属性类型的变量 l_num2,它的数据类型与 l_num1 相同,接着又声明了一个属性类型的变量 l_char,它的 数据类型与 dept 表中的 dname 列的数据类型相同。  %ROWTYPE:引用数据库表中的一行的记录类型,可以存储从表中选择或由游标提 取出的整行数据。声明语法如下: variable_name table_name%ROWTYPE; 2. 按回车后系统提示“输入部门编号的值”,输入要查询的部门编号,如 10,按回车键 后结果如图 5-1 所示。 Oracle 数据库案例教程_教师用书 - 116 - 翰子昂系列理论教材 图 5-1 查询部门名称 3.2 用条件控制语句来实现为员工加薪 员工编号是 7934 的员工因为表现较好,领导决定为其加薪,加薪的原则是:如果原工资 小于 1000,则加 200 元;如果原工资大于等于 1000 且小于 2000,则加 150 元;否则的话加 100 元。要实现这一功能,可以分别用 IF—THEN—ELSIF 或 CASE 两种条件控制语句来完成。 理论知识: 条件控制用于根据条件来执行一系列的语句,包括 IF 和 CASE 两种条件控制语句。 IF 条件控制语句 IF 语句有三种形式:IF—THEN、IF—THEN—ELSE 和 IF—THEN—ELSIF。 1. IF—THEN 语句: IF condition THEN statements; END IF; 该语句先测试条件 condition,如果条件为 TRUE,则执行 THEN 部分的语句块 statements。 2. IF—THEN—ELSE 语句: IF condition THEN statements1; ELSE statements2; END IF; 该语句先测试条件 condition,如果条件为 TRUE,则执行 THEN 部分的语句块 statements1,如果条件为 FALSE,则执行 ELSE 部分的语句块 statements2。 3. IF—THEN—ELSIF 语句: IF condition1 THEN statements1; ELSIF condition2 THEN statements2; ELSE statements3; END IF; 该语句先测试条件 condition1,如果条件为 TRUE,则执行第一个 THEN 部分的语句 块 statements1,如果条件为 FALSE,则继续测试条件 condition2,如果条件为 TRUE, 则执行第二个 THEN 部分的语句块 statements2,如果条件为 FALSE,则执行 ELSE 部分 的语句块 statements3。 CASE 条件控制语句 专题五 PL/SQL 编程 翰子昂系列理论教材 - 117 - CASE 语句根据条件将单个变量或表达式与多个值进行比较。语法如下: CASE selector WHEN expression1 THEN statements1; WHEN expression2 THEN statements2; … … WHEN expressionN THEN statementsN; [ELSE statementsN] END CASE; 在执行语句前,该语句先计算选择器 selector 的值,当 selector 的值与某个 expression 相等时,则执行对应了 THEN 子句部分的语句,如果 selector 的值与所有的 expression 都不相等相等,则执行 ELSE 部分的语句。ELSE 部分是可选的。 还有另外一种形式的 CASE 语句,这种形式不使用选择器,而是计算 WHEN 子句中的各 个比较表达式,找到第一个为 TRUE 的表达式后执行对应的语句块,然后退出条件判断部分。 语法如下: CASE WHEN condition_expression1 THEN statements1; WHEN condition_expression2 THEN statements2; … … WHEN condition_expressionN THEN statementsN; [ELSE statementsN] END CASE; 1. 用查询命令查看编号是 7934 的员工的原工资,在 SQL 提示符下输入以下代码: SELECT sal FROM emp WHERE empno=7934; 按回车键执行后可看到其原工资是 1300。 2. 先用 IF—THEN—ELSIF 条件控制语句来实现功能。在 SQL 提示符下,输入以下代码: DECLARE l_sal NUMBER; BEGIN SELECT sal INTO l_sal FROM emp WHERE empno=7934; IF l_sal<1000 THEN UPDATE emp SET sal=sal+200 WHERE empno=7934; ELSIF l_sal>=1000 AND l_sal<2000 THEN UPDATE emp SET sal=sal+150 WHERE empno=7934; ELSE UPDATE emp SET sal=sal+100 WHERE empno=7934; END IF; END; Oracle 数据库案例教程_教师用书 - 118 - 翰子昂系列理论教材 / 3. 按回车键后系统提示“PL/SQL 过程已成功完成”。 4. 用执行第 1 步中的查询语句,可以看到其工资已更改为 1450,说明数据已成功更新。 5. 在 SQL 提示符下输入“ROLLBACK;”命令回滚前面的更新。 6. 用查询语句查看其工资已回滚到原来的 1300。 7. 接下来再用 CASE 条件控制语句来实现加薪功能。在 SQL 提示符下,输入以下代码: DECLARE l_sal NUMBER; BEGIN SELECT sal INTO l_sal FROM emp WHERE empno=7934; CASE WHEN l_sal<1000 THEN UPDATE emp SET sal=sal+200 WHERE empno=7934; WHEN l_sal>=1000 AND l_sal<2000 THEN UPDATE emp SET sal=sal+150 WHERE empno=7934; ELSE UPDATE emp SET sal=sal+100 WHERE empno=7934; END CASE; END; / 8. 按回车键执行程序成功后,再用查询语句可以查看到其工资同样也被更新为 1450。 3.3 使用循环控制语句 现有一个需求,要求计算出 1 到 100 之间所有能被 9 整除的整数,并把结果输出出来。 操作如下: 1. 在 SQL 提示符下输入如下的代码: DECLARE num PLS_INTEGER; BEGIN FOR num IN 1..100 LOOP IF MOD(num,9)=0 THEN DBMS_OUTPUT.PUT(num||' '); END IF; END LOOP; DBMS_OUTPUT.NEW_LINE; END; / 专题五 PL/SQL 编程 翰子昂系列理论教材 - 119 - 2. 按回车后执行结果如下所示。 9 18 27 36 45 54 63 72 81 90 99 理论知识: 循环控制用于重复执行一系列语句。循环控制包括 LOOP 和 EXIT 语句,使 用 EXIT 语句 可以立即退出循环,使用 EXIT WHEN 语句可以根据条件结束循环。 循环有三种类型:LOOP 循环、WHILE 循环和 FOR 循环。 1. LOOP 循环:包括 LOOP 与 END LOOP 之间的一系列语句。为了避免进入死循环,必 须在循环中加入 EXIT 或 EXIT WHEN 语句。 LOOP 循环的语法如下: LOOP statements; END LOOP; 2. WHILE 循环 将条件与一系列的语句结合在一起的循环。WHILE 循环根据条件执行语句。每次在执行 语句前,先判断条件,如果条件为 TRUE,则执行 LOOP 和 END LOOP 之间的语句,如果为 FALSE,则退出循环。WHILE 循环的次数到循环结束时才知道。 WHILE 循环的语法如下: WHILE condition LOOP statements; END LOOP; 3. FOR 循环 在执行语句前,FOR 循环中的循环次数是已知的,也就是说,FOR 循环适于用在循环次 数为已知的情况下。在 FOR 循环中,循环计数器变量无需事先声明,且在循环体语句中不能 给计数器变量赋值。FOR 循环的步长总为 1。 FOR 循环的语法如下: FOR counter IN [REVERSE] value1..value2 LOOP statements; END LOOP; 其中:counter 为循环计数器变量。关键字 REVERSE 为可选项,只有在需要对值从大 到小执行循环时才会使用它。value1 应小于 value2。 3.4 用顺序控制语句实现更新员工工资 公司经过技能评定后,人事部门决定,编号是 7499 的员工如果原工资低于 2000,则给 其长到 2000,如果等于或高于 2000,则维持原工资不变。下面用顺序控制语句来实现这一功 能,操作如下: Oracle 数据库案例教程_教师用书 - 120 - 翰子昂系列理论教材 1. 在更新之前,先用命令“SELECT sal FROM emp WHERE empno=7499;”查询出编 号是 7499 的员工原工资为 1600。 2. 在 SQL 提示符下输入以下的代码: DECLARE l_sal emp.sal%TYPE; BEGIN SELECT sal INTO l_sal FROM emp WHERE empno=7499; IF l_sal<2000 THEN GOTO UPDATION; ELSE NULL; END IF; <> UPDATE emp SET sal=2000 WHERE empno=7499; END; / 3. 按回车后执行此 PL/SQL 块,系统提示“PL/SQL 过程已成功完成”。 4. 再用 SELECT 语句查询该员工的工资发现已更新为 2000。 理论知识: 顺序控制用于按顺序执行的语句。顺序控制包括 GOTO 语句和 NULL 语句。  GOTO 语句 GOTO 语句用于无条件地将控制权转到标签指定的语句。标签是用双尖括号括起来的标识 符。在 PL/SQL 块内标签必须具有唯一的名称,标签后必须紧跟执行语句或 PL/SQL 块。G- OTO 语句不能跳转到 IF 语句、CASE 语句、LOOP 语句或子块中。  NULL 语句 NULL 语句什么也不做,只是将控制权转移到下一条语句。NULL 语句是可执行语句。N- ULL 语句用在 IF 或其它语句语法要求至少需要一条可执行语句,但又不需要执行操作的情 况下。 3.5 PL/SQL 异常处理机制的应用 1. 数据库用户知道一个员工的编号,想通过编号查询出该员工的姓名。输入如下的代码, 请注意系统定义异常的用法。 DECLARE l_empno emp.empno%TYPE; l_emp emp%ROWTYPE; BEGIN l_empno:=&员工编号; SELECT * INTO l_emp FROM emp WHERE empno=l_empno; 专题五 PL/SQL 编程 翰子昂系列理论教材 - 121 - DBMS_OUTPUT.PUT_LINE('编号是'||l_empno||'的员工姓名是'||l_emp.ename); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('编号是'||l_empno||'的员工不存在!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('错误号:'||SQLCODE); DBMS_OUTPUT.PUT_LINE('错误原因:'||SQLERRM); END; / 2. 按回车键,系统提示“输入员工编号的值”,输入一个表中存在的值,如 7934,结果 如图 5-2 所示,说明程序能完成功能。 图 5-2 查询员工姓名 1 3. 为了验证异常处理是否发生作用,再在 SQL 提示符下输入“/”键来执行程序,系统 提示“输入员工编号的值”后,输入一个不存在的员工编号,如 3929,执行结果如图 5-3 所 示,可见异常处理已发生作用。 图 5-3 查询员工姓名 2 4. 修改上面的代码,用自定义异常来实现功能。代码如下: DECLARE l_empno emp.empno%TYPE; l_emp emp%ROWTYPE; empno_no_found EXCEPTION; l_count PLS_INTEGER; Oracle 数据库案例教程_教师用书 - 122 - 翰子昂系列理论教材 BEGIN l_empno:=&员工编号; SELECT COUNT(*) INTO l_count FROM emp WHERE empno=l_empno; IF l_count<1 THEN RAISE empno_no_found; END IF; SELECT * INTO l_emp FROM emp WHERE empno=l_empno; DBMS_OUTPUT.PUT_LINE('编号是'||l_empno||'的员工姓名是'||l_emp.ename); EXCEPTION WHEN empno_no_found THEN RAISE_APPLICATION_ERROR(-20001,'该编号的员工不存在!'); END; / 5. 按回车键,系统提示“输入员工编号的值”,输入一个表中存在的值,如 7934,结果 和图 5-2 相同,说明程序也能完成查询功能。 6. 为了验证异常处理是否发生作用,再在 SQL 提示符下输入“/”键来执行程序,系统 提示“输入员工编号的值”后,输入一个不存在的员工编号,如 8899,执行结果如图 5-4 所 示,可以看出,因为编号不存在,自定义异常发生作用,程序进入到异常处理部分,过程 RAISE_APPLICATION_ERROR 被执行,引发了应用程序错误。 图 5-4 查询员工姓名 3 理论知识: 异常处理 程序运行时出现的错误被称为异常。当开发 PL/SQL 应用程序时,为了提高程序的健壮 性,开发人员必须考虑到应用程序可能出现的各种错误,并进行相应的错误处理。因此在 P- L/SQL 程序块中加入异常处理部分是一个较好的编程习惯。 异常的分类 异常主要分为两类:预定义异常和自定义异常。 1. 预定义异常 预定义异常是指由 PL/SQL 所提供的系统异常,是当 PL/SQL 违反 Oracle 规则或超越 系统限制时,隐式地自动地引发的内部异常。Oracle 把一些常见的错误预定义为异常,每 专题五 PL/SQL 编程 翰子昂系列理论教材 - 123 - 个预定义异常都有一个错误号和一个名字,在 PL/SQL 块的异常处理部分必须通过名字来捕 获异常。 表 5-5 列出了常用的系统预定义异常。 异常 说明 ACCESS_INTO_NULL 未初始化对象时出现 CASE_NOT_FOUND 用户的输入与 CASE 语句中的各个 WHEN 条件都不匹配,且 CASE 语句中没有 ELSE 分支时会出现 COLLECTION_IS_NULL 在给未初始化的集合元素(如数组)赋值进会出现 CURSOR_ALREADY_OPEN 企图打开一个已经打开的游标时出现 DUP_VAL_ON_INDEX 试图破坏一个唯一性约束时出现 INVALID_CURSOR 在执行非法的游标操作(如从未打开的游标中提取数据)时出现 INVALID_NUMBER 在不能将字符串有效地转换为数字时出现 LOGIN_DENIED 企图用无效的用户名或密码登录 Oracle 时出现 NO_DATA_FOUND 在表中不存在请求的数据时出现,当引用一个已经删除的元素时也会 出现 STORAGE_ERROR 内存溢出或内存冲突会出现 TOO_MANY_ROWS 在执行 SELECT INTO 语句后返回超过一行时出现 VALUE_ERROR 在 PL/SQL 块中执行赋值操作,而被赋值的变量不足以容纳实际数据 时出现 ZERO_DIVIDE 用零作除数时出现 表 5-5 常用的系统预定义异常 2. 自定义异常 自定义异常又被称为用户定义异常,是由开发人员为特定情况所定义的异常,它可以与 Oracle 内部错误没有任何关联,让 PL/SQL 程序用与处理内部错误相同的方式来处理自定 义的异常。这是一种很好的编程习惯,可以获得更直观的代码。 要使用自定义异常应该先在声明部分声明。声明的语法如下: DECLARE exception_name EXCEPTION; 异常的抛出 异常的抛出主要有三种方式: 1. 自动抛出:如发生预定义异常及其它 Oracle 内部错误时,会自动引发异常。 2. 用 RAISE 语句显示抛出:用 RAISE 语句不仅可以显示抛出自定义异常,也可以抛出 预定义异常。使用的语法如下: RAISE exception_name; 其中:exception_name 表示异常的名字。 3. 用 RAISE_APPLICATION_ERROR 语句抛出:RAISE_APPLICATION_ERROR 语句 Oracle 数据库案例教程_教师用书 - 124 - 翰子昂系列理论教材 用于引发应用程序错误,它抛出一个异常并给异常赋予一个错误号以及错误信息。RAISE_ APPLICATION_ERROR 语句既可以在 PL/SQL 程序的执行部分使用,也可以在异常处理部分 使用。 使用 RAISE_APPLICATION_ERROR 语句的语法如下: RAISE_APPLICATION_ERROR(error_number,error_message); 其中:error_number 表示错误号,取值范围是-20,000 到-20,999。error_message 表示错误信息字符串,最大为 2048 个字节。 异常的处理 PL/SQL 程序块的异常处理部分包含了程序处理错误的代码,当异常被抛出时,程序控 制离开执行部分转入异常部分,一旦程序进入异常部分就不能再回到同一块的执行部分。下 面是异常处理部分的一般语法: EXCEPTION WHEN exception_name1 THEN statements1; WHEN exception_name2 THEN statements2; …… WHEN OTHERS THEN Other_statements; END; 如果发生与某一个异常名称相匹配的异常时,程序就会执行与其相应的 THEN 后面的代 码,执行完成后退出程序。OTHERS 处理程序可以确保不会漏过任何异常,可以提高程序捕 获异常的能力。在 PL/SQL 块中可以只有 OTHERS 处理程序,用以捕获全部异常。可以在异 常处理的语句中使用 SQLCODE 和 SQLERRM 来返回错误代码和错误信息。 4. 提高 使用动态 SQL 1. 静态 SQL 和动态 SQL  静态 SQL 静态 SQL 是直接嵌到 PL/SQL 块中的 SQL 语句,用于完成特定或固定的任务。静态 SQ L 的性能要优于动态 SQL,因此在编写 PL/SQL 块时,如果功能完全确定,应使用动态 SQL。  动态 SQL 动态 SQL 是指在运行时动态形成的 SQL 语句。如果需要在 PL/SQL 中执行 DDL 语句(如 CREATE、ALTER、DROP)、DCL 语句(GRANT、REVOKE),或者在 PL/SQL 块中需要 执行更加灵活的 SQL 语句(如在 SELECT 语句中使用不同的 WHERE 条件),那么就必须借 助于动态 SQL。 专题五 PL/SQL 编程 翰子昂系列理论教材 - 125 - 2. 动态 SQL 的执行 在大部分情况下,可以使用 EXECUTE IMMEDIATE 来执行动态 SQL 语句,语法如下: EXECUTE IMMEDIATE dynamic_sql_string [INTO variable_list] [USING bind_argument_list]; 其中:dynamic_sql_string 是动态 SQL 语句字符串。INTO 子句用于接受 SELECT 语句选 择的记录值。USING 子句用于绑定输入参数变量。 下面的示例演示了动态 SQL 的使用方法: DECLARE sql_string VARCHAR2(200); emp_rec emp%ROWTYPE; BEGIN sql_string:='SELECT * FROM emp WHERE empno=:id'; EXECUTE IMMEDIATE sql_string INTO emp_rec USING &emp_id; DBMS_OUTPUT.PUT_LINE('查询出的员工姓名是:'||emp_rec.ename); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('该编号的员工不存在!'); END; / 按回车后,系统提示“输入 emp_id 的值:”,输入员工编号,如 7934,执行结果如图 5-5 所示。 图 5-5 动态 SQL 的查询结果 在该程序中,PL/SQL 块可以根据用户输入的不同的员工编号,生成动态 SQL 语句,并 把查询的结果存储到%ROWTYPE 的属性变量中,最后输出查询出的员工姓名。 5. 实验 按照第 3 部分相关实践知识及提高部分依次练习,主要掌握: 1. 用 PL/SQL 块来从表 dept 中查询出部门名称,了解 PL/SQL 块的语言特征,掌握 PL/ Oracle 数据库案例教程_教师用书 - 126 - 翰子昂系列理论教材 SQL 块的结构和变量的声明方法,参见 3.1(20 分钟)。 2. 用条件控制语句来实现为员工加薪,掌握条件控制结构的用法,参见 3.2(15 分钟)。 3. 用循环控制语句来输出 1 到 100 之间能被 9 整除的整数,掌握循环控制结构的用法, 参见 3.3(15 分钟)。 4. 用顺序控制语句实现更新员工工资,掌握顺序控制结构的用法,参见 3.4(15 分钟)。 5. 按员工编号查询员工姓名,掌握 PL/SQL 的异常处理机制,参见 3.5(15 分钟)。 6. 根据用户输入的员工编号查询员工姓名,掌握动态 SQL 的用法,参见提高部分(10 分钟)。 6. 课后作业 以 SCOTT 身份登陆到 Oracle 数据库后完成以下作业: 1. 圆周率 PI 假定取常量 3.14,让用户输入圆的半径,用 PL/SQL 程序计算出圆的面积, 并显示出来。 2. 用 SELECT 语句查询出编号是 7369 的员工所在的部门,根据其所在部门的不同,给 予不同的工资。如果部门编号是 10,工资定为 1000;如果部门编号是 20,工资定为 1200; 如果部门编号是 30,工资定为 1500;否则的话,工资定为 1800。用带 CASE 语句的条件控 制结构来实现此功能。 专题六 游标 翰子昂系列理论教材 - 127 - 专题六 游标 1. 教学目标 1.1 了解游标的概念和种类 1.2 掌握游标属性的用法 1.3 掌握显式游标的用法 1.4 掌握游标中记录变量和%ROWTYPE 的用法 1.5 掌握带参数游标的用法 1.6 掌握循环游标的用法 1.7 掌握使用游标更新数据的方法 1.8 掌握 REF 游标的使用方法 2. 工作任务 2.1 使用隐式游标属性 2.2 用游标生成员工报表 2.3 用游标生成分部门员工报表 2.4 用游标更新员工工资 2.5 使用 REF 游标动态返回结果集 3. 相关实践知识 从开始菜单中打开 SQL*Plus 工具,以 SCOTT 用户的身份登录到数据库。 理论知识: 为了处理 SQL 语句,Oracle 将在内存中分配一个区域,这就是上下文区。上下文区包 含了完成该处理所需的信息,比如已经处理完的行数、指向被分析语句的指针,查询语句返 回的数据行集等。游标就是指向上下文区的句柄或指针。通过游标,PL/SQL 程序可以控制 上下文区和在处理语句时上下文区会发生些什么事情。 游标有两种类型:显式游标、隐式游标。 3.1 使用游标属性 1. 在 SQL 提示符下,输入命令“SET SERVEROUT ON”。 2. 再在 SQL 提示符下输入以下的代码: BEGIN Oracle 数据库案例教程_教师用书 - 128 - 翰子昂系列理论教材 DELETE FROM emp; DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT || ' rows have been deleted!'); END; 理论知识: 隐式游标是 Oracle 自动定义的,用于 DML 语句和单行查询语句 SELECT…INTO。隐式 游标的名称为 SQL。 通过隐式游标的属性,可以获取 DML 语句的结果。 当执行一条 DML 语句后,DML 语句的结果保存在四个游标属性中,它们是:  SQL%FOUND  SQL%NOTFOUND  SQL%ROWCOUNT  SQL%ISOPEN 这些属性用于控制程序流程或者了解程序的状态。当运行 DML 语句时,PL/SQL 打开一 个内建游标并处理结果,游标是维护查询结果的内存中的一个区域,游标在运行 DML 语句时 打开,完成后关闭。隐式游标只使用 SQL%FOUND、SQL%NOTFOUND 和 SQL%ROWCOUNT 三个属性。SQL%FOUND 和 SQL%NOTFOUND 是布尔值,SQL%ROWCOUNT 是整数值。 SQL%FOUND 和 SQL%NOTFOUND 属性 在执行任何 DML 语句前 SQL%FOUND 和 SQL%NOTFOUND 的值都是 NULL,在 执行 DML 语句后,SQL%FOUND 的属性值在下面的情况下将是 TRUE。 1. 成功执行一条 INSERT 语句时; 2. 执行 DELETE 或 UPDATE,至少有一行被 DELETE 或 UPDATE 时; 3. 用 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;如果游标关闭,则为 FAL- SE。对 于 隐式游标而言 SQL%ISOPEN 总是 FALSE,这是因为隐式游标在 DML 语句执行时打 开,结束时就立即关闭。 注意:游标属性不能直接用于 SQL 语句中。如果需要在 SQL 语句中使用,可先将其放入 一 PL/SQL 变量中,然后在 SQL 语句中通过该变量使用游标属性值。 3. 按“/”键,系统显示“14 rows have been deleted!”。这表示 SQL%ROWCOUNT 代 表了 DELETE 语句删除的记录数。 4. 如因为工作调动,编号是 7839 的员工被调到 dept=20 的部门工作。在 SQL 提示符下 专题六 游标 翰子昂系列理论教材 - 129 - 输入如下的代码: BEGIN UPDATE emp SET deptno=20 WHERE empno=7839; IF SQL%FOUND THEN DBMS_OUTPUT.PUT_LINE('数据已更新!'); ELSE DBMS_OUTPUT.PUT_LINE('该编号不存在!'); END IF; END; 5. 按“/”键后,系统提示“数据已更新!”。因为编号是 7839 的员工存在,所以 SQL%FOUND 返回 TRUE,其相对应的语句被执行。 6. 为以后演示方便,输入命令“ROLLBACK;”,回滚前面的操作。 3.2 用游标生成员工报表 现要求用游标查询出表 emp 中的员工编号、员工姓名和工资三列的数据,按工资(sal) 排序。 1. 在 SQL 提示符下输入以下代码: DECLARE CURSOR c_emp IS SELECT empno,ename,sal FROM emp ORDER BY sal; v_empno emp.empno%TYPE; v_ename emp.ename%TYPE; v_sal emp.sal%TYPE; BEGIN OPEN c_emp; LOOP FETCH c_emp INTO v_empno,v_ename,v_sal; EXIT WHEN c_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE (v_empno||' '||v_ename||' '||v_sal); END LOOP; CLOSE c_emp; END; 理论知识: 显示游标是由 PL/SQL 程序员定义和命名的游标,用于多行查询语句。 当查询返回结果超过一行时,就需要一个显式游标,此时用户不能使用 SELECT…INTO 语句。PL/SQL 管理隐式游标,当查询开始时隐式游标打开,查询结束时隐式游标自动关闭, 而显式游标在 PL/SQL 块的声明部分声明,在执行部分或异常处理部分打开,取完数据后将 其关闭。 上面所讲的隐式游标属性显示游标也有,不同之处在于游标属性的前缀是游标名而不是 Oracle 数据库案例教程_教师用书 - 130 - 翰子昂系列理论教材 SQL。 下表显示了显式游标和隐式游标的差别: 隐式游标 显式游标 PL/SQL 维护,当执行查询时自动打开和关闭 在程序中显式定义、打开、关闭,游标有一个名字。 游标属性前缀是 SQL 游标属性的前缀是游标名 属性%ISOPEN 总是为 FALSE %ISOPEN 根据游标的状态确定值 SELECT 语句带有 INTO 子句,只有一行数据被处 理 可以处理多行数据,在程序中设置循环,取 出每一 行数据。 表 6-1 隐式游标和显式游标 这里要做一个声明,我们所说的游标通常是指显式游标,因此从现在起没有特别指明的 情况,我们所说的游标都是指显式游标。使用显示游标的 4 个步骤是: 1. 声明游标 2. 打开游标 3. 从游标提取数据 4. 关闭游标 下面详细介绍这 4 个步骤:  声明游标 要在程序中使用游标,必须首先声明游标。一般语法为: CURSOR cursor_name IS select_statement; 在 PL/SQL 中游标名是一个未声明变量,不能给游标名赋值或用于表达式中。 在游标定义中 SELECT 语句中不一定非要是数据表,也可以是视图,也可以从多个表或 视图中选择的列,甚至可以使用*来选择所有的列。  打开游标 使用游标中的值之前应该首先打开游标,打开游标初始化查询处理。打开游标的语法是: OPEN cursor_name; cursor_name 是在声明部分定义的游标名。  从游标提取数据 从游标得到一行数据使用 FETCH 命令。每一次提取数据后,游标都指向结果集的下一行。 语法如下: FETCH cursor_name INTO variable[,variable,...] 对于 SELECT 定义的游标的每一列,FETCH 变量列表都应该有一个变量与之相对应,变 量的类型也要相同。 如果有多行返回结果,可以使用循环逐行提取数据,然后以游标属性作为结束循环的条 件。即: 专题六 游标 翰子昂系列理论教材 - 131 - OPEN cursor_name; LOOP FETCH cursor_name INTO variable[,variable,...]; EXIT WHEN cursor_name%NOTFOUND; -- 使用数据 END LOOP; CLOSE OPEN cursor_name;  关闭游标 关闭游标的语法为: CLOSE cursor_name; 2. 按“/”键,执行上述代码,结果如图 6-1 所示。 图 6-1 用游标生成员工报表 3. 专题五中的属性类型%ROWTYPE 也可用于游标,利用它可以把上面第 1 步中的代码 改写为: DECLARE CURSOR c_emp IS SELECT empno,ename,sal FROM emp ORDER BY sal; r_emp c_emp%ROWTYPE; BEGIN OPEN c_emp; LOOP FETCH c_emp INTO r_emp; EXIT WHEN c_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE(r_emp.empno||' '||r_emp.ename Oracle 数据库案例教程_教师用书 - 132 - 翰子昂系列理论教材 理论知识: 一个记录变量(也就是专题五中的属性类型%ROWTYPE)可以存放游标返回的一行数据。 所以记录变量可用于从游标中提取数据行,当游标选择很多列的时候,那么使用记录比为每 列声明一个变量要方便得多。记录变量定义的语法为: v_record_variable cursor_name%ROWTYPE; 或者 v_record_variable table_name%ROWTYPE; 当在表上使用%ROWTYPE 并将从游标中取出的值放入记录中时,如果要选择表中所有 列,那么在 SELECT 子句中使用*比将所有列名列出来要安全得多。 对应的获取数据的语法变为: FETCH cursor_name INTO v_record_variable; 可以通过 v_record_variable.column_name 的方式来访问记录中相应字段的值。 column_name 是数据表中的列名。 4. 按“/”键,可以看到,执行结果也与前面相同。 5. 上面的实现代码仍较多,可用循环游标来简化,如下面的代码所示。 DECLARE CURSOR c_emp IS SELECT empno, ename, sal FROM emp ORDER BY sal; BEGIN FOR r_emp IN c_emp LOOP DBMS_OUTPUT.PUT_LINE( r_emp.empno||' '||r_emp.ename ||' '||r_emp.sal); END LOOP; END; 理论知识: 在大多数时候我们在设计程序的时候都遵循下面的步骤: 1. 打开游标 2. 开始循环 3. 从游标中取值 4. 检查哪一行被返回 5. 处理 ||' '||r_emp.sal); END LOOP; CLOSE c_emp; END; 专题六 游标 翰子昂系列理论教材 - 133 - 6. 关闭循环 7. 关闭游标 这种使用游标的方式比较麻烦。有一种特殊的 FOR 循环可以简化上述操作,这就是游标 FOR 循环。用于游标 FOR 循环的游标按照正常的声明方式声明,但在使用时不需要显式的打 开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。游标 FOR 循环的语法如下: FOR record_name IN (corsor_name[(parameter[,parameter]...)] | (query_difinition) LOOP statements END LOOP; record_name 为隐式声明的记录变量,可直接使用,无需打开关闭,也不用在声明部 分再定义此变量。 query_difinition 为一子查询,可以不在前面声明游标,而直接在此写一个查询语 句。 6. 按“/”键,执行上述代码。可以看到,执行结果也与前面相同。 7. 上述的代码还可以写成: BEGIN FOR r_emp IN (SELECT empno, ename, sal FROM emp ORDER BY sal) LOOP DBMS_OUTPUT.PUT_LINE( r_emp.empno||' '||r_emp.ename ||' '||r_emp.sal); END LOOP; END; 按“/”键执行代码,结果也与前面相同。 3.3 用游标生成分部门员工报表 现要求分部门的员工报表,预期的结果如图 6-2 所示。 Oracle 数据库案例教程_教师用书 - 134 - 翰子昂系列理论教材 图 6-2 分部门员工报表 1. 在 SQL 提示符下,输入以下代码: DECLARE v_deptno emp.deptno%TYPE; CURSOR c_emp IS SELECT empno,ename,sal FROM emp WHERE deptno=v_deptno; r_emp c_emp%ROWTYPE; BEGIN FOR r_dept IN (SELECT * FROM dept) LOOP DBMS_OUTPUT.PUT_LINE(r_dept.deptno||' '||r_dept.dname ||' '||r_dept.dname); DBMS_OUTPUT.PUT_LINE('-------------------------------------'); v_deptno:=r_dept.deptno; OPEN c_emp; LOOP FETCH c_emp INTO r_emp; EXIT WHEN c_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE(r_emp.empno||' '||r_emp.ename ||' '||r_emp.sal); END LOOP; DBMS_OUTPUT.PUT_LINE(''); CLOSE c_emp; 专题六 游标 翰子昂系列理论教材 - 135 - END LOOP; END; 2. 按“/”键,执行上述代码。结果如图 6-2 所示。 3. 下面用带参数的游标来实现该功能。在 SQL 提示符下,输入以下代码: -- example6-7 DECLARE CURSOR c_emp(p_deptno emp.deptno%TYPE) IS SELECT empno,ename,sal FROM emp WHERE deptno=p_deptno; BEGIN FOR r_dept IN (SELECT * FROM dept) LOOP DBMS_OUTPUT.PUT_LINE(r_dept.deptno||' '||r_dept.dname ||' '||r_dept.dname); DBMS_OUTPUT.PUT_LINE('-------------------------------------'); FOR r_emp IN c_emp(r_dept.deptno) LOOP DBMS_OUTPUT.PUT_LINE(r_emp.empno||' '||r_emp.ename||' ' ||r_emp.sal); END LOOP; DBMS_OUTPUT.PUT_LINE(''); END LOOP; END; 理论知识: 与过程和函数(专题七中介绍)相似,可以将参数传递给游标并在查询中使用。这对于 处理在某种条件下打开游标的情况非常有用。它的声明语法如下: CURSOR cursor_name[(parameter[,parameter],...)] IS select_statement; 定义参数的语法为: Parameter_name [IN] data_type[{:=|DEFAULT} value] 游标只能接受传递的值,而不能返回值。参数只定义数据类型,没有大小。 另外可以给参数设定一个缺省值,当没有参数值传递给游标时,就使用缺省值。游标中 定义的参数只是一个占位符,在别处引用该参数不一定可靠。 在打开游标时给参数赋值,语法如下: OPEN cursor_name[value[,value]....]; 参数值可以是字面值(常量)或变量。 3. 按“/”键执行结果与上面相同。 3.4 用游标更新员工工资 将 emp 表中员工按工资 sal 字段降序排列。工资最高的员工不加工资,次高的加 100,第 Oracle 数据库案例教程_教师用书 - 136 - 翰子昂系列理论教材 三名加 200,第四名加 300…,依次类推。并显示输出各员工的编号,姓名及更新前后的工资。 1. 先用如下的 SELECT 语句查询表 emp 内的员工工资。 SELECT empno,ename,sal FROM emp; 查询结果如图 6-3 所示。 图 6-3 更新前员工工资 2. 为了实现上述的功能,在 SQL 提示符下,输入以下代码: DECLARE CURSOR c_emp IS SELECT empno,ename,sal FROM emp ORDER BY sal DESC FOR UPDATE; v_increase NUMBER := 0; v_new_sal NUMBER; BEGIN FOR r_emp IN c_emp LOOP v_new_sal := r_emp.sal + v_increase; UPDATE emp SET sal = v_new_sal WHERE CURRENT OF c_emp; DBMS_OUTPUT.PUT_LINE(r_emp.empno||' '||r_emp.ename||' old salary = '||r_emp.sal||' new salary = '||v_new_sal); v_increase:=v_increase+100; END LOOP; END; 理论知识: 在 PL/SQL 中可以使用 UPDATE 和 DELETE 语句更新或删除数据行。显式游标只有在需 专题六 游标 翰子昂系列理论教材 - 137 - 要获得多行数据的情况下使用。PL/SQL 提供了仅仅使用游标就可以执行删除或更新记录的 方法。 UPDATE 或 DELETE 语句中的 WHERE CURRENT OF 子句专门处理要执行 UPDATE 或 D- ELETE 操作的表中取出的最近的数据。要使用这个方法,在声明游标时必须使用 FOR UPD- ATE 子句,当对话使用 FOR UPDATE 子句打开一个游标时,所有返回集中的数据行都将处于 行级独占式锁定,其他对象只能查询这些数据行,不能进行 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} 3. 按“/”键,执行上述代码。结果如图 6-4 所示。 图 6-4 用游标更新员工工资 4. 再用 SELECT 语句查询,可以看到员工工资已被更新为图 6-4 中的“new salary”值。 3.5 使用 REF 游标动态返回结果集 使用专题五中介绍的 EXECUTE IMMEDIATE 执行动态 SQL 语句,只能处理返回单行或 没有返回值的 SQL 语句,通过 REF 游标则可以处理返回结果集的动态 SQL 语句。下面的示 例可以帮助用户按部门查询员工信息,先让用户输入部门编号,程序可以查询出该部门的员 工信息,并把员工编号和员工姓名输出。 1. 以 SCOTT 用户身份登录到数据库,在 SQL 提示符下输入如下的 PL/SQL 程序块。 Oracle 数据库案例教程_教师用书 - 138 - 翰子昂系列理论教材 DECLARE TYPE refcur IS REF CURSOR; refcur_test refcur; l_emp emp%ROWTYPE; l_deptno emp.deptno%TYPE; BEGIN L_deptno:=&部门编号; OPEN refcur_test FOR SELECT * FROM emp WHERE deptno=l_deptno; DBMS_OUTPUT.PUT_LINE('---部门'||l_deptno||'的员工信息---'); FETCH refcur_test INTO l_emp; WHILE refcur_test%FOUND LOOP DBMS_OUTPUT.PUT_LINE('员工编号:'||l_emp.empno||' 员工姓名:' ||l_emp.ename); FETCH refcur_test INTO l_emp; END LOOP; CLOSE refcur_test; END; / 2. 按回车后运行程序,系统提示“输入部门编号的值:”,如输入 20,运行结果如图 6 -5 所示。 图 6-5 用 REF 游标返回结果集 4. 提高 游标子查询 游标子查询(有时被称为嵌套游标表达式)是在 SELECT 语句内部使用游标表达式。当 FETCH 外层游标中的数据时,游标子查询对应返回类型总是游标变量 REF CURSOR。该游 标变量不需要再打开,可以直接使用。 以 SCOTT 用户身份登录到数据库后,在 SQL 提示符下输入下面的 PL/SQL 程序: 专题六 游标 翰子昂系列理论教材 - 139 - DECLARE c_emp SYS_REFCURSOR; CURSOR c_dept IS SELECT deptno, dname,CURSOR(SELECT * FROM emp WHERE deptno=dept.deptno) FROM dept ORDER BY deptno; v_name dept.dname%TYPE; v_no dept.deptno%TYPE; r_emp emp%ROWTYPE; BEGIN OPEN c_dept; LOOP FETCH c_dept INTO v_no, v_name, c_emp; EXIT WHEN c_dept%NOTFOUND; DBMS_OUTPUT.PUT_LINE(v_no || ' ' || v_name); DBMS_OUTPUT.PUT_LINE('-------------------------------------'); LOOP FETCH c_emp INTO r_emp; EXIT WHEN c_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE(r_emp.ename || ' ' || r_emp.sal); END LOOP; DBMS_OUTPUT.PUT_LINE(''); END LOOP; CLOSE c_dept; END; / 程序运行结果如图 6-6 所示。 Oracle 数据库案例教程_教师用书 - 140 - 翰子昂系列理论教材 图 6-6 使用游标子查询 5. 实验 按照第 3 部分相关实践知识及提高部分依次练习,主要掌握: 1. 隐式游标及游标属性的使用,参见 3.1(15 分钟)。 2. 显示游标的使用步骤,%ROWTYPE、游标 FOR 循环的使用,参见 3.2(25 分钟)。 3. 带参数游标的使用,参见 3.3(15 分钟)。 4. 游标锁定并更新记录数据的方法,参见 3.4(15 分钟)。 5. 使用 REF 游标返回结果集,参见 3.5(10 分钟)。 6. 游标子查询的使用,参见提高部分(10 分钟)。 6. 课后作业 以 SCOTT 身份登录到 Oracle 数据库后完成以下作业: 先创建一个表 demo,表中有两列:id(NUMBER)和 info(VARCHAR2(10)),按表 6- 2 所示向表 demo 中插入数据。 id info 1 你 2 好 3 吗 4 ? 表 6-2 表 demo 中的记录 编写一个 PL/SQL 匿名块,使用游标将 demo 表的 info 字段按照 id 的升序顺序连接为一 句话输出。 专题七 过程、函数和程序包 翰子昂系列理论教材 - 141 - 专题七 过程、函数和程序包 1. 教学目标 1.1 掌握过程的用法 1.2 掌握函数的用法 1.3 理解过程与函数的相同点和不同点 1.4 理解程序包的概念并能熟练应用 2. 工作任务 2.1 用无参过程实现“Hello World!”程序 2.2 用带输入参数的过程向表中插入记录 2.3 用带输出参数的过程查询表中的记录数 2.4 使用带输入输出参数的过程查询记录是否存在 2.5 使用函数查询部门信息 2.6 使用程序包封装过程和函数 3. 相关实践知识 从开始菜单中打开 SQL*Plus 工具,以 SCOTT 用户的身份登录到数据库。 3.1 无参的显示“Hello World!”的过程 1. 在 SQL 提示符下,输入以下代码: CREATE OR REPLACE PROCEDURE sp_helloWorld AS BEGIN DBMS_OUTPUT.PUT_LINE('Hello World!'); END sp_helloWorld; 理论知识: 过程是一组为了完成特定功能的符合数据库程序脚本规范的程序,经编译后存储在数据 库中,然后由一个应用程序或其他的 PL/SQL 程序调用。从根本上讲,过程就是命名的 PL/ SQL 程序块。 创建过程的语法: CREATE [OR REPLACE] PROCEDURE procedure_name [(parameter_list)] Oracle 数据库案例教程_教师用书 - 142 - 翰子昂系列理论教材 {AS|IS} [local_declarations] BEGIN executable_statements [EXCEPTION exception_handlers] END [procedure_name]; 其中,procedure_name 是过程 的 名 称,parameter_list 是 参 数 列 表, local_declarations 是 局 部 声明, executable_statements 是可执 行 语句, exception_handlers 是异常处理程序。 2. 按“/”键,系统提示“过程已创建。”,表示过程创建成功。在 SQL 提示符下输入如 下的代码来执行过程: EXECUTE sp_helloWorld; 3. 执行结果如下: Hello World! 理论知识: 在 SQL 提示符下,使用 EXECUTE 语句来执行过程。语法如下: EXECUTE procedure_name(parameters_list) 4. 输入如下代码来删除我们创建的过程: DROP PROCEDURE sp_helloWorld; 5. 系统会提示“过程已删除”,表示删除成功。 理论知识: 使用 DROP PROCEDURE 语句来删除过程,语法如下: DROP PROCEDURE procedure_name; 注意: 如果执行过程时屏幕上只是提示“PL/SQL 过程已成功完成”,却看不 到执行的结果,须执行命令:SET SERVEROUT ON; 3.2 用带输入参数的过程向表中插入记录 1. 在 SQL 提示符下输入以下代码,首先查看一下 dept 表中的记录: SELECT * FROM dept; 2. 输出结果如下: 专题七 过程、函数和程序包 翰子昂系列理论教材 - 143 - DEPTNO DNAME LOC ---------- -------------- ------------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON 3. 在 SQL 提示符下,或者用 EDIT 命令打开记事本输入如下的代码: CREATE OR REPLACE PROCEDURE sp_dept_insert (i_deptno NUMBER, i_dname VARCHAR2, i_loc VARCHAR2) AS BEGIN INSERT INTO dept VALUES(i_deptno,i_dname,i_loc); COMMIT; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('添加失败!原因为:'||SQLERRM); ROLLBACK; END sp_dept_insert; 4. 按“/”键系统提示创建成功后,分别输入两组数据(100,’aa’,’aa’)和(60,’aaa’,’aaa’) 来执行过程,如图 7-1 所示: 图 7-1 带输入参数的过程的输出结果 5. 再用 SELECT 语句查询 dept 表中的记录,可以发现第二组数据已经插入到表中,而第 一组数据因为违反 empno 列的定义没有被添加进去。 理论知识: 在 Oracle 中过程的参数模式有三种:IN、OUT 和 IN OUT,即输入、输出和输入输出。 定义参数的语法如下: Oracle 数据库案例教程_教师用书 - 144 - 翰子昂系列理论教材 parameter_name [IN|OUT|IN OUT] DATATYPE [{:=|DEFAULT} expression] IN 模式是输入模式,可以传递输入参数;IN 模式是默认模式,如果未指定参数的模式, 则该参数是 IN 模式的。可以在参数列表中为 IN 参数赋予一个默认值。 3.3 用带输出参数的过程查询表中的记录数 1. 在 SQL 提示符下或用 EDIT 命令打开记事本输入以下代码: CREATE OR REPLACE PROCEDURE sp_getcount (o_count OUT NUMBER) AS BEGIN SELECT COUNT(*) INTO o_count FROM dept; END sp_getcount; 理论知识: 过程中的 OUT 模式是输出模式,可以传递输出参数;OUT 不能省略,在返回到调用环境 之前,应该先给 OUT 模式的参数赋值。 2. 按“/”键后系统提示创建成功,继续编写一段匿名的 PL/SQL 块来执行上面的过程, 代码如下: DECLARE cnt NUMBER; BEGIN sp_getcount(cnt); DBMS_OUTPUT.PUT_LINE('dept 表中的记录数为: '||cnt); END; 3. 按“/”键执行此匿名的程序块,输出结果为: dept 表中的记录数为:5 3.4 使用带输入输出参数的过程查询记录是否存在 1. 在 SQL 提示符下或用 EDIT 命令打开记事本输入以下代码: CREATE OR REPLACE PROCEDURE sp_dept_dname_exist (io_value IN OUT VARCHAR2) IS l_count NUMBER; BEGIN SELECT COUNT(*) INTO l_count FROM dept WHERE dname=io_value; IF(l_count>0) THEN io_value:='存在'; ELSE io_value:='不存在'; 专题七 过程、函数和程序包 翰子昂系列理论教材 - 145 - END IF; END sp_dept_dname_exist; 理论知识: 过程中的 IN OUT 模式是输入输出模式,它兼有输入和输出参数的特点。INOUT 不能省 略,在返回到调用环境之前,应该先给 IN OUT 模式的参数赋值。 2. 按“/”键后系统提示创建成功,继续编写一段匿名的 PL/SQL 块来执行上面的过程, 代码如下: DECLARE l_iotest varchar2(20):='ACCOUNTING'; BEGIN sp_dept_dname_exist(l_iotest); DBMS_OUTPUT.PUT_LINE('部门名称 ACCOUNTING'||l_iotest||'!'); END; 3. 按“/”键执行此匿名的程序块,输出结果为: 部门名称 ACCOUNTING 存在! 3.5 使用函数查询部门信息 1. 在 SQL 提示符下或用 EDIT 命令打开记事本输入以下代码: CREATE OR REPLACE FUNCTION f_dept_getname_byno (i_deptno NUMBER) RETURN VARCHAR2 AS l_dname VARCHAR2(14); BEGIN SELECT dname INTO l_dname FROM dept WHERE deptno=i_deptno; RETURN l_dname; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN '错误!该编号的部门不存在!'; END f_dept_getname_byno; 理论知识: 函数的实质也是数据库中已命名的 PL/SQL 程序块。它的主要特性是函数能且只能返回 一个值。创建函数的语法: CREATE [OR REPLACE] FUNCTION function_name [(parameter_list)] RETURN DATATYPE {AS|IS} [local_declarations] Oracle 数据库案例教程_教师用书 - 146 - 翰子昂系列理论教材 BEGIN executable_statements [EXCEPTION exception_handlers] END [function_name]; 要点:  创建函数时必须通过 RETURN 子句来定义函数的返回类型。  在函数体的任何地方用户都可以通过 RETURN expression 语句从函数返回,但表 达式的类型一定要与 RETURN 子句中定义的数据类型一致。  函数的参数模式只能是 IN 模式的,而不能是 OUT 或 IN OUT 模式的。 2. 按“/”键系统提示函数创建成功后,输入如下的代码,传入参数 10 来调用函数: SELECT f_dept_getname_byno(10) FROM dual; 3. 输出结果为: F_DEPT_GETNAME_BYNO(10) ------------------------------- ACCOUNTING 4. 继续输入如下的代码,传入参数 90 来调用函数: SELECT f_dept_getname_byno(90) FROM dual; 5. 输出结果为: F_DEPT_GETNAME_BYNO(10) ------------------------------- 错误!该编号的部门不存在! 理论知识: 函数的调用:与过程不同,函数不能通过 EXECUTE 语句单独执行,只能通过 SQL 语句 或 PL/SQL 语句块来调用。  在 SQL 提示符下,可以用 SELECT 语句调用函数,语法如下: SELECT function_name from dual;  在其它的过程、函数或匿名的 PL/SQL 块中的可执行语句部分调用函数可以用一个 与函数的返回类型相同的变量来接收该函数。例如,如我们可以用下面的代码来调 用实践部分 3.5 中的函数 f_dept_getname_byno: DECLARE l_temp VARCHAR2; BEGIN 专题七 过程、函数和程序包 翰子昂系列理论教材 - 147 - l_temp=f_dept_getname_byno(90); DBMS_OUTPUT.PUT_LINE(‘编号是 90 的部门名称是:’||l_temp); END; 注意: 过程、函数和程序包创建后,只有创建者才有权调用它。要想让其他用 户有权调用,需运行“GRANT EXECUTE ON object_name TO user_name” 语句为其赋权。 6. 输入以下的代码删除我们创建的函数: SQL> DROP PROCEDURE sp_helloWorld; 7. 系统会提示“函数已删除”,表示删除成功。 理论知识: 删除函数用 DROP FUNCTION 语句,语法如下: DROP FUNCTION function_name; 理论小结: 过程与函数的比较  共同点:两者的实质都是已命名的 PL/SQL 程序块,即子程序,它们是子程序的两 种类型,存储在数据库中,可以从任何数据库客户端和前台应用程序中调用它们。  不同点: 过程 函数 参数模式可以是 IN、OUT 或 IN OUT 参数模式只能是 IN 模式 在语法规范中不包含 RETURN 子句 在语法规范中必须包含 RETURN 子句 在可执行语句部分可以有 RETURN 语句,但其后不 能加任何表达式 在可执行语句部分至少应该包含一条 RETURN expression 语句 可以用 EXECUTE 语句来执行 不能用 EXECUTE 语句来执行 表 7-1 过程与函数的不同点 3.6 使用程序包封装过程和函数 1. 在 SQL 提示符下或用 EDIT 命令打开记事本输入以下代码,创建程序包规范部分,把 我们在前面 3.2 中创建的过程和 3.5 中创建的函数封装起来: CREATE OR REPLACE PACKAGE pkg_dept AS PROCEDURE sp_dept_insert (i_deptno NUMBER,i_dname VARCHAR2,i_loc VARCHAR2); FUNCTION f_dept_getname_byno(i_deptno NUMBER) Oracle 数据库案例教程_教师用书 - 148 - 翰子昂系列理论教材 RETURN VARCHAR2; END pkg_dept; 理论知识: 程序包是对过程、函数、变量、常量、游标、异常及 PL/SQL 数据类型等的封装,是一 种数据库对象。它由两部分构成:程序包规范和程序包主体。 程序包规范中包含一些应用程序可见的公共对象和类型的声明,是与应用程序的接口。 规范包含应用程序所需的程序包资源。如果程序包规范没有声明子程序(过程和函数)和游 标,则不需要有程序包主体。创建程序包规范的语法如下: CREATE [OR REPLACE] package_name IS|AS [public type and item declarations] [subprogram specifications] END [package_name]; 其中:package_name 是程序包的名称,public type and item declarations 是声明变量、常量、游标、类型和异常等,subprogram specifications 指子程序(过 程和函数)。 在程序包规范中声明的项也可以在程序包之外使用(前提是要有相应的权限)被称为公 用元素,又叫公有项。 2. 系统提示创建成功后,继续输入下面的代码创建程序包主体: CREATE OR REPLACE PACKAGE BODY pkg_dept AS --过程 sp_dept_insert PROCEDURE sp_dept_insert (i_deptno NUMBER,i_dname VARCHAR2,i_loc VARCHAR2) AS BEGIN INSERT INTO dept VALUES(i_deptno,i_dname,i_loc); COMMIT; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('添加失败!原因为:'||SQLERRM); ROLLBACK; END sp_dept_insert; --函数 f_num_range FUNCTION f_dept_getname_byno(i_deptno NUMBER) RETURN VARCHAR2 AS l_dname VARCHAR2(14); BEGIN SELECT dname INTO l_dname FROM dept WHERE deptno=i_deptno; 专题七 过程、函数和程序包 翰子昂系列理论教材 - 149 - RETURN l_dname; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN '错误!该编号的部门不存在!'; END f_dept_getname_byno; END pkg_dept; 理论知识: 程序包主体中包含的是对在程序规范中声明的每个子程序和游标的具体实现。另外还可 包含一部分私有对象的声明。创建程序包主体的语法为: CREATE [OR REPLACE] PACKAGE BODY package_name IS|AS [subprogram bodies] [BEGIN initialization_statements] END [package_name]; 其中:subprogram bodies 是指子程序(过程和函数)定义的主体,initializa- tion_statements 指初始化部分,用于声明程序包中的私有项。 注意: 在创建过程、函数及程序包时出现提示“警告: 创建的过程带有编译错 误”,可以用 SHOW ERRORS 命令来查看错误原因。 3. 分别执行程序包中的过程和函数,结果如图 7-2 所示: 图 7-2 执行程序包中的过程和函数的输出结果 理论知识: 程序包元素的引用: 在程序包规范中声明的公用元素可以在其它应用程序或 PL/SQL 块中引用,引用时使用 点分表示法,即“程序包名称”+“.”+“元素名称”,可参见示例 3.5。对于在程序包主 Oracle 数据库案例教程_教师用书 - 150 - 翰子昂系列理论教材 体中声明的私有元素,不 能 在程序包之外引用,在程序包主体内引用时不需要用点分表示法。 在程序包规范中声明的元素,可 以 被其它的应用程序或 PL/SQL 块引用,被称为公有项。 而在程序包主体中定义的元素,并没有在程序包规范中出现过,只能在程序包主体内被使用, 不能在程序包之外被引用,这样的元素被称为私有项。 假设我们在程序包规范中定义了一个的变量 num1,在程序包主体部分定义了另一个变量 num2,我们可以在程序包之外用点分表示法引用 num1,而不能引用到 num2。 如果必须把私有元素变成公有的,则可以把该项添加到程序包规范中,再重新编译该规 范,操作完成后,该元素在程序包之外就可以引用了。 4. 输入以下的代码删除程序包: DROP PACKAGE pkg_dept; 5. 系统提示:“程序包已删除。”,表示删除成功。 理论知识: 只删除程序包的主体而不删除程序包的规范,语法为: DROP PACKAGE BODY package_name; 将程序包主体和程序包规范全部删除的语法为: DROP PACKAGE package_name; 理论小结: 程序包的优点: 程序包概念的引入,是 Oracle 面向对象思想的一个体现,通过程序包,将相关的功能 (如对某个表的增删改查的过程)组织在一起,也更利于数据库的管理。它的优点主要包括:  信息隐藏:如在程序包主体中用到的变量和类型在包外是不可见的。  模块化:使用程序包把相关的类型、对象和子程序封装在一个包内,非常有利于数 据库的管理。  对多态的支持:允许用户在同一个包中创建多个同名的过程,但其参数的数量或类 型不能相同。  性能更佳:首次打包子程序时,整个程序包被加载到内存中,因此在后续调用时不 再需要进行输入输出操作。 4. 提高 4.1 用过程返回结果集 在 Oracle 中的过程不能象 SQL Server 那样直接返回结果集,而必须借助于 REF 游标。 在程序包规范中声明一个 REF 游标和一个过程,代码如下: CREATE OR REPLACE PACKAGE pkg_dept 专题七 过程、函数和程序包 翰子昂系列理论教材 - 151 - AS TYPE deptcursor IS REF CURSOR; PROCEDURE sp_dept_getall(dept_cur OUT deptcursor); END pkg_dept; 程序包的主体部分的代码: CREATE OR REPLACE PACKAGE BODY pkg_dept AS PROCEDURE sp_dept_getall(dept_cur OUT deptcursor) IS BEGIN OPEN dept_cur FOR SELECT * FROM dept; END sp_dept_getall; END pkg_dept; 执行程序包中的过程,输入以下代码: VARIABLE test_cur REFCURSOR; EXECUTE pkg_dept.sp_dept_getall(:test_cur); 系统提示“PL/SQL 过程已成功完成。”后,执行下面的命令: PRINT test_cur; 输出结果为: DEPTNO DNAME LOC ---------- -------------- ------------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON 4.2 在企业管理器中管理过程、函数和程序包 我们可以在 Oracle 10g 的企业管理器(OEM)中用可视化的方式方便地创建、编辑、查 看和删除过程、函数和程序包。 打开浏览器,用 SYS 用户名以 SYSDBA 的身份登陆进入到 OEM 后,在主菜单中选择“管 理”,点击后进入如图 7-3 所示的窗体: Oracle 数据库案例教程_教师用书 - 152 - 翰子昂系列理论教材 图 7-3 企业管理器-数据库实例 在其“方案”的“程序”菜单下,列出的有“程序包”、“程序包体”、“过程”、“函 数”等对象,现在假设我们要管理其中的“过程”对象,点击后出现如图 7-4 所示的界面: 图 7-4 企业管理器-过程 1 在图 7-4 的右下方有一个“创建”按钮,我们可以点击它开始创建新的过程。如果要搜 专题七 过程、函数和程序包 翰子昂系列理论教材 - 153 - 索已经存在的过程,在“方案”文本框中输入用户名(我们输入 SCOTT),在对象名文本框 中可以输入具体的过程名,我们不输表示将搜索 SCOTT 用户下所有的过程,再点“开始”按 钮,系统会为我们搜索出 SCOTT 用户下所有的过程,如图 7-5 所示。 图 7-5 企业管理器-过程 2 从图 7-5 中可以看到有“编辑”、“查看”、“删除”等按钮,至此我们就可以对过程进行 相应的操作了。 其它对象的管理都与之类似,我们在此不再一一阐述。 5. 实验 按照第 3 部分相关实践知识及提高部分依次练习,主要掌握: 1. 不带参数的过程的创建和执行,过程的删除,参见 3.1(15 分钟)。 2. 带输入参数的过程的创建和执行,参见 3.2(10 分钟)。 3. 带输入输出参数的过程的创建和执行,参见 3.3(10 分钟)。 4. 带输出参数的过程的创建和执行,参见 3.4(10 分钟)。 5. 函数的创建、调用和删除,参见 3.5(10 分钟)。 6. 程序包的创建,程序包内元素的调用,程序包的删除,参见 3.6(15 分钟)。 7. 用过程返回结果集,参见提高第 1 部分(10 分钟)。 8. 在企业管理器中管理过程、函数和程序包,参见提高第 2 部分(10 分钟)。 6. 课后作业 以 SCOTT 身份登陆到 Oracle 数据库后完成以下作业: 1. 在 Oracle 的 SQL*Plus 中创建一个程序包,该程序包中包含一个过程,该过程带有一 个输入参数 i_empno(表示员工编号)和一个输出参数 o_empname(表示员工姓名),根据 输入的员工编号查询出表 emp 中员工的姓名,并把查询出的结果输出到屏幕上。执行此过程 Oracle 数据库案例教程_教师用书 - 154 - 翰子昂系列理论教材 以验证其正确性。 2. Oracle 的 SQL*Plus 中创建一个程序包,该程序包中包含一个过程和一个函数,函数 根据输入的参数 i_empno(表示员工编号)来判断表 emp 中是否存在该员工的信息,如果存 在返回字符串:“存在!”,如果不存在,返回字符串“不存在!”;在过程中调用函数, 将查询的结果输出,执行该过程验证其正确性。 专题八 触发器 翰子昂系列理论教材 - 155 - 专题八 触发器 1. 教学目标 1.1 了解触发器的概念和作用 1.2 理解触发器的设计原则 1.3 掌握触发器的组成部分 1.4 熟练使用 DML 触发器 1.5 了解 INSTEAD OF 触发器和系统触发器 1.6 掌握触发器的查看、禁用、激活和删除 2. 工作任务 2.1 使用 BEFORE 行级触发器 2.2 使用 AFTER 行级触发器 2.3 使用 BEFORE 语句级触发器 2.4 使用 AFTER 语句级触发器 2.5 使用 INSTEAD OF 触发器 2.6 使用 DDL 触发器 2.7 使用数据库启动和关闭触发器 2.8 使用用户登录和退出触发器 2.9 管理触发器 3. 相关实践知识 触发器是当某一事件发生时自动执行的特殊的过程,利用它我们可以实现复杂的约束和 业务逻辑,实现更高的数据安全性。 理论知识: 触发器是当某一事件发生时自动执行的特殊的过程,它与过程的主要区别是:过程是由 用户或应用程序显示调用的,而触发器不能被直接调用,当特定事件(如修改表内数据、删 除表或登陆到数据库等)发生时,触发器被自动触发,执行事先定义好的 PL/SQL 块。 触发器的作用 1. 提高数据的安全性。可以基于时间限制用户的操作,如不允许下班后和节假日修改数 据库数据。可以基于数据库中的数据限制用户的操作,如不允许员工工资的升幅一次超过 10 %。 Oracle 数据库案例教程_教师用书 - 156 - 翰子昂系列理论教材 2. 实现数据审计。审计可用于监视非法或可疑的数据库操作。可以通过触发器跟踪用户 对数据库的操作,审计用户操作数据库的语句。把用户对数据库的更新写入自定义的审计表。 3. 实现复杂的数据完整性规则。触发器可用来实现比简单约束更为复杂的约束,实现非 标准的数据完整性检查和约束。 4. 实现复杂的非标准的相关完整性规则。触发器可以对数据库中相关的表进行连环更 新,如对主表中数据进行删除操作时,必须确保从表中相关的数据已经被删除。 5. 自动生成数据值。如果数据的值达到了一定的要求,则进行特定的处理。例如,如果 某种产品的库存低于一个数值时则发出警告信息。 触发器的设计原则 1. 当一个操作执行时,为了确保与其相关的其它操作也必须执行时可以使用触发器。 2. 如果能用简单约束(主键、外键、CHECK 约束等)实现的功能不要使用触发器。 3. 当触发器的代码超过 60 行或触发器大小超过 32KB 时,应先建立过程,再在触发器 中通过 CALL 语句调用过程。 4. 不要建立递归触发器。递归触发器是指在触发器中执行的操作,又直接或间接地触发 了同一个触发器。 触发器的命名 在同一个用户模式下,触发器不允许重名。尽管按照规则触发器名可以与其它的数据库 对象(表、视图等)同名,但不建议这么做。 触发器的组成部分 触发器一般由三部分组成,即触发事件、触发条件和触发操作。 触发事件是指那些可能导致触发器被触发的语句或事件。如 INSERT、UPDATE 或 DEL- ETE 等 DML 语句、创建或删除数据库对象之类的 DDL 语句、登陆或退出数据库的操作等。 触发条件:触发条件是限制触发器触发的条件,是用 WHEN 子句指定的一个布尔表达式, 当表达式的值为 TRUE 时,会自动执行触发器中的代码,如果该表达式的值为 FALSE,则不 执行触发器操作。 此部分是可选的,即触发器中可以没有触发条件。 触发操作是触发器的主体,是包含 SQL 语句的 PL/SQL 块。触发器代码中不能包含 DDL 语句(CREATE、ALTER、DROP)和事务控制语句(COMMIT、ROLLBACK、SAVEPOINT)。 如果触发器中包含这些语句,编译时可能不报错,但在触发器被执行会抛出异常。但对于系 统级触发器,可以包含{CREATE|ALTER|DROP}TABLE 语句和 ALTER…COMPILE 语句。 触发器的类型 按触发事件的不同,触发器主要可分为三大类: DML 触发器、INSTEAD OF 触发器和系 统触发器。 3.1 BEFORE 行级触发器 公司规定,如果给员工加薪,每次的加薪比例不能超过本人原工资的 10%,为实现这一 功能,可以在表 emp 上建立一个 BEFORE 行级触发器(DML 触发器中的一种)。 专题八 触发器 翰子昂系列理论教材 - 157 - 理论知识: DML 触发器是指在表上建立的由 DML 语句触发的触发器。当在表上执行 INSERT、UP- DATE 或 DELETE 等 DML 语句时,如果已经建立了 DML 触发器,则触发器中的代码将被自动 执行。它的触发事件是 DML 语句。 创建 DML 触发器的语法为: CREATE OR REPLACE TRIGGER trigger_name {BEFORE|AFTER} {INSERT|DELETE|UPDATE [OF column_list]} [OR {INSERT|DELETE|UPDATE [OF column_list]}] ON [schema.]table_name [REFERENCING [NEW AS new_alias] [OLD AS old_alias]] [FOR EACH ROW] [WHEN (condition)] PL/SQL Block; 其中: 1. trigger_name 是触发器的名称。 2. BEFORE 和 AFTER 是触发时机,是指触发器是在触发事件之前或之后触发。当使用 BEFORE 关键字时,触发器在触发事件之前执行;当使用 AFTER 关键字时,触发器在触发事 件之后执行; 3. INSERT、DELETE 和 UPDATE 指触发事件,是导致触发器被触发的操作类型(插入、 修改或删除)。当使用 UPDATE 时还可以指定要修改的列的列表。 可以在触发器中同时包含多个触发事件,之间用 OR 关键字隔开。此时在触发器语句中, 如有必要,可以使用条件谓词 INSERTING、DELETING 和 UPDATING 来区分具体的操作类 型。  INSERTING:当触发事件是 INSERT 时,该条件谓词的返回值是 TRUE,否则返回 FALSE;  DELETING:当触发事件是 DELETE 时,该条件谓词的返回值是 TRUE,否则返回 FALSE;  UPDATING:当触发事件是 UPDATE 时,该条件谓词的返回值是 TRUE,否则返回 FALSE; 4. schema.table_name 是要建立触发器的表名,如为当前用户模式下的表建立触发 器,模式名 schema 可以省略。 5. REFERENCING 语句:可以使用此语句为新行和旧行指定别名,默认情况下,新行名 为 NEW,旧行名为 OLD。 在行级触发器的语句体中,可以引用 DML 语句中涉及的新值和旧值。旧 值是指在 DML 语 句前存在的数据,新值是指由 DML 语句插入或更新进去的数据。引用的方式是“:NEW.列名” 和“:OLD.列名”(注意如果在触发器的触发条件中引用不能加冒号),如果为新行或旧行 Oracle 数据库案例教程_教师用书 - 158 - 翰子昂系列理论教材 起了别名,则用别名代替其中的 NEW 或 OLD。 使用新值和旧值时还要注意:  如果触发器是用 INSERT 语句触发的,则只能引用新值。  如果触发器是用 DELETE 语句触发的,则只能引用旧值。  如果触发器是用 UPDATE 语句触发的,则既可以引用新值,也可以引用旧值。 6. FOR EACH ROW 语句是行级触发器所必须的,如有此语句则对受影响的每行都执行 一次触发器,如没有此语句则为语句级触发器。 7. WHEN (condition)是触发器的触发条件,它不是必需的。 8. PL/SQL Block 是一个标准的 PL/SQL 块,或是调用过程的 CALL 语句。 DML 触发器主要可分为两类:行级触发器和语句级触发器。 行级触发器 行级触发器是指在执行 DML 操作时,每作用一行就触发一次的触发器。如对数据库的表 执行删除操作时,一次删除了多行,则每删除一行,就会激活一次行级触发器。行级触发器 是触发器中最常用的一种,常用于数据审计和实现复杂的业务逻辑。创建行级触发器时必须 加上 FOR EACH ROW 子句,凡是还 FOR EACH ROW 子句的触发器都是行级触发器。 按照触发时机的不同,行级触发器可分为 BEFORE 行级触发器和 AFTER 行级触发器。 BEFORE 行级触发器指在触发器创建语句中含有 BEFORE 关键字的行级触发器。 在进行数据库设计时,为了确保数据符合一定的规则和逻辑,可以使用约束来实现。但 是有些复杂的业务规则和逻辑则用约束无法实现,此时可以考虑使用 BEFORE 行级触发器。 1. 从开始菜单中打开 SQL*Plus 工具,以 SCOTT 用户的身份登录到数据库。 2. 在 SQL 提示符下输入如下的代码来创建触发器。 CREATE OR REPLACE TRIGGER tr_emp_bef_row BEFORE UPDATE OF sal ON emp FOR EACH ROW DECLARE l_scale NUMBER; BEGIN l_scale:=(:NEW.sal-:OLD.sal)/:OLD.sal; IF l_scale>0.1 THEN DBMS_OUTPUT.PUT_LINE('对不起,员工的加薪比例不能超过 10%!'); :NEW.sal:=:OLD.sal*1.1; END IF; END TRIGGER; / 3. 按回车键后,系统提示“触发器已创建”,说明触发器创建成功。 专题八 触发器 翰子昂系列理论教材 - 159 - 4. 假如现在要给编号是 7369 的员工加薪。先用如下的查询语句查看一下他的原工资。 SELECT empno,sal FROM emp WHERE empno=7369; 可以查出编号是 7369 的员工的原工资是 800。 5. 计划给该员工加薪到 1000,用如下的 UPDATE 语句修改。 UPDATE emp SET sal=1000 WHERE empno=7369; 6. 按回车后,系统提示“对不起,员工的加薪比例不能超过 10%!”和“已更新一行”, 再同第 4 步相同的查询语句,查看该员工的工资,结果如图 8-1 所示。 图 8-1 查看该员工的工资 从图中可以看出,该员工的工资被修改为 880,而不是 1000,触发器被执行。 7. 假定现在公司的规定变为:只有原工资高于 1500 的员工加薪才不能超过 10%,低于 1000 的不再受此限制。可在第 2 步的创建触发器的代码中,在 FOR EACH ROW 子句的后 面加入如下触发条件。 WHEN (OLD.sal>1500) 8. 创建触发器成功后,继续用如下的 UPDATE 语句想把编号是 7369 的员工的工资改为 1000。 UPDATE emp SET sal=1000 WHERE empno=7369; 9. 语句执行后系统提示“已更新一行”,再用语句查询可以查出该员工的工资被更新为 1000。而不是 880×1.1=968,可见因为触发条件的限制,触发器内的触发操作语句并没有被 执行。 10. 如果把创建触发器的代码中的“BEFORE”改为“AFTER”,按回车后将会出现如图 8-2 所示的结果。 Oracle 数据库案例教程_教师用书 - 160 - 翰子昂系列理论教材 图 8-2 AFTER 行级触发器中更改 NEW 值的编译结果 由此可见,只能在 BEFORE 行级触发器中重新设置 NEW 值,而不能在 AFTER 触发器 中更改 NEW 值。 3.2 AFTER 行级触发器 现要审计员工的工资变动情况,用一个表来记录相关信息,利用 AFTER 行级触发器向表 中插入数据。 理论知识: AFTER 行级触发器指在触发器创建语句中含有 AFTER 关键字的行级触发器。 为了审计 DML 操作造成的数据变化,可以考虑使用 AFTER 行级触发器。 在使用 AFTER 行级触发器时,可以引用新值,但是不允许重新设置新值。 1. 以 SCOTT 用户身份登录到数据库后,建立记录工资变动情况的表 t_em_sal_change, 在 SQL 提示符下输入如下的建表语句。 CREATE TABLE t_emp_sal_change ( time DATE, empno NUMBER(4), oldsal NUMBER(7,2), newsal NUMBER(7,2) ) / 2. 在 SQL 提示符下,输入以下的代码来创建 AFTER 行级触发器。 CREATE OR REPLACE TRIGGER tr_emp_aft_row AFTER UPDATE OF sal ON emp FOR EACH ROW BEGIN INSERT INTO t_emp_sal_change VALUES(systimestamp,:OLD.empno,:OLD.sal,:NEW.sal); END TRIGGER; / 专题八 触发器 翰子昂系列理论教材 - 161 - 3. 修改部门编号是 10 的所有员工的工资,给每人加薪 100。 UPDATE emp SET sal=sal+100 WHERE deptno=10; 4. 语句执行后系统提示“已更新 3 行”,再用如下的 SELECT 语句查询表 t_em_sal_ch- ange 内的信息,如图 8-3 所示。 图 8-3 查询表 t_emp_sal_change 中的记录 可见,利用 AFTER 行级触发器,员工工资的变动情况被自动记录到表 t_emp_sal_change 中,实现了对工资变动情况的审计。 3.3 BEFORE 语句级触发器 emp 表中包括员工工资等敏感数据,为了防止有人随意修改,公司规定只有 SCOTT 用户 才有权修改此表,现通过在 emp 表上建立 BEFORE 语句级触发器来实现此功能。 理论知识: 语句级触发器 语句级触发器在执行 DML 语句时被自动触发,每个 DML 语句触发一次,即无论一个 D- ML 语句影响了多少行数据,语句级触发器都只执行一次。语句级触发器通常用于审计 DML 操作和强制实施额外的安全措施。 创建语句级触发器时不能包含 WHEN 子句。 按照触发时机的不同,语句级触发器可分为两种:BEFORE 语句级触发器和 AFTER 语句 级触发器。 BEFORE 语句级触发器指在触发器创建语句中含有 BEFORE 关键字的语句级触发器。B- EFORE 语句级触发器常用于提供额外的安全性措施。如某单位规定不允许在下班时间及周六 周日修改改数据,则可以用 BEFORE 语句级触发器实现。 1. 以 SCOTT 用户身份登录到数据库后,在 SQL 提示符下,输入以下的代码来创建 BE- FORE 语句级触发器。 CREATE TRIGGER tr_emp_bef BEFORE INSERT OR UPDATE OR DELETE ON emp BEGIN Oracle 数据库案例教程_教师用书 - 162 - 翰子昂系列理论教材 IF user NOT IN ('SCOTT') THEN RAISE_APPLICATION_ERROR(-20001,'您无权修改 emp 表'); END IF; END TRIGGER; / 2. 系统提示“触发器已创建“后,为了测试触发器是否能真正实现功能,在 SQL 提示符 下输入如下的代码,以 SYSDBA 身份登录到数据库。 CONNECT / AS SYSDBA; 3. 系统管理员想向表 emp 中插入一条记录,在 SQL 提示符下输入如下的代码。 INSERT INTO emp(empno,ename) VALUES(‘8888’,’aaa’); 4. 按回车后系统出现如图 8-4 所示的错误提示。 图 8-4 越权修改 emp 表的结果图 5. 在 SQL 提示符下,输入如下的代码,以 SCOTT 用户的身份登录到数据库。 CONNECT SCOTT/TIGER; 6. 输入与上面第 3 步相同的代码,系统提示“已创建一行”,说明操作成功。 从以上的操作可以看出,加上此 BEFORE 语句级触发器后,不允许除 SCOTT 用户之外 的其他任何用户修改表 emp,即使管理员也不行。 3.4 AFTER 语句级触发器 为了审计数据库用户对表 emp 所做的 DML 操作,可以用 AFTER 语句级触发器来实现此 功能。 理论知识: AFTER 语句级触发器指在触发器创建语句中含有 AFTER 关键字的语句级触发器。AFT- ER 语句级触发器一般用来对 DML 操作进行审计。 相关理论知识: DML 触发器的触发顺序 专题八 触发器 翰子昂系列理论教材 - 163 - 如果在一个表上同时定义了多种 DML 触发器,则当执行 DML 操作时,各种 DML 触发器 的触发顺序如下: 1. BEFORE 语句级触发器 2. BEFORE 行级触发器 3. AFTER 行级触发器 4. AFTER 语句级触发器 1. 以 SCOTT 用户的身份登录到数据库,在 SQL 提示符下,输入如下的代码,创建记录 DML 操作的表 t_emp_dml。 CREATE TABLE t_emp_dml ( who VARCHAR2(10), when DATE, operater VARCHAR2(10) ); 2. 输入如下的代码创建 AFTER 语句级触发器。 CREATE OR REPLACE TRIGGER tr_emp_aft AFTER INSERT OR UPDATE OR DELETE ON emp BEGIN CASE WHEN INSERTING THEN INSERT INTO t_emp_dml VALUES(user,sysdate,'INSERT'); WHEN UPDATING THEN INSERT INTO t_emp_dml VALUES(user,sysdate,'UPDATE'); WHEN DELETING THEN INSERT INTO t_emp_dml VALUES(user,sysdate,'DELETE'); END CASE; END TRIGGER; / 3. 按回车系统提示“触发器已创建”后,为验证其功能,在 SQL 提示符下按如下的代码 所示输入一些测试数据。 INSERT INTO emp(empno,ename) VALUES(1000,'TEST1'); INSERT INTO emp(empno,ename) VALUES(1000,'TEST2'); UPDATE emp SET ename='TEST3' WHERE empno=1000; DELETE FROM emp WHERE empno=1000 OR empno=1001; 4. 用如下的 SELECT 语句查询表 t_emp_dml 的内容。 Oracle 数据库案例教程_教师用书 - 164 - 翰子昂系列理论教材 SELECT * FROM t_emp_dml; 5. 查询结果如图 8-5 所示。 图 8-5 查询 t_emp_dml 表的结果图 可以看出,表 t_emp_dml 记录了对表 emp 的 DML 操作。请注意上面第 3 步中的 DELETE 语句尽管一次删除了两条记录,但因为是语句级触发器而非行级触发器,所以只在表 t_emp_dml 添加了一条信息。 3.5 INSTEAD OF 触发器 理论知识: INSTEAD OF 触发器是在视图上而非表上定义的触发器。对于简单的视图,可以直接在 视图上执行 DML 语句而更新基表。但对于复杂的视图,执行 DML 语句则有某些限制(本书专 题四中曾有介绍),此时可通过 INSTEAD OF 触发器,用触发器内的语句来代替实际的 DML 语句,从而允许用户更新本来不能用 DML 语句直接更新的视图。  使用 INSTEAD OF 触发器,需注意以下几点:  INSTEAD OF 触发器只能应用于视图,而不能应用于表。  INSTEAD OF 触发器只能是行级的,不能是语句级的,定义 INSTEAD OF 触发器时 必须加上 FOR EACH ROW 选项。  在 INSTEAD OF 触发器不能包含 WHEN 子句。  INSTEAD OF 触发器的定义中不能包含 BEFORE 和 AFTER 选项。 建立 INSTEAD OF 触发器的语法为: CREATE OR REPLACE TRIGGER trigger_name INSTEAD OF {INSERT|DELETE|UPDATE [OF column_list]} [OR {INSERT|DELETE|UPDATE [OF column_list]}] ON [schema.]view_name [REFERENCING [NEW AS new_alias] [OLD AS old_alias]] FOR EACH ROW PL/SQL Block; 专题八 触发器 翰子昂系列理论教材 - 165 - 1. 基于表 emp 和 dept 创建视图 v_dept_emp,如下面的代码所示。 CREATE OR REPLACE VIEW v_dept_emp AS SELECT d.deptno,d.dname,e.empno,e.ename FROM dept d,emp e WHERE d.deptno=e.deptno; 2. 视图创建完成后,可以正常地执行查询语句。如可以用下面的代码来查询视图。 SELECT * FROM v_dept_emp; 查询结果如图 8-6 所示。 图 8-6 查询视图的结果图 3. 但是,如果想用如下的 INSERT 语句直接向视图中插入数据则会报错。 INSERT INTO v_dept_emp VALUES(10,'ACCOUNTING',2222,'TOM'); 错误结果如图 8-7 所示。 图 8-7 直接向视图中插入数据报错结果 4. 假如我们事先建立了如下所示的 INSTEAD OF 触发器,则不会出错。 CREATE OR REPLACE TRIGGER tr_v_dept_emp_inf_row INSTEAD OF INSERT ON v_dept_emp FOR EACH ROW Oracle 数据库案例教程_教师用书 - 166 - 翰子昂系列理论教材 DECLARE l_temp PLS_INTEGER; BEGIN SELECT COUNT(*) INTO l_temp FROM dept WHERE deptno=:NEW.deptno; IF l_temp=0 THEN INSERT INTO dept(deptno,dname) VALUES(:NEW.deptno,:NEW.dname); END IF; SELECT COUNT(*) INTO l_temp FROM emp WHERE empno=:NEW.empno; IF l_temp=0 THEN INSERT INTO emp(empno,ename,deptno) VALUES(:NEW.empno,:NEW.ename,:NEW.deptno); END IF; END TRIGGER; / 5. 系统提示“触发器已创建”后,再输入与第 3 步相同的 INSERT 语句,系统提示“已 创建一行”,不再报错。 6. 分别查询视图 v_dept_emp 和表 dept、emp 的记录,如图 8-8 所示。 图 8-8 查询视图 v_dept_emp 和表 dept、emp 从图中可以看出,视图中多了一条记录。dept 表中因为原来就有编号是 10 的部门,因此 并没有新的数据被添加。表 emp 中添加了一条记录。实际上,通过 INSTEAD OF 触发器,对 专题八 触发器 翰子昂系列理论教材 - 167 - 视图的操作转化为了对基表的操作。 3.6 DDL 触发器 理论知识: 系统触发器 系统触发器是被 Oracle 系统事件自动触发的触发器。这里的 Oracle 系统事件包括启 动和关闭数据库、用户登陆和退出以及各种 DDL 操作(如 CREATE TABLE、ALTER TABLE 等)。按触发的系统事件的不同,可将系统级触发器分为 DDL 触发器(包括模式级的和数据 库级的)、实例启动和关闭触发器和用户登陆和退出触发器等。 建立系统触发器的语法为: CREATE [OR REPLACE] TRIGER trigger_name {BEFORE|AFTER} system_event ON {SCHEMA|DATABASE} [WHEN (condition)] Trigger_body; DDL 触发器 为了记载系统的各种 DDL 操作和防止些 DDL 操作,可以建立 DDL 触发器。DDL 操作包 括 CREATE、ALTER、DROP 等。当有用户执行这些 DDL 操作时,就会触发相应的 DDL 触发 器。它的主要功能是为了阻止 DDL 操作或在发生 DDL 操作时提供额外的安全监控。 DDL 触发器可分为模式级的和数据库级的。模式级的只在创建 DDL 触发器的用户模式下 执行 DDL 操作时才被触发,对于数据库级的,当在任何一个用户模式下执行了 DDL 操作时, 相应的的 DDL 触发器都会被触发。 如果某个表非常重要,不允许被删除,可以创建 DDL 触发器来阻止用户的删除操作。 1. 以 SYSDBA 身份登录到数据库,在 SQL 提示符下,输入如下的代码来创建一个表 t_ test。 CREATE TABLE t_test (id NUMBER); 2. 假定这个表不允许被删除,可以输入如下的代码来创建 DDL 触发器,请注意必须用 BEFORE 关键字,不能是 AFTER 的。 CREATE OR REPLACE TRIGGER tr_t_test_ddl BEFORE DROP ON SCHEMA BEGIN IF ora_dict_obj_name='T_TEST' THEN RAISE_APPLICATION_ERROR(-20003,'表 t_test 不允许被删除!'); END IF; END TRIGGER; / Oracle 数据库案例教程_教师用书 - 168 - 翰子昂系列理论教材 其中的 ora_dict_obj_name 是系统事件属性函数,它返回 DDL 操作所对应的数据库对象 名。 3. 创建此触发器后,用户如果执行了删除表 t_test 的操作“DROP TABLE t_test;”,将 会 出现如图 8-9 所示的错误,提示表不允许被删除。 图 8-9 删除 t_test 表报错 4. 以 SYSDBA 身份登录到数据库,执行如下的删除表的操作。 DROP TABLE scott.t_test; 5. 系统会提示“表已删除”。这是因为上面创建的触发器是模式级的,即只有在 SCOTT 用户模式下,触发器才起作用。如果想让 SYSDBA 也不能删除表 t_test,需要以 SYSDBA 身 份登录到数据库后,把触发器创建成数据库级的,即在创建触发器代码中把“SCHEMA”改 成“DATABASE”。 6. 修改完成后,再试一下第 4 步的删除表的语句,执行结果与 8-9 相同。 3.7 数据库启动和关闭触发器 理论知识: 为了记载数据库的启动和关闭事件,可以分别建立数据库启动和关闭触发器。当执行 S- TARTUP 操作时,触发数据库启动触发器,当执行 SHUTDOWN 操作时,触发数据库关闭触发 器。只有特权用户才可以建立此类的触发器,且启动触发器只能用 AFTER 关键字,关闭触发 器只能用 BEFORE 关键字。 1. 以 SYSDBA 身份登录到数据库。 2. 为了记载数据库启动和关闭情况,先创建一个表 t_db_event。 CREATE TABLE t_db_event ( time DATE, event VARCHAR2(10) ); 3. 创建数据库启动触发器。 CREATE OR REPLACE TRIGGER tr_startup 专题八 触发器 翰子昂系列理论教材 - 169 - AFTER STARTUP ON DATABASE BEGIN INSERT INTO t_db_event VALUES(sysdate,’STARTUP’); END; 4. 创建数据库关闭触发器。 CREATE OR REPLACE TRIGGER tr_shutdown BEFORE SHUTDOWN ON DATABASE BEGIN INSERT INTO t_db_event VALUES(sysdate,’SHUTDOWN’); END; 5. 用如下的代码,依次关闭和打开数据库。 SQL>SHUTDOWN IMMEDIATE SQL>STARTUP 6. 用如下的 SELECT 语句查询查询 t_db_event 表的信息。 SELECT TO_CHAR(time,'YYYY-MM-DD HH24:MI:SS') AS ope_time,event FROM t_db_event; 查询结果如图 8-10 所示。 图 8-10 查询 t_db_event 表的信息 3.8 用户登录和退出触发器 理论知识: 分别建立用户登录和退出触发器,可以记录用户登录和退出数据库这一类的系统事件。 当用户登录到数据库时,触发用户登录触发器。当用户退出数据库时,触发用户退出触发器。 只有特权用户才可以建立此类的触发器,且用户登录触发器只能用 AFTER 关键字,用户退出 触发器只能用 BEFORE 关键字。 1. 以 SYSDBA 身份登录到数据库。 2. 为了记载用户登录和退出情况,先创建一个表 t_user_log。 CREATE TABLE t_user_log Oracle 数据库案例教程_教师用书 - 170 - 翰子昂系列理论教材 ( username VARCHAR(20), time_logon DATE, time_logoff DATE ); 3. 创建用户登录触发器。 CREATE OR REPLACE TRIGGER tr_user_logon AFTER LOGON ON DATABASE BEGIN INSERT INTO t_user_log(username,time_logon) VALUES(user,sysdate); END; 4. 创建用户退出触发器。 CREATE OR REPLACE TRIGGER tr_user_logoff BEFORE LOGOFF ON DATABASE BEGIN INSERT INTO t_user_log(username,time_logoff) VALUES(user,sysdate); END; 5. 在 SQL 提示符下输入如下的代码。 CONNECT SCOTT/TIGER; CONNECT / AS SYSDBA; 6. 执行如下的 SELECT 语句查询 t_user_log 表的内容。 SELECT username, TO_CHAR(time_logon,'YYYY-MM-DD HH24:MI:SS') AS logon_time, TO_CHAR(time_logoff,'YYYY-MM-DD HH24:MI:SS') AS logoff_time FROM t_user_log; 查询结果如图 8-11 所示。 图 8-11 查询 t_user_log 表的内容 专题八 触发器 翰子昂系列理论教材 - 171 - 3.9 管理触发器 1. 以 SCOTT 身份登录到数据库。 2. 先用 DESC 语句查看数据字典视图 user_triggers 的结构。 DESC user_triggers; 查询结果如图 8-12 所示。 图 8-12 查看视图 user_triggers 的结构 3. 用数据字典视图 user_triggers 查看基于表 emp 创建的触发器。 COLUMN trigger_name FORMAT A16 --设置 trigger_name 列的显示宽度 COLUMN triggering_event FORMAT A30 设置 triggering_event 列的显示宽度 SELECT trigger_name,trigger_type,triggering_event,status FROM user_triggers WHERE table_name=’EMP’; 查询的结果如图 8-13 所示。 图 8-13 查看基于表 emp 创建的触发器 理论知识: 通过数据字典视图 USER_TRIGGERS 可以查看当前用户所包含的所有触发器信息。 Oracle 数据库案例教程_教师用书 - 172 - 翰子昂系列理论教材 4. 现因工作需要,想把 3.1 中所创建的 BEFORE 行级触发器 tr_emp_bef_row 临时禁用, 可在 SQL 提示符下输入如下的语句。 ALTER TRIGGER tr_emp_bef_row DISABLE; 5. 系统提示“触发器已更改”,表明触发器禁用成功。 理论知识: 默认情况下,触发器一旦建立,会立即生效,但有时侯需要使其临时失效,即禁用。如 进行大量的数据加载时为了提高速度,就可以临时禁用触发器。 禁用触发器的语法如下: ALTER TRIGGER trigger_name DISABLE; 6. 把禁用的触发 tr_emp_bef_row 激活,输入如下的代码。 ALTER TRIGGER tr_emp_bef_row ENABLE; 理论知识: 默认情况下,触发器建立后立即生效无需激活,但当触发器被禁用后可能需要重新启用, 此时就需要激活它,使其在适当的时侯触发。 激活触发器的语法如下: ALTER TRIGGER trigger_name ENABLE; 另外,使用下面的语句可以禁用或激活某个表上的所有触发器。 ALTER table_name {ENABLE|DISABLE} ALL TRIGGERS; 7. 删除触发器 tr_emp_bef_row,可在 SQL 提示符下输入如下的代码。 DROP TRIGGERS trigger_name; 理论知识: 在表上的触发器越多,对 DDL 操作的性能影响也越大,所以应适当地使用触发器。因此, 有时侯需要删除已经建立的触发器。 用于删除数据库中的触发器的语法如下: DROP TRIGGERS trigger_name; 4. 实验 按照第 3 部分相关实践知识依次练习,主要掌握: 1. BEFORE 行级触发器的使用,参见 3.1(10 分钟)。 2. AFTER 行级触发器的使用,参见 3.2(10 分钟)。 3. BEFORE 语句级触发器的使用,参见 3.3(10 分钟)。 专题八 触发器 翰子昂系列理论教材 - 173 - 4. AFTER 语句级触发器的使用,参见 3.4(15 分钟)。 5. INSTEAD OF 触发器的使用,参见 3.5(15 分钟)。 6. DDL 触发器的使用,参见 3.6(15 分钟)。 7. 数据库启动和关闭触发器的使用,参见 3.7(15 分钟)。 8. 用户登录和退出触发器的使用,参见 3.8(15 分钟)。 9. 管理触发器,参见 3.9(15 分钟)。 5. 课后作业 以 SCOTT 身份登陆到 Oracle 数据库后完成以下作业: 1. 用触发器完成专题 4 实践 3.2 部分留下的问题。 2. 公司规定,对表 emp 进行任何修改(INSERT、UPDATE、DELETE)都只能在上班时 间(8:00 到 18:00)进行,用触发器实现此功能。 Oracle 数据库案例教程_教师用书 - 174 - 翰子昂系列理论教材 专题九 数据库高级管理 1. 教学目标 1.1 理解 Oracle 数据库的用户和角色管理机制 1.2 理解 Oracle 数据库用户的两种认证方式 1.3 理解备份与恢复原理 1.4 掌握如何调整数据库为归档模式 1.5 掌握数据的导入导出 2. 工作任务 2.1 用户和角色的创建和使用,及设置用户登录 Oracle 数据库的方式。 2.2 调整数据库为归档模式。 2.3 使用导出命令,导出 SCOTT 模式下的 emp、dept 表;然后用导入命令将这两个表导 入到另一个用户模式下。 3. 相关实践知识 从开始菜单中打开 SQL*Plus 工具,以 SYS 用户的身份登录到数据库。 3.1 用户和角色的创建和使用 3.1.1 创建和使用用户 1. 创建一个 20M 大小的表空间,以后用户创建的数据库对象保存在这个表空间中,在 SQL 提示符下,输入如下代码: CREATE TABLESPACE student_data DATAFILE ‘F:\oracle\oradata\orcl\student_data’ SIZE 20M AUTOEXTEND OFF; 说明:路径可以随意设置,建议 AUTOEXTENTD 属性为 OFF。 按回车键,系统提示“表空间已创建”。 2. 创建用户,在 SQL 提示符下输入如下代码: CREATE USER student IDENTIFIED BY abcdef DEFAULT TABLESPACE student_data 专题九 数据库高级管理 翰子昂系列理论教材 - 175 - TEMPORARY TABLESPACE TEMP; 按回车键,系统提示“创建用户成功”。这样就创建了一个用户名为 student,密码为 abcdef 的用户。但此用户还不能连接到数据库,更不能创建表及执行各种操作,还需要给其赋予权 限。 3. 给 student 用户赋予相应的权限,一般是将两个角色(CONNECT,RESOURCE)赋给 此用户即可。作为一般的开发用户,这两个权限已经足够。在 SQL 提示符下输入如下代码: GRANT CONNECT,RESOURCE TO student; 按回车键,系统提示“授权成功”。 理论知识: Oracle 是多用户系统,它允许多用户共享系统资源。为了保证数据库系统的安全,数 据库管理系统配置了良好的安全机制。 其中很关键的一种机制就是:建立用户级的安全保证。 用户级安全保障通过用户口令和角色机制(一组权利)来实现。引入角色机制的目的是 简化对用户的授权与管理。做法是把用户按照其功能分组,为每个用户建立角色,然后把角 色分配给用户,具有同样角色的用户有相同的特权。 Oracle 的用户根据所被授予的权限分为系统权限和对象权限,系统权限经常被包含在 角色中授予,新建一个用户时,首先要赋予 CONNECT 角色,CONNECT 角色中包含了 CREA- TE SESSION 等 8 个系统权限,拥有 CREATE SESSION 权限是连接数据库的必要条件,如 果想用 Enterprise Manager console 连接的话,还必须拥有 SELECT ANY DICTION- ARY 权限。 如果是开发人员,一般还要授予 RESOURCE 角色,此角色包含了开发人员所需要的基本 权限。比如 CREATE PROCEDURE、CREATE SEQUENCE、CREATE TABLE、CREATE TRI- GGER 等。 在所有权限中,最高的权限是 SYSDBA。SYSDBA 具有控制 Oracle 一切行为的特权, 诸如创建、启动、关闭、恢复数据库,使数据库归档/非归档,备份表空间等,关键性的动作 只能通过具有 SYSDBA 权限的用户来执行。这些任务即使是普通 DBA 角色也不行。 SYSOPER 是一个与 SYSDBA 相似的权限,只不过比 SYSDBA 少了 SYSOPER PRIVILE- GES WITH ADMIN OPTION,CREATE DATABASE,RECOVERDATABASE UNTIL 这几个权 限而已。 4. 用新创建的 student 用户登录数据库,在 SQL 提示符下输入如下代码: CONN student/abcdef; 按回车键,系统提示“已连接”。 5. 在 student 用户模式下创建一个表,在 SQL 提示符下,输入如下代码: CREATE TABLE grade ( Oracle 数据库案例教程_教师用书 - 176 - 翰子昂系列理论教材 gradeno NUMBER PRIMARY KEY, Name VARCHAR2(10) ); 按回车键,系统提示“表创建成功”。 3.1.2 创建并使用角色 1. 使用具有 SYSDBA 权限的用户登录数据库,使用如下语句创建一个角色: CREATE ROLE developer_role; 按回车将,系统提示“角色已创建”。 2. 将权限授权给角色 GRANT CREATE SYNONYM,CREATE VIEW TO developer_role; 按回车键,系统提示“授权成功”。 将创建同义词和视图的角色授权给 developer_role 角色。 3. 也可以将角色授权给某个角色 GRANT RESOURCE,CONNECT TO developer_role; 按回车键,系统提示“授权成功”。 这样就将 RESOURCE、CONNECT 两个角色具有的权限授权给了 developer_role 角色。 4. 将角色授权给用户: GRANT developer_role TO SCOTT; 按回车键,系统提示“授权成功”。 这样 SCOTT 用户就拥有了 developer_role 角色。 理论知识: 在创建用户和给用户授权的过程中,会发现一个问题:如果有一组人,他们的所需的权 限是一样的,当对他们的权限进行管理的时候会很不方便。因为你要对这组中的每个用户的 权限都进行管理。 有一个很好的解决办法就是:角色。角色是一组权限的集合,将角色赋给一个用户,这 个用户就拥有了这个角色中的所有权限。那么上述问题就很好处理了,只要第一次将角色赋 给这一组用户,接下来就只要针对角色进行管理就可以了。 以上是角色的一个典型用途。其实,只要明白:角色就是一组权限的集合。下面分两个 部分来对 Oracle 角色进行说明。 系统预定义角色 预定义角色是在数据库安装后,系统自动创建的一些常用的角色。下介简单的介绍一下 这些预定角色。角色所包含的权限可以用以下语句查询: 专题九 数据库高级管理 翰子昂系列理论教材 - 177 - SELECT * FROM role_sys_privs WHERE role='角色名'; CONNECT、RESOURCE、DBA 这些预定义角色主要是为了向后兼容。其主要是用于数据 库管理。Oracle 建议用户自己设计数据库管理和安全的权限规划,而不要简单的使用这些 预定义角色。将来的版本中这些角色可能不会作为预定义角色。 DELETE_CATALOG_ROLE、EXECUTE_CATALOG_ROLE、SELECT_CATALOG_ROLE 这 些角色主要用于访问数据字典视图和包。 EXP_FULL_DATABASE 和 IMP_FULL_DATABASE 这两个角色用于数据导入导出工具的 使用。 AQ_USER_ROLE 和 AQ_ADMINISTRATOR_ROLE 这两个角色用于 oracle 高级查询功 能。(AQ:Advanced Query) RECOVERY_CATALOG_OWNER 用于创建拥有恢复库的用户。 自定义角色 自定义角色是数据库的管理员根据实际情况自行创建的角色,并为他们分配某些权限。 创建自定义角色的语法为: SELECT * FROM role_sys_privs WHERE role='角色名'; 为角色授权等操作与用户基本相同,请参见实践部分。 3.1.3 两种用户认证方式 1. 即使忘记 SYS 的用户名,也可以登录数据库。在控制台 DOS 命令下输入如下代码: SQLPLUS /NOLOG 2. 按回车后系统进入 SQL 提示符状态,然后接着输入如下命令使用 SYS 用户连接到数 据库: CONN SYS/AS SYSDBA; 3. 系统提示输入“请输入口令:”,直接回车,系统提示“已连接”。 4. 判断是否登录用户为 SYS,输入如下代码: SHOW USER; 5. 按回车后系统提示“USER 为"SYS"”,说明当前用户已经是 SYS。 没有输入密码也可以登录系统,这对系统的安全性带来了问题。能不能通过修改设置要 求必须输入密码才能登录呢? 6. 打开%oracle_home%/network/admin/sqlnet.ora 文件,如图 9-1 所示。 Oracle 数据库案例教程_教师用书 - 178 - 翰子昂系列理论教材 图 9-1 文件 sqlnet.ora 的内容 注意: %oracle_home%表示 Oracle 数据库的安装路径,这是 windows 平台的写 法,在注册表中定义。在 UNIX 平台下$oracle_home。可以通过注册表查看。 7. 将此文件中如图中的第三行指令注释掉,注释符为“#”。 8. 注释后的文件的内容如图 9-2 所示。 图 9-2 修改后文件 sqlnet.ora 的内容 9. 然后测试是否能够不使用密码登录数据库。输入如下代码: CONN SYS/AS SYSDBA; 10. 系统提示输入“Enter password:”,直接回车,系统提示如下: SQL> conn sys/as sysdba 请输入口令: ERROR: ORA-01031: insufficient privileges 警告: 您不再连接到 Oracle。 以上信息说明,已经不能通过不输入密码而登录数据库了。 通过以上设置将数据的登录模式从 OS 认证修改为了密码文件认证。 专题九 数据库高级管理 翰子昂系列理论教材 - 179 - 理论知识: 对于用户的身份验证有三种方式:密码验证、外部验证(操作系统验证)、全局验证。 密码验证是最常见的一种方式,即将用户信息码存储在数据目录里。对 SYSDBA 的认证方式 有两种:操作系统认证和密码文件认证。具体选择那一种认证方式取决于:你是想在 Orac- le 运行的机器上维护数据库,还是在一台机器上管理分布于不同机器上的所有的 Oracle 数 据库。若选择在本机维护数据库,则选择操作系统认证可能是一个简单易行的办法;若有好 多数据库,想进行集中管理,则可以选择 password 文件认证方式。 如果使用操作系统认证方式的话,那数据库服务器的操作系统的密码将是非常重要的, 如果这个密码外泄,那么入侵者可以轻松地以 CONN / AS SYSDBA 连接到数据库,并具有 至高权限!操作系统认证方式的配置过程如下: 第一步:在操作系统中建立一个合法帐户。具体来说,在 NT 上,首先建立一个本地用户 组,取名为 ORA__DBA,其中 SID 为该数据库实例的 SID,或者建立一个 ORA_DBA 地组,该组不对应于任何一个单独的 Oracle 实例。这样当一个 NT 上有好几个 Oracle 实 例时,不用分别管理。 第二步:在 NT 上建立一个用户,并且把它归入该组中。但是实际上这两步在 Oracle 10g 安装过程中已经自动完成了,一般不用手动进行。 第三步:在 sqlnet.ora(位于%Oracle_HOME%/NETWORK/ADMIN 目录中)中,把 SQLNET.AUTHENTICATION_SERVICES 设置为 SQLNET.AUTHENTICATION_SERVICES= (NTS),意思为使用 NT 认证方式。 第四步:在 INIT.ORA(若无此文件,进入 SQL*PLUS,以 SYS 登录,运行 CRE ATE pfile FROM SPFILE 语句)中,将 REMOTE_LOGIN_PASSWORD 设置为 NONE,意思 是不用 password 认证方式。 完成以上步骤后,就可以在登录到 NT 后,直接在 SQL*Plus 中 CONNECT/AS SYSDBA 来作为超级用户登录到 Oracle 中,执行一些只有超级用户才能进行的操作。 在 Unix 下,情况有些不同。毕竟这是两个完全不同的操作系统。 首先,在安装 Oracle 之前,建立一个 DBA 组,这一步不用说了,不然是装不上 Ora- cle 的。一般还建立一个名为 Oracle 的用户,并把它加入到 DBA 组中。 第二步,设置 REMOTE_LOGIN_PASSWORD 为 NONE。在 Oracle8.1 以后,该参数默认 为 EXCLUSIVE。一定要记得改过来。 第三步,用该用户名登录 Unix,运行 SQL*Plus 或者 SERVER MANAGER,输入以下命 令:CONNECT / AS SYSDBA 来登录到 Oracle 中。 用户的基本管理(创建、修改口令和删除等)在专题 1 中已经介绍过,在此不再赘述。 3.2 调整数据库从非归档模式到归档模式 数据库默认安装运行在非归档模式,但非归档模式是一种不安全的模式,因为非归档模 式不能产生连续的日志信息。因此,一般情况下需要将数据库从非归档模式调整为归档模式。 1. 使用 SYS 用户登录数据库,在 SQL 提示符下,输入如下代码: CONN SYS/PASSWORD AS SYSDBA; Oracle 数据库案例教程_教师用书 - 180 - 翰子昂系列理论教材 这里“PASSWORD”为 SYS 用户的密码。 2. 查看当前数据库的运行模式,在 SQL 提示符下,输入如下代码: ARCHIVE LOG LIST; 按回车键,系统显示如下信息: 数据库日志模式 非存档模式 自动存档 禁用 存档终点 USE_DB_RECOVERY_FILE_DEST 最早的联机日志序列 3 当前日志序列 5 以上信息显示:系统运行在非归档模式。 3. 下面改变数据库的运行模式为归档模式,首先关闭数据库,在 SQL 提示符下,输入如 下代码: SHUTDOWN IMMEDIATE; 按回车键,系统提示信息如下: 数据库已经关闭。 已经卸载数据库。 Oracle 例程已经关闭。 4. 以 mount 方式启动实例,在 SQL 提示符下,输入如下代码: STARTUP MOUNT; 按回车键,系统提示如下信息: Oracle 例程已经启动。 Total System Global Area 289406976 bytes Fixed Size 1248576 bytes Variable Size 96469696 bytes Database Buffers 184549376 bytes Redo Buffers 7139328 bytes 数据库装载完毕。 5. 修改数据库为归档模式,在 SQL 提示符下,输入如下代码: ALTER DATABASE ARCHIVELOG; 按回车键,系统提示“数据库已更改”。 6. 启动数据库,在 SQL 提示符下,输入如下代码: 专题九 数据库高级管理 翰子昂系列理论教材 - 181 - ALTER DATABASE OPEN; 按回车键,系统提示“数据库已更改”。 7. 再用如下的代码查看数据库运行模式: ARCHIVE LOG LIST; 按回车键,系统提示如下信息: 数据库日志模式 存档模式 自动存档 启用 存档终点 USE_DB_RECOVERY_FILE_DEST 最早的联机日志序列 3 下一个存档日志序列 5 当前日志序列 5 说明数据库日志模式已经是存档模式。 8. 在 SQL 提示符下输入“EXIT”命令退出 SQL*Plus 进入到 DOS 状态,在 DOS 提示符 下输入如下命令,创建归档日志的存放目录。 md d:\backup md d:\bak 在 D 盘根目录下,创建了两个文件夹,分别为 backup 和 bak。 9. 再以 SYS 用户身份登录到数据库,启动自动归档,即当发生日志切换时自动启动归档 进程,其实就是修改初始化参数 log_archive_start 为 true,命令如下: ALTER SYSTEM SET log_archive_start =true SCOPE=spfile; 按回车键,系统提示“系统已更改”。 10. 输入如下的代码查看 log_archive_start 参数: SHOW PARAMETER log_archive_start; 按回车键,系统显示如下信息: NAME TYPE VALUE ------------------------------------ ----------- -------- log_archive_start boolean FALSE 可见,虽然已经更改了参数但设置还未生效。必须重新启动数据库,此更改才能生效。 Oracle 数据库案例教程_教师用书 - 182 - 翰子昂系列理论教材 注意: 该参数存放在二进制参数文件中,修改必须指定 SCOPE=spfile;Oracle 数据库有两类参数文件:二进制参数文件和文本参数文件,从 9i 开始默认使 用二进制参数文件 spfile。 11. 在 SQL 提示符下,输入如下代码,关闭数据库。 SHUTDOWN IMMEDIATE; 12. 输入如下代码重新启动数据库。 STARTUP 按回车键,系统显示如下信息: ORA-32004: obsolete and/or deprecated parameter(s) specified Oracle 例程已经启动。 Total System Global Area 289406976 bytes Fixed Size 1248576 bytes Variable Size 96469696 bytes Database Buffers 184549376 bytes Redo Buffers 7139328 bytes 数据库装载完毕。 数据库已经打开。 注意第一行的错误提示的意思是使用了过期的参数。 13. 再查看参数 log_archive_start 的值,输入如下代码: SHOW PARAMETER log_archive_start; 按回车键,系统显示如下信息: NAME TYPE VALUE ------------------------------------ ----------- -------- log_archive_start boolean TRUE 说明参数已经更改。 注意: Oracle 10G 中已经废弃了此参数,也就是不用修改此参数的值,只要调 整数据库为归档模式,默认就是自动归档。 14. 设置归档进程的个数,为了提高效率,可以启动多个进程完成归档任务。因为在归 专题九 数据库高级管理 翰子昂系列理论教材 - 183 - 档的时候,可能会有多个归档目的,这是启动多个进程,并行操作提高效率。 ALTER SYSTEM SET log_archive_max_processes=3; 按回车键,系统提示“系统已更改”。 15. 设置归档的目的地,就是把连续的日志存放在那里,在 Oracle 中可以同时存放多个 归档的备份,这可以最大限度保证日志的安全性。如下例中,日志文件在两个目录下存储, 每个目录下内容均一样,当一个损坏时,可以用另一个。 ALTER SYSTEM SET log_archive_dest_1="location=d:\bak"; 按回车键,系统提示“系统已更改”。 ALTER SYSTEM SET log_archive_dest_2="location=d:\backup"; 按回车键,系统提示“系统已更改”。 16. 设置归档后日志文件的文件名。 ALTER SYSTEM SET log_archive_format='arch_%T_%S_%R.arc' SCOPE=spfile; %T 表示日志序列号,%S 表示线程号,%R 表示重做日志号。 同上面的 log_archive_start 参数一样,此参数也需要重新启动生效。 17. 至此,将数据库从非归档模式调整为归档模式已全部完成。 18. 在 SQL 提示符下,输入以下代码,强制产生归档日志: ALTER SYSTEN SWITCH LOGFILE; 系统提示“系统已更改”。每次日志切换都会产生归档日志。 19. 运行两次后的 backup 和 bak 文件夹内的内容如下图: 图 9-3 生成归档日志后 backup 文件夹的内容 Oracle 数据库案例教程_教师用书 - 184 - 翰子昂系列理论教材 图 9-4 生成归档日志后 bak 文件夹的内容 理论知识: Oracle 数据库默认安装完成后,数据运行在非归档模式。根据 Oracle 数据库日志文 件的工作原理,日志组之间是循环使用,会覆盖以前的日志信息,这将不能保证日志是连续 的。如果日志不连续,那么就不能最大限度的恢复数据库。所以在生产数据库中,数据库都 是运行在归档模式下。 ◆ 非归档日志方式可以避免实例故障,但无法避免介质故障。在此方式下,数据库只 能实施冷备份; ◆ 归档日志方式产生归档日志,用户可以使用归档日志完全恢复数据库。 因此,一般情况下都需要将数据库从非归档模式调整为归档模式。 3.3 数据导入导出 有时候需要将数据库从一个地方带到另一个地方,这就需要用到数据导入导出实用工具。 此工具不需要数据库运行在归挡模式下,不但备份简单,而且可以不需要外部存储设备。 1. 导出 SCOTT 模式下所有的数据库对象,在 DOS 提示符下输入以下代码: exp soctt/tiger file=c:\scott.dmp owner=scott 在这里 exp 是一个 DOS 命令,用于从数据库中导出数据; soctt/tiger:指定登录的用户名和密码; file:制定导出文件存放的位置和文件名,一般扩展名为 dmp; owner:表示以用户的方式导出数据。 按回车键,系统显示如下图所示: 专题九 数据库高级管理 翰子昂系列理论教材 - 185 - 图 9-5 以用户方式导出数据 2. 以 SCOTT 用户登录数据库,将 dept 和 emp 表 DROP 掉,输入如下代码: DROP TABLE emp; DROP TABLE dept; 系统显示“表已删除”。 如何恢复表的数据呢? 3. 因为在第一步已经备份了 SCOTT 模式的所有对象,所以完全可以恢复。在 DOS 提示 符下输入如下语句: imp scott/tiger file=c:\scott.dmp full=y ignore=y Oracle 数据库案例教程_教师用书 - 186 - 翰子昂系列理论教材 imp 是导入命令; scott/tiger:指定登录的用户名和密码; file:从那个文件获取数据,应是通过 EXP 命令导出的文件; full:导入文件中的所有内容; ignore:忽略导入过程中出现的错误。 按回车键,系统显示如下图所示: 图 9-6 以用户方式导入数据 4. 再以 SCOTT 用户登录数据库,查看 dept 和 emp 表是否恢复,输入如下代码: SELECT * FROM TAB; 按回车键,系统显示如下信息: TNAME TABTYPE CLUSTERID ------------------------------ ------- ---------- BONUS TABLE SALGRADE TABLE BIN$y2dVnK4IQmmjUX9rcfekNw==$0 TABLE BIN$VZgedEFwQzih3Zz4XpJR/g==$0 TABLE DEPT TABLE EMP TABLE 6 rows selected. 通过上面信息显示 dept 和 emp 表已经恢复。 专题九 数据库高级管理 翰子昂系列理论教材 - 187 - 5. 将 SCOTT 模式下的 dept 和 emp 表导入到 student 模式下。 首先以表的方式将 SCOTT 模式下的 dept 和 emp 表导出,在 DOS 提示符下,输入如下命 令: exp scott/tiger file=c:\table.dmp tables=(dept,emp) 按回车键,系统显示信息如下图: 图 9-7 以表方式导入数据 6. 将导出的数据导入到 student 用户模式下,在 DOS 提示符下,输入如下命令: imp system/abcdef file=c:\table.dmp fromuser=SCOTT touser=student tables=(dept,emp) 注意:将数据从一个用户导入另一个用户,一般由数据库管理员来完成,普通用户不能 完成此操作。 按回车键,系统显示信息如图 9-8 所示。 Oracle 数据库案例教程_教师用书 - 188 - 翰子昂系列理论教材 图 9-8 将数据导入其他模式下 7. 使用 student 用户登录系统,在 SQL 提示符下,输入查询语句: SELECT * FROM dept; 按回车键,系统显示如下信息: SQL> SELECT * FROM dept; DEPTNO DNAME LOC ---------- -------------- ------------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON 通过以上信息,说明数据已经导入成功。 理论知识: 当我们使用一个数据库时,总希望数据库的内容是可靠的、正确的,但由于计算机系统 的故障(硬件故障、软件故障、网络故障、进程故障和系统故障)影响数据库系统的操作, 影响数据库中数据的正确性,甚至破坏数据库,使数据库中全部或部分数据丢失。因此当发 生上述故障后,希望能重构这个完整的数据库,该处理称为数据库恢复。数据的恢复需要借 助以前备份的数据(起点)和连续的归档日志。这样就可以先把数据恢复到备份点,然后重 做连续的归档日志,将数据库恢复到出现故障前最新的数据库正确状态。因此,恢复过程大 致可以分为复原(Restore)与恢复(Restore)过程。 复原:就是给数据库一个起点; 专题九 数据库高级管理 翰子昂系列理论教材 - 189 - 恢复:重做日志,从而恢复数据库。 数据库的备份分为逻辑备份和物理备份:逻辑备份就是根据数据的逻辑结构有选择的备 份相关的数据库逻辑对象,比如模式、表、表空间等; 物理备份是根据数据库物理结构备份相关的操作系统文件,包括数据文件、日志文件和 控制文件。 逻辑备份和恢复 现在先来介绍一下逻辑备份方式的方法,利用 Export 可将数据从数据库中提取出来, 利用 Import 则可将提取出来的数据送回到 Oracle 数据库中去。他们都是以 DOS 命令的方 式提供给用户,一般情况可简写为 exp、imp。Oracle 提供的 Export 和 Import 具有四种 不同的操作方式(就是备份的数据输出(入)类型): 1. 表方式(T):可以将指定的表导出备份; 2. 全库方式(E):将数据库中的所有对象导出; 3. 用户方式(U):可以将指定的用户相应的所有数据对象导出; 4. 表空间方式:可以导出没一个表空间。 利用 Export 工具可以在数据库打开状态下备份数据库。Export 把数据库中的对象导 出到一个二进制的文件中。Export 也是数据库间进行迁移的一个常用工具。导出实用工具 具有如下命令行参数,见表 9-1。 参数名 参数描述 USERID 用户名/口令,用于登录到数据库中 FULL 是否将整个数据导出如:FULL=Y,只有具有 EXP_FULL_DATABASE 权限的用户才能导出整个数据库 BUFFER 数据缓冲区的大小 OWNER 以用户方式导出时,用户列表 FILE 输出文件(EXPDAT.DMP),默认存放在 C:\Documents and Settings\(用 户名)文件夹下 TABLES 以表方式导出时表的列表,如果是不是导出登录模式下的表,需要制 定模式如(SCOTT.EMP) INCTYPE 增量导出类型 ROWS 是否导出数据行(Y) PARFILE 参数文件名 LOG 屏幕输出的日志文件 表 9-1 导出使用工具参数说明 使用导出实用程序 Export 导出的数据,可以使用导入实用程序 Import 将其导入数据 库。导入实用程序有选择地从导出转储文件中导入对象和用户。同样,使用导入实用程序导 入数据时,也有一系列的参数,如表 9-2 所示。 参数名 参数描述 USERID 用户名/口令,用于登录到数据库中 Oracle 数据库案例教程_教师用书 - 190 - 翰子昂系列理论教材 参数名 参数描述 FULL 导入整个文件,只有具有 IMP_FULL_DATABASE 权限的用户才能导 入整个数据库 BUFFER 数据缓冲区大小 FROMUSER 所有人用户名列表 FILE 输入文件(EXPDAT.DMP) TOUSER 用户名列表 TABLES 表名列表 IGNORE 忽略创建错误(N) INCTYPE 增量导入类型 PARFILE 参数文件名 LOG 屏幕输出的日志文件 表 9-2 导入使用工具参数说明 在导入导出备份方式中,提供了很强大的一种方法,就是增量导出/导入,但是它必须作 为 System 来完成增量的导入导出,而且只能是对整个数据库进行实施。增量导出又可以分 为三种类别: 1. 完全增量导出(Complete Export):这种方式将把整个数据库文件导出备份: exp system/manager inctype=complete file=20041125.dmp 为了方便检索和事后的查询,通常我们将备份文件以日期或者其他有明确含义的字符命 名。 2. 增量型增量导出(Incremental Export):这种方式将只会备份上一次备份后改变 的结果: exp system/manager inctype=incremental file=20041125.dmp 3. 累积型增量导出(Cumulate Export):这种方式是导出自上次完全增量导出后数 据库变化的信息: exp system/manager inctype=cumulative file=20041125.dmp 通常情况下,DBA 们所要做的,就是按照企业指定或者是自己习惯的标准(如果是自己指 定的标准,建议写好计划说明),一般,我们采用普遍认可的下面的方式进行每天的增量备份: Mon:完全备份(A) Tue:增量导出(B) Wed:增量导出(C) Thu:增量导出(D) Fri:累计导出(E) Sat:增量导出(F) Sun:增量导出(G) 专题九 数据库高级管理 翰子昂系列理论教材 - 191 - 这样,我们可以保证每周数据的完整性,以及恢复时的快捷和最大限度的数据损失。恢 复的时候,假设事故发生在周末,DBA 可按这样的步骤来恢复数据库: 第一步:用命令 CREATE DATABASE 重新生成数据库结构; 第二步:创建一个足够大的附加回滚。 第三步:完全增量导入 A: imp system/manager inctype=RESTORE FULL=y FILE=A 第四步:累计增量导入 E: imp system/manager inctype=RESTORE FULL=Y FILE=E 第五步:最近增量导入 F: imp system/manager inctype=RESTORE FULL=Y FILE=F 通常情况下,DBA 所要做的导入导出备份就算完成,只要科学地按照规律作出备份,就 可以将数据的损失降低到最小,提供更可靠的服务。另外,DBA 最好对每次的备份做一个比 较详细的说明文档,使得数据库的恢复更加可靠。 当数据库可以暂时处于关闭状态时,我们需要将它在这一稳定时刻的数据相关文件转移 到安全的区域,当数据库遭到破坏,再从安全区域将备份的数据库相关文件拷贝回原来的位 置,这样,就完成了一次快捷安全等数据转移。由于是在数据库不提供服务的关闭状态,所 以称为冷备份。冷备份具有很多优良特性,比如上面图中我们提到的,快速,方便,以及高 效。一次完整的冷备份步骤应该是: 1. 首先关闭数据库。 SHUTDOWN IMMEDIATE; 2. 拷贝相关文件到安全区域( 利用操作系统命令拷贝数据库的所有的数据文件、日志文 件、控制文件、参数文件、口令文件等(包括路径)) 3. 重新启动数据库 STARTUP; 以上的步骤我们可以用一个脚本来完成操作: SQLPLUS /NOLOG CONNECT sys/password AS SYSDBA; SHUTDOWN IMMEDIATE; !copy 文件 备份位置(所有的日志、数据、控制及参数文件); STARTUP; EXIT; 这样,就完成了一次冷备份,请确定你对这些相应的目录(包括写入的目标文件夹)有 相应的权限。 Oracle 数据库案例教程_教师用书 - 192 - 翰子昂系列理论教材 恢复的时候,相对比较简单了,停掉数据库,将文件拷贝回相应位置,重启数据库就可 以了,当然也可以用脚本来完成。 冷备份只能将数据恢复到备份点,而在备份点和出现故障之间的信息没有了。这对生产 数据库而言是不可想象的。 要保证最大限度的恢复数据库,保证最小量数据的损失,需要使用热备份。 热备份(联机备份)及恢复 当需要做一个精度比较高的备份,而且数据库不可能停掉(少许访问量)时,这个情况 下,就需要在归档方式下的备份,就是下面讨论的热备份。 热备份可以非常精确的备份表空间级和用户级的数据,由于它是根据归档日志的时间轴 来备份恢复的,理论上可以恢复到前一个操作,甚至就是前一秒的操作。具体步骤如下: 1. 将数据库从非归档模式调整为归档模式。 如 3.3 步骤所示,这里不再重复,只对在上面步骤中所用到的参数进行一下说明: LOG_ARCHIVE_START:是否启用自动归档,Oracle 10g 中已经废弃了此参数,也就 是不用修改此参数的值,只要调整数据库为归档模式,默认就是自动归档。 LOG_ARCHIVE_DEST_n:指定的归档日志文件的路径,建议与 Oracle 数据库文件存 在不同的硬盘,一方面减少磁盘 I/O 竞争,另外一方面也可以避免数据库文件所在硬盘毁坏 之后的文件丢失。归档路径也可以直接指定为磁带等其它物理存储设备,但可能要考虑读写 速度、可写条件和性能等因素。 LOG_ARCHIVE_MAX_PROCESSES:指定可以启用的最大归档进程数,可以并行执行, 提高效率。 LOG_ARCHIVE_FORMAT:指定归档日志文件的文件名,%T 日志序列号,%S 线程号,% R 重做日志号。要求在文件的命名中必须包含以上三项。比如“%T%S%R.ARC”。 2. 备份表空间文件: 首先,修改表空间文件为备份模式: ALTER TABLESPACE tablespace_name BEGIN BACKUP; 然后,拷贝表空间文件到安全区域: !COPY tablespace_name D_PATH; 最后,将表空间的备份模式关闭: ALTER TABLESPACE tablespace_name END BACKUP; 3. 备份控制文件: ALTER DATABASE BACKUP controlfile TO 'controlfile_back_name' REUSE; 热备份的恢复,对于归档方式数据库的恢复要求不但有有效的日志备份还要求有一个在 归档方式下作的有效的全库备份。归档备份在理论上可以无数据丢失,但是对于硬件以及操 专题九 数据库高级管理 翰子昂系列理论教材 - 193 - 作人员的要求都比较高。在我们使用归档方式备份的时候,全库物理备份也是非常重要的。 归档方式下数据库的恢复要求从全备份到失败点所有的日志都要完好无缺。 4. 恢复某个备份的表空间: 首先,将数据库启动到 mount 状态 : STARTUP MOUNT; 然后,将表空间脱机: ALTER TABLESPACE tablespace_name OFFLIE FOR RECOVER; 然后,打开数据库: ALTER DATABASE OPEN; 然后,通过操作系统命令将备份的数据文件拷贝到相应目录(RESTORE)。恢复表空间 (RECOVER): RECOVER TABLESPACE tablespace_name; 最后,将表空间联机: ALTER TABLESPACE tablespace_name ONLINE; 这样就利用归档日志,将表空间恢复到了出现故障前的最后数据库一致状态。 4. 实验 按照第 3 部分相关实践知识依次练习,主要掌握: 1. 创建用户和表空间,参见 3.1(15 分钟)。 2. 创建角色并使用角色,参见 3.2(10 分钟)。 3. 修改数据库用户认证方式,参见 3.3(10 分钟)。 4. 调整数据库从非归档模式到归档模式,参见 3.4(20 分钟)。 5. 使用导入导出实用工具,参见 3.5(35 分钟)。 5. 课后作业 使用具有 DBA 权限的用户使用导出工具,导出整个数据库。然后将数据库卸载(使用 DBCA 工具),然后通过导入工具将数据导入。
还剩196页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

xxb514770229

贡献于2014-03-23

下载需要 8 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf