本讲稿(ORACLE8i 数据库基础)是作者在多年的工作和授课中的总结,主要包括两个部分,第一部分是ORACLE SQL*PLUS基础,主要讲述ORACLE 应用系统设计的基本知识和给出一些有用的实例;第二部分是介绍ORACLE PL/SQL知识,主要讲述ORACLE数据库PL/SQL程序设计所用到基本知识,这部分给出进行应用设计所必需的基础知识。这两部分的内容都尽可能做到内容简洁而全面。特点是,1.用简单明了的语句对解释各个部分的内容,省去一些理论性的解释;2.给出作者在应用设计和开发中的一些具体的例子,为软件人员提供一些借鉴,省去查阅大量资料的时间。3.给出了许多资料所没有提供的一些使用技巧,如导出触发器等。总之,内容基本上包括当前Oracle8I的最新内容。同时也覆盖了最新的ORACLE8iOCP培训大纲的内容。不仅包含ORACLE 程序设计人员、DBA所必须掌握的知识,而且还含盖了系统分析员所要求的内容。与本书(讲稿)相伴的还有《Oracle8i/9i初级数据库管理》和《Oracle8i/9i高级数据库管理》。
全书内容简练实用,可作为ORACLE 数据库管理人员参考,也可作为应用开发人员和系统分析与设计人员以及大学计算机专业教学的参考资料。由于作者水平所限,加之ORACLE的产品与内容的浩瀚,在资料的整理与收集中可能有不少错误和不妥之处,希望读者给予批评制正。
目 录
第一部分 Oracle SQL*PLUS基础... 23
第一章 Oracle数据库基础... 23
§1.1 理解关系数据库系统(RDBMS)... 23
§1.1.1 关系模型... 23
§1.1.2 Codd十二法则... 24
§1.2 关系数据库系统(RDBMS)的组成... 24
§1.2.1 RDBMS 内核... 24
§1.2.2 数据字典概念... 25
§1.3 SQL、SQL*Plus及 PL/SQL. 25
§1.3.1 SQL和SQL*PLUS的差别... 25
§1.3.2 PL/SQL语言... 27
§1.4 登录到SQL*PLUS. 27
§1.4.1 UNIX环境... 27
§1.4.2 Windows NT和WINDOWS/2000环境... 29
§1.5 常用SQL*PLUS 附加命令简介... 32
§1.5.1 登录到SQL*PLUS. 32
§1.5.2 EXIT和QUIT. 32
§1.5.4 LIST(列出)命令... 33
§1.5.5 Change(替换字符串)命令... 34
§1.5.6 Append(追加字符串)命令... 34
§1.5.7 Save保存当前缓冲区命令到文件... 35
§1.5.8 GET将命令文件读到缓冲区... 35
§1.5.9 SPOOL将信息记录到文件中... 36
§1.5.10 再运行当前缓冲区的命令... 36
§1.6 常用数据字典简介... 37
§1.7 ORACLE数据类型... 38
§1.8 SQL 语句基础... 44
§1.8.1 SQL语句所用符号... 44
§1.8.2 简单select 查询... 45
§1.9 伪列及伪表... 46
§1.10 使用SQL Worksheet工作... 46
第二章 查询基础... 50
§2.1 SELECT语句... 50
§2.2 SQL中的单记录函数... 50
§2.2.1 单记录字符函数... 50
§2.2.2 单记录数字函数... 56
§2.2.3 单记录日期函数... 62
§2.2.4 单记录转换函数... 65
§2.2.5 其它的单记录函数... 68
§2.3 SQL中的组函数... 73
§2.3.1 多记录组函数... 73
§2.3.2 带 GROUP BY 的计算... 75
§2.3.3 用 HAVING 来限制分组的计算... 75
§2.4 控制和格式化输出... 76
§2.4.1 用 ORDER BY 子句来对结果进行排序... 76
§2.4.2 用 BREAK 命令 来对结果进行排列... 76
§2.4.3 用 COMPUTE 命令对结果进行格式化... 79
§2.5 配置会话环境... 82
§2.5.1 ARRAYSIZE(取回的行数)... 82
§2.5.8 Termout (启/停屏幕显示)84
§2.5.9 ECHO (启/停命令显示)84
§2.6 格式化输出... 87
§2.6.1 一般数据的格式化输出... 87
§2.6.2 日期的格式化输出... 88
§2.7 加标题... 89
§2.8 建立简单报告... 90
§2.9 输入变量... 91
第三章 表及索引的定义操作... 94
§3.1 建立表结构... 94
§3.1.1 建立表结构命令... 94
§3.1.2 建立表结构例子... 96
§3.1.3 建立临时表结构... 97
§3.3 修改表结构... 98
§3.3.1 修改表结构命令... 98
§3.3.2 修改表结构例子... 99
§3.3.3 删除表结构... 101
§3.3.4 使用CHECK作限制约束... 101
§3.3.5 使用UNRECOVERABLE创建表... 102
§3.3.6 将表移动到新的数据段或新的表空间... 102
§3.3.7 手工分配表的存储空间... 104
§3.3.8 标记不使用的列和删除不使用的列... 104
§3.3 主键... 106
§3.3.1 创建主键... 106
§3.3.2 改变主键... 109
§3.3.3 删除主键... 109
§3.4 外部键... 110
§3.4.1 建立外部键... 110
§3.4.2 修改外部键... 112
§3.4.3 删除外部键... 112
§3.5 索引... 112
§3.5.1 建立索引... 112
§3.5.2 修改索引... 114
§3.5.3 删除索引... 115
§3.6 新索引类型... 115
§3.6.1 基于函数的索引... 115
§3.6.2 反向键索引... 117
§3.6.3 索引组织表... 117
§3.7 抽象数据类型的使用... 118
§3.8 大数据类型的使用... 119
§3.8.1 可用数据类型... 119
§3.8.2 为LOB数据类型指定存储... 120
§3.8.3 操作和检索LOB数据... 121
§3.9 表和索引有关的数据字典... 124
§3.9.1 表和索引数据字典... 124
§3.9.2 数据字典查询例子... 125
第四章 视图、同义词和序列... 128
§4.1 视图... 128
§4.1.1 使用视图来修改表中数据... 128
§4.1.2 创建一个新视图... 128
§4.1.3 删除一个视图... 130
§4.1.4 改变视图... 131
§4.2 实体视图(MATERIALIZED VIEW)131
§4.2.1 创建实体视图... 131
§4.2.2 创建实体视图日志... 137
§4.2.3 修改实体视图... 139
§4.2.4 修改实体视图日志... 141
§4.2.45 实体视图完整例子... 142
§4.3 序号(sequence)146
§4.3.1 建立序号... 146
§4.3.2 修改序号... 147
§4.3.3 使用序号... 147
§4.3.4 删除序号... 147
§4.4 同义词... 148
§4.4.1 建立同义词... 148
§4.4.2 删除同义词... 149
§4.5 视图、同义词和序列有关的数据字典... 149
第五章 簇与分区... 150
§5.1 簇( cluster )150
§5.1.0 簇概念... 150
§5.1.1 建立簇... 152
§5.1.2 改变簇... 155
§5.1.3 删除簇... 155
§5.1.4 删除簇索引... 156
§5.1.5 收集簇信息... 156
§5.2 分区... 158
§5.2.1 分区的优点... 158
§5.2.2 分区的方法... 159
§5.2.3 创建表的分区... 159
§5.2.3 创建索引的分区... 164
§5.2.4 维护表分区和索引分区... 167
§5.3 簇与分区有关的数据字典... 169
§5.3.1 分区、簇数据字典列表... 169
§5.3.2 基本的分区、簇信息查询... 169
第六章 使用SQL 进行数据操作... 170
§6.1 INSERT操作... 170
§6.1.1 用文字插入操作... 171
§6.1.2 用子查询插入操作... 171
§6.2 UPDATE操作... 172
§6.2.1 用文字更新操作... 173
§6.2.2 用查询更新操作... 173
§6.2.3 用相关子查询更新操作... 174
§6.3 DETELE操作... 174
§6.3.1 用delete删除全部记录的操作... 174
§6.3.2 用delete有条件删除部分记录... 175
§6.3.3 用delete分段删除大量记录... 175
§6.4 insert、delete及update 的提交和撤消... 176
§6.4.1 自动提交的设置... 176
§6.4.2 保留点和撤消... 177
第七章 复杂查询语句的使用... 180
§7.1 复杂查询语句的使用... 180
§7.1.1 相关子查询... 180
§7.1.2 外连接... 180
§7.1.3 自我连接... 181
§7.1.4 UNION,INTERSECT及MINUS. 182
§7.2 创建复杂的视图... 183
§7.2.1 分组视图... 183
§7.2.2 合计视图... 183
§7.2.3 组合视图... 184
§7.3 家族树... 184
§7.3.1 排除单一体和分枝... 184
§7.3.2 遍历至根... 185
§7.4 在from 中使用视图... 187
第八章 一些高级的用法... 188
§8.1 关于DECODE. 188
§8.1.1 DECODE 中的if-then-else逻辑... 188
§8.1.2 DECODE 的简单例子... 188
§8.1.3 DECODE实现表的转置... 189
§8.2 关于访问远程数据库... 192
§8.2.1 数据库链接... 192
§8.2.2 使用同义词获得本地透明... 193
§8.2.3 在视图中使用user伪列... 194
§8.2.4 使用COPY功能... 195
§8.2.5 管理Oracle名称服务器... 196
§8.3 关于上下文的使用... 196
§8.3.1 设置上下文选项... 196
§8.3.2 为上下文查询设置表... 197
§8.3.3 优化文本索引... 199
§8.4 关于维数(DIMENSION)... 199
§8.4.1 CREATE DIMENSION语法... 200
§8.4.2 创建维的例子... 201
第九章 安全管理... 203
§9.1 CREATE USER 命令... 203
§9.2 建立用户... 204
§9.2.1 外部验证(Authenticated )用户... 204
§9.2.2 全局(Globally)验证用户-企业验证... 204
§9.3 ALTER USER 命令... 205
§9.4 DROP USER 命令... 205
§9.5 GRANT 命令与REVOKE 命令... 206
§9.5.1 GRANT 命令... 206
§9.5.2 REVOKE 命令... 206
§9.6 权限和角色... 207
§9.6.1 建立角色... 207
§9.6.2 给角色授权... 208
§9.6.3 授权角色给用户... 209
§9.7 有关的数据字典... 209
§9.7.1 与用户、角色与权限有关的数据字典... 209
§9.7.2 查询数据字典信息... 209
第十章 其它一些常见问题及技巧... 212
§10.1 一些常见问题... 212
§10.1.1 Oracle与2000年问题... 212
§10.1.2 如何正确插入日期数据... 213
§10.1.3 在查询中只返回满足条件的部分记录... 214
§10.1.4 快速大量删除数据Truncate. 215
§10.1.5 Rowid的使用... 215
§10.1.6 在查询中不让记录被更新... 217
§10.1.7 EXCEPTIONS(违反完整性)问题... 217
§10.1.8 Not in和Not Exists218
§10.1.9 关于 COPY命令... 218
§10.1.10 列值为NULL情形的处理... 219
§10.1.11 使用 product_user_file来限制用户使用产品... 220
§10.2 常用技巧... 221
§10.2.1 long 类型的查询... 222
§10.2.2 如何确定执行时间... 222
§10.2.3 如何终止用户会话... 222
§10.2.4 用TRANSLATE对数据加密和解密... 224
§10.2.5 如何用查询来修改数据... 225
§10.2.6 如何产生创建用户的脚本... 226
§10.2.7 如何产生创建表结构的脚本... 227
§10.2.8 如何产生创建视图的脚本... 229
§10.2.9 如何产生创建序号的脚本... 229
§10.2.10 如何为用户创建公共同义词... 229
第二部分 Oracle PL/SQL基础... 231
第十一章 PL/SQL 程序设计简介... 231
§11.1 概述... 231
§11.2 SQL与PL/SQL. 231
§11.2.1 什么是PL/SQL?. 231
§11.2.1 PL/SQL的好处... 232
§11.2.1.1 有利于客户/服务器环境应用的运行... 232
§11.2.1.2 适合于客户环境... 232
§11.2.1.3 客户及服务器端的好处... 232
§11.2.2 PL/SQL 可用的SQL语句... 233
§11.3 运行PL/SQL程序... 233
§11.4 PL/SQL内置包... 234
第十二章PL/SQL 块结构和组成元素... 235
§12.1 PL/SQL结构... 235
§12.2 PL/SQL块... 236
§12.3 标识符... 236
§12.4 PL/SQL 变量类型... 237
§12.4.1 变量类型... 237
§12.4.2 复合类型(记录和表)... 238
§12.4.3 使用%ROWTYPE. 240
§12.4.4 LOB类型... 240
§12.4.5 用户定义的子类型... 241
§12.4.6 数据类型的转换... 243
§12.5 运算符和表达式(数据定义)243
§12.5.1 关系运算符... 243
§12.5.2 一般运算符... 244
§12.5.3 逻辑运算符... 244
§12.6 变量赋值... 245
§12.6.1 字符及数字运算特点... 245
§12.6.2 Boolean 赋值... 245
§12.6.3 数据库赋值... 245
§12.6.4 可转换的类型赋值... 246
§12.7 变量作用范围以可见性... 248
§12.8 注释... 248
§12.9 简单例子... 249
§12.9.1 简单数据插入例子... 249
§12.9.2 简单数据删除例子... 249
第十三章PL/SQL 处理流程... 250
§13.1 条件语句... 250
§13.2 循环... 251
§13.3 标号和GOTO.. 254
§13.4 NULL 语句... 255
第十四章 光标的使用... 255
§14.1 光标概念... 255
§14.1.1 处理显式光标... 255
§14.1.2 处理隐式光标... 257
§14.2 光标循环... 258
§14.2.1 简单循环... 258
§14.2.2 WHILE循环... 259
§14.2.3 光标 FOR 循环... 260
§14.2.4 关于NO_DATA_FOUND和%NOTFOUND.. 260
§14.2.5SELECT FOR UPDATE 光标... 261
§14.3 光标变量... 262
§14.3.1 声明光标变量... 262
§14.3.2 为光标变量分配存储空间... 262
§14.3.3 打开光标变量... 262
§14.3.4 关闭光标变量... 262
§14.3.5 光标变量例子... 263
§14.3.6 光标变量... 265
第十五章 错误处理... 267
§15.1 异常处理概念... 267
§15.1.1 预定义的异常处理... 267
§15.1.2 触发异常情态... 268
§15.1.3 处理异常情态... 269
§15.1.4 用户定义的异常处理... 270
§15.2 异常情态传播... 271
§15.2.1 在执行部分引发异常情态... 271
§15.2.2 在声明部分引发异常情态... 272
§15.3 异常处理编程... 273
§15.4 在 PL/SQL 中使用 sqlcode,sqlerrm.. 273
第十六章 存储过程和函数... 276
§16.1 引言... 276
§16.2 存储过程... 276
§16.2.1 创建过程... 276
§16.2.2 使用过程... 278
§16.2.3 开发存储过程步骤... 279
§16.2.3.1 编辑存储过程源码... 279
§16.2.3.2 对存储过程程序进行解释... 279
§16.2.3.3 调试源码直到正确... 279
§16.2.3.4 授权执行权给相关的用户或角色... 279
§16.2.4 与存储过程相关数据字典... 280
§16.3 创建函数... 281
§16.4 过程和函数中的例外处理... 282
§16.4.1 使用系统定义的例外处理... 282
§16.4.1.1 没有例外处理的缺点... 283
§16.4.1.2 使用预定义的例外处理... 283
§16.4.2 使用用户定义的例外处理+. 286
§16.4.2.1 定义的用户例外处理... 286
§16.4.2.2 使用户EXCEPTION_INIT处理... 286
§16.4.2.3 使用户raise_application_error处理... 286
第十七章 创建包和使用包... 287
§17.1 引言... 287
§17.2 包的定义... 288
§17.3 包的开发步骤... 289
§17.4 包的头部说明... 289
§17.5 包体的说明... 290
§17.6 删除过程、函数和包... 293
§17.7 包的管理... 293
§17.7.1 包有关的数据字典... 293
§17.7.2 包中无效对象的查询和编译... 294
§17.7.3 包源代码的导出... 296
第十八章 触发器... 297
§18.1 触发器类型... 297
§18.1.1 DML触发器... 297
§18.1.2 替代触发器... 298
§18.1.3 系统触发器... 298
§18.2 创建触发器... 298
§18.2.1 创建DML触发器... 299
§18.2.2 创建替代(Instead_of)触发器... 300
§18.2.3 创建系统触发器... 300
§18.2.4 触发器触发次序... 302
§18.2.5 使用触发器谓词... 302
§18.3 删除和使能触发器... 303
§18.4 创建触发器的限制... 304
§18.5 变异表... 305
§18.5.1 变异表典型例子... 307
§18.5.2 变异表错误的处理... 308
§18.6 触发器数据字典与管理... 309
§18.6.1 触发器数据字典... 309
§18.6.2 无效触发器的编译... 310
第十九章 外部存储过程... 311
§19.1 什么是外部例程... 311
§19.2 C外部例程... 311
§19.2.1 调用步骤... 312
§19.2.2 参数映射( 转换)317
第二十章 会话间通信... 319
§20.1.1 发送消息... 319
§20.1.2 接收消息... 320
§20.1.3 示例... 321
§20.2.1 使用警告... 341
§20.2.2 警告所用的各个过程... 341
§20.2.3 警告例子... 342
§20.3 DBMS_PIPE 和 DBMS_ALERT. 343
第二十一章 数据库作业和文件I/O.. 344
§21.1 数据库作业... 344
§21.1.1后台进程... 344
§21.1.2运行作业... 344
§21.1.3失效作业... 346
§21.1.4删除作业... 347
§21.1.5修改作业参数... 348
§21.1.6与作业参数有关数据字典... 350
§21.2 文件I/O.. 350
§21.2.1 安全性... 351
§21.2.2 打开和关闭文件... 351
§21.2.3文件输出... 353
§21.2.3文件输入... 354
§21.2.4文件操作例子... 354
第二十二章 在PL/SQL 使用SQL语句... 355
§22.1在PL/SQL 使用DML语句... 355
§22.2 伪列... 357
§22.3 GRANT、REVOKE 和权限... 358
§22.3.1 对象和系统权限... 358
§22.3.2 GRANT和REVOKE命令语法... 358
§22.4 事务控制... 359
§22.4.1 COMMIT 和 ROLLBACK.. 359
§22.4.2 保留点 SAVEPOINT. 360
§22.5 在PL/SQL中使用SQL函数... 361
§22.5.1 错误处理函数... 362
§22.5.2 数字函数... 362
第二十三章 PL/SQL程序的测试和调试... 363
§23.1 问题诊断... 363
§23.1.1 调试指南... 363
§23.1.2 显示在编译产生的错误... 363
§23.2 插入测试表... 364
§23.3 DBMS_OUTPUT的使用... 364
§23.4 PL/SQL调试器... 365
§23.4.1 ProcedureBuilder调试器... 365
§23.4.2SQL-Station调试器... 366
§23.5 程序设计方法... 366
§23.5.1 模块化程序设计... 366
§23.5.2 自顶向下设计... 366
§23.5.3 数据抽象... 366
第二十四章 性能及其它问题... 367
§24.1共享池... 367
§24.1.1 共享池工作原理... 367
§24.1.2 估计共享池大小... 368
§24.1.3 将PL/SQL 驻留在共享池中... 369
§24.2 SQL 语句调整... 370
§24.3 网络问题... 373
§24.4.1 运行 wrap实用程序... 373
§24.4.2 输入和输出文件... 374
§24.5 DBMS_OUTPUT的使用... 374
第二十五章 对象类型... 376
§25.1 对象类型定义... 376
§25.1.1 定义对象类型头... 376
§25.1.2 定义对象类型体... 376
§25.1.3 定义对象类型例子... 377
§25.1.4 初始化对象类型... 378
§25.1.5 使用对象类型... 379
§25.2 对象类型修改... 381
§25.3 对象类型删除... 381
第二十六章 动态PL/SQL简介... 383
§26.1 概述... 383
§26.1.1 静态SQL和动态SQL. 383
§26.1.2 用DBMS_SQL 包实现动态... 383
§26.1.3 用本地动态SQL实现动态... 385
§26.2 使用DBMS_SQL进行动态编程... 385
§26.2.1 执行DML、DDL及Alter session语句... 385
§26.2.2 示例... 388
§26.2.3 执行DDL 语句... 389
§26.2.4 执行PL/SQL 块... 390
§26.4 本地动态SQL. 391
§26.4.1使用EXECUTE IMMEDIATE语句... 391
§26.4.2向后兼容情况... 392
§26.4.3指定参数模式... 393
第二十七章 LOB和DBMS_LOB包简介... 394
§27.1 LOB类型一般使用... 394
§27.1.1 LOB类型存储... 395
§27.1.2 临时LOB类型... 395
§27.1.3 LOB类型的接口... 396
§27.2 一般LOB表与数据加载... 396
§27.2.1 建立包含LOB的表... 396
§27.2.2 用EMPTY_CLOB或EMPTY_BLOB插入LOB. 398
§27.2.3 一般的LOB插入... 399
§27.3 内部LOB和DBMS_LOB的使用... 399
§27.3.1 APPEND过程... 400
§27.3.2 CLOSE过程... 401
§27.3.3 COMPARE函数... 401
§27.3.4 COPY过程... 403
§27.3.5 ERASE过程... 405
§27.3.6 GETCHUNKSIZE函数... 406
§27.3.7 GETLENGTH函数... 407
§27.3.8 INSTR函数... 408
§27.3.9 READ过程... 409
§27.3.10 SUBSTR函数... 410
§27.3.11 WRITE过程... 411
§27.3.12 WRITEAPPEND过程... 412
§27.4临时LOB. 413
§27.4.1 建立临时LOB. 413
§27.4.2 查看临时LOB. 414
§27.4.3 释放临时LOB. 414
§27.4.4 从BFILE中加载临时LOB. 415
§27.4.5 查看临时LOB是否打开... 415
§27.4.6 显示临时LOB数据... 416
§27.4.7 从临时LOB读数据... 417
§27.4.8 从临时LOB读部分数据... 418
§27.4.9 比较两个临时LOB数据... 419
§27.4.10 查看临时LOB模式的存在... 420
§27.4.11 得到临时LOB的长度... 421
§27.4.12 拷贝部分临时LOB数据... 421
§27.4.13 为临时LOB拷贝位置... 422
§27.4.14 加临时LOB到另外的LOB. 423
§27.4.15 写追加到临时LOB. 424
§27.4.16 写数据到临时LOB. 424
§27.4.17 修理临时LOB数据... 425
§27.4.18 删除临时LOB数据... 426
§27.5外部LOB (BFILE)426
§27.5.1 BFILE目录指定... 426
§27.5.2 建立包括BFILE列的表... 427
§27.5.3 用BFILENAME()插入数据... 428
§27.5.4 从另外表选择BFILE插入数据... 429
§27.5.5 用初始化BFILE位置来插入数据BFILE行... 429
§27.5.6 动态加载数据BFILE的表... 429
§27.5.7 用BFILE数据来加载LOB数据... 430
§27.5.8 用FILEOPEN打开BFILE. 431
§27.5.9 用OPEN打开BFILE. 431
§27.5.10 用FILEISOPEN看BFILE是否打开... 431
§27.5.11 用ISOPEN看BFILE是否打开... 432
§27.5.12 显示BFILE. 432
§27.5.13 从BFILE中读数据... 433
§27.5.14 读部分BFILE. 433
§27.5.15 比较BFILE. 434
§27.5.16 判断BFILE是否存在模式... 434
§27.5.17 判断BFILE是否存在... 435
§27.5.18 得到BFILE长度... 435
§27.5.19 拷贝BFILE的LOB位置... 436
§27.5.20 得到目录别名和文件名字... 436
§27.5.21 用初始化BFILE位置更新BFILE.. 437
§27.5.22 用FILECLOSE关闭BFILE. 437
§27.5.23 用CLOSE关闭BFILE. 437
§27.5.24 用CLOSEALL关闭所有BFILE. 438
§27.5.25 用DELETE等删除BFILE数据... 438
§27.6 使用SQL*loader加载LOB. 438
§27.6.1 加载图象和文本文件... 438
§27.6.2 加载文本文件... 439
第二十八章 PL/SQL编程技巧... 442
§28.1用触发器实现日期格式的自动设置... 442
§28.2如何避免TOO_MANY_ROWS错误... 443
§28.3如何解决TOO_MANY_ROWS问题... 446
§28.4如何在PL/SQL中使用数组... 447
§28.5如何使用触发器完成数据复制... 448
§28.6在PL/SQL中实现Truncate. 449
§28.7如何导出存储过程、触发器的代码... 450
附录A:SQL及SQL*PLUS 命令参考... 459
@ (“at” 号)464
CREATE LIBRARY.. 503
CREATE PACKAGE BODY.. 504
运算符... 582
运算符— < >. 583
运算符—>. 583
运算符—> =. 584
运算符—! =. 584
运算符—*. 585
运算符—+. 586
运算符—-*. 586
运算符—/587
运算符—<=. 587
运算符—=. 588
运算符—AND.. 589
运算符—BETWEEN.. 589
运算符—IN.. 590
运算符—IS NOT NULL. 590
运算符—IS NULL. 591
运算符—NOT. 591
运算符—NOT BETWEEN.. 592
运算符—NOT IN.. 592
运算符—OR. 593
第一部分 OracleSQL*PLUS基础
第一章 Oracle数据库基础
Oracle的SQL*PLUS是设计所有应用系统的基础工具。要想将应用系统设计成一个健壮的、性能优越的系统。最关键的是要理解RDBMS的真正含义和结构,理解OracleSQL*PLUS 的特点和核心,弄清关系数据库与桌面数据库的差别。比如理解数据的完整性、一致性、索引、视图等。只有这样才能设计出符合Oracle特点的应用系统。从而保证系统在提供使用后不会出现一致性、性能等问题。
§1.1 理解关系数据库系统(RDBMS)
CODASYL(数据系统语言协会)是数据库任务组(Database Task Group,DBTG)创建的一种数据库标准,这是一种基于COBOL的网络数据库标准。
§1.1.1 关系模型
一个描述两个集合的元素如何相互联系或如何一一对应的数学概念,对于数据库来说,关系只是一个带有一些特殊属性的表。所以有:
l 数据的基础项是关系
l 在这些表上的操作只产生关系
一个关系表必须符合某些特定条件,才能成为关系模型的一部分
l 储存在单元中的数据必须是原子的。每个单元只能存储一条数据,叫信息原则(Information Principle)。如果存储多条则违反信息原则。特舒情况下可能需要违反信息原则。
l 储存在列下的数据必须具有相同的数据类型。
l 每一行是唯一的(没有完全相同的行)。
l 列没有顺序。
l 行没有顺序
l 列有一个唯一性的名称。
关系模型的另一个是完整性原则。它包括实体完整性原则(Entity integrity rule)和引用完整性原则( Referential integrity rule ),如:
l 主键(Primary key )是能唯一标识行的一列或一组列的集合。
l 由多个列构成的主键称为连接键(Concatenated key)、组合键(Compound key ),或称作为复合键(Composity key )。
另外就是外部键(Foreign key )是一个表中的一列或一组列,它们在其它表中作为主键而存在。一个表中的外部键被认为是对另外一个表中主键的引用。实体完整性原则简洁地表明主键不能全部或部分地空缺或为空,引用完整性原则简洁地表明一个外键必须为空或者它所引用的主键当前存在值相一致。
§1.1.2 Codd十二法则
Oracle数据库系统是一个完美的完全符合数据库技术的关系数据库系统。要想你的应用设计按照数据库原理来进行,最重要的就是理解Oracle的结构、语句和命令。Codd提出的十二条法则在Oracle系统中都可以找到:
1)信息法则。
2)授权存储法则,每一个数据项都通过“表名+行主键+列名”的组合形成访问。
3)必须以一致的方法使用空值。
4)一个活跃的、在线数据字典应作为关系型表被储存
5)必须提供数据存取语言进行存取访问。
6)所有能被更新的视图应当是可被更新的。
7)必须有集合级的插入、更新和删除。
8)物理数据的独立性。即应用不依赖物理结构。
9)逻辑数据的独立性。如果一个表被分成两个部分,则应用视图连接在一起,以便不会对应用产生影响。
10) 完整性的独立性。完整性规则应该储存在数据字典中。
11) 分布独立性。一个数据库即使被分布,也应该能工作。
12) 非破坏性原则。如果允许低级存取,一定不能绕过安全性和完整性原则。
§1.2 关系数据库系统(RDBMS)的组成
RDBMS由两部分组成,即数据库系统内核(软件)和数据字典(内核用于管理数据库系统的数据结构)两部分。
§1.2.1 RDBMS 内核
RDBMS就是用来控制数据访问的操作系统。它的任务是按照一定的规则存储数据、检索数据及保护数据。
§1.2.2 数据字典概念
数据自动存放数据库中所有对象(如表,索引,视图等)所需的信息。Oracle 8i 的数据字典是存放数据库系统信息的一组表,从数据字典中的信息可以确认数据库中数据对象的基本信息及存放位置。
§1.3 SQL、SQL*Plus及PL/SQL
下面用简单的语言解释Oracle的常用产品所包含的SQL*PLUS和PL/SQL的关系。
§1.3.1 SQL和SQL*PLUS的差别
SQL是标准结构查询语言,而SQL*PLUS实际是包括标准的SQL和Oracle公司的一些命令组成的产品,因而Oracle公司将其取名为SQL*PLUS。下面是它们的解释。
1. SQL(StructuredQuery Language)
SQL有许多关键字,以下语句是常用于开头的语句:
Alter Insert
Audit Lock
Commit Noaudit
Comment Rename
Create Revoke
Delete Select
Drop Update
Grant Validate
注:
1.Oracle的SQL缓冲区一次只能存放一条SQL命令;
2.Validate(使生效) 在 oracle 中跟 Enable 一起用,但可以省去 Validate,所以许多资料都不介绍Validate 的用法。其语法如:
Enable{[Validate][Novalidate]}{ [UNIQUE][PRIMARY KEY]... } ...
2. SQL*PLUS
除SQL外,SQL*PLUS还包括称为SQL*PLUS命令的附加命令,这些命令主要用于形成复杂报表,编辑SQL命令,提供帮助信息,维护系统等。SQL*PLUS包括的命令如下:
@ Connect
Host Set
# Copy
Input Show
$ Define
List Spool
/ Del
Newpage Sqlplus
Accept Describe
Pause Start
Append Disconnect
Quit Timing
Break Document
Remark Ttitle
Btitle Edit
Prompt Undefine
Chang Execute
Print Save
Clear Exit
Run Column
Get Runform
Compute Help
3. DDL(Data Define Language)
对于结构查询语言(有时称SQL命令),可以将它们分成两组,一组是数据定义语言(DDL);另一组是数据操纵语言(DML)。其中用于数据定义的语言如下:
Alter procedure 重编译过程
Alter table 修改表的属性
Analyze 统计数据库对象性能值
Alter table add Constraint 对已有的表加约束
Create table 建立表结构
Create index 建立索引
Drop table 删除表实体及相关的索引
Drop index 删除索引
Grant 授权给用户或角色
Truncate 删除表中的所有行
Revoke 从用户或角色收回权限
4.DML(Data Manipulation Language)
对于结构查询语言的另一组是数据操纵语言(DML)。DML其中用于数据操纵的命令如下:
Insert
Delete
Update
Select
Commit work
Rollback
§1.3.2 PL/SQL语言
PL/SQL是OracleRDBMS (Oracle 6 之后版本)的一个组成部分,PL 是“过程化语言(ProcedureLanguage )”的缩写。PL/SQL语言是在SQL语言中结合了结构化过程语言成分的准第四代语言。
使用PL/SQL的目的:
由于大多数PL/SQL是在服务端来运行,这样可减少由客户端运行程序时所需的网络数据流量。
可以使用PL/SQL的地方:
l PL/SQL可以单独进行程序的编写,完成一般的处理功能;
l 在高级语言中可嵌入PL/SQL 块;
l 在4GL中可以嵌入PL/SQL块;
l 在PL/SQL程序中可以嵌入 HTML和XML。
§1.4 登录到SQL*PLUS
我们创建任何对象,如创建表、索引等都需要连接到Oracle中,这里用“登录”主要是Oracle的界面提供的是Login 这样的叫法。其实就是连接的意思。在Client/Server结构下,Oracle提供两种方式连接SQL*PLUS,其中SQL*NET V2.x 版本(目前版本不再使用SQL*NET V2.x)提供在字符方式下连接到 SQL*PLUS,SQL*NET V2. x版本提供在图形方式(Window)使用,目前版本的 ORACLE8/8i 都使用 NET8 连接.(NET8 不再支持字符终端)
§1.4.1 UNIX环境
在UNIX下,要确保客户端或服务器端与Oracle服务器系统进行连接,必须保证tnsnames.ora和listener.ora两个参数文件的正确配置。详细的配置解释在DBA章节里解释。下面是tnsnames.ora和listener.ora两个参数文件的内容显示。
1. tnsnames.ora参数文件:
INST1_HTTP =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST =dbsvr)(PORT = 1521))
)
(CONNECT_DATA =
(SERVER = SHARED)
(SERVICE_NAME = s450)
(PRESENTATION = http://admin)
)
)
EXTPROC_CONNECTION_DATA=
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY =EXTPROC))
)
(CONNECT_DATA =
(SID = PLSExtProc)
(PRESENTATION = RO)
)
)
S450 =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = dbsvr)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = s450)
)
)
2. listener.ora参数文件:
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = dbsvr)(PORT = 1521))
)
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY =EXTPROC))
)
)
)
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME =/home/oracle/app/oracle/product/8.1.7)
(PROGRAM = extproc)
)
(SID_DESC =
(GLOBAL_DBNAME = s450)
(ORACLE_HOME = /home/oracle/app/oracle/product/8.1.7)
(SID_NAME = s450)
)
)
如果安装后已经正确地对上面的两个参数文件进行配置,则可以按照下面步骤进行登录:
1)先启动服务器端监听器:
lsnrctl start
2)用下面命令显式登录到 SQL*PLUS:
$sqlplususername/password
或
$SQLPLUS username/password@connect_string
3)用下面命令隐式登录到 SQL*PLUS:
$sqlplus [enter]
Enter username:scott
Enterpassword:******
§1.4.2 WindowsNT和WINDOWS/2000环境
在NT/2000环境下,要使客户端能与Oracle服务器进行连接,tnsnames.ora和listener.ora参数文件的配置如下:
1. tnsnames.ora参数文件:
ORA816.TAIJI.COM.CN =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST =zhao)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = ora816)
)
)
EXTPROC_CONNECTION_DATA.TAIJI.COM.CN=
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY =EXTPROC1))
)
(CONNECT_DATA =
(SID = PLSExtProc)
(PRESENTATION = RO)
)
)
S450 =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST =dbsvr)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = s450)
)
)
2. listener.ora参数文件:
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY =EXTPROC1))
)
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST =zhao)(PORT = 1521))
)
)
(DESCRIPTION =
(PROTOCOL_STACK =
(PRESENTATION = GIOP)
(SESSION = RAW)
)
(ADDRESS = (PROTOCOL = TCP)(HOST =zhao)(PORT = 2481))
)
)
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = D:\oracle)
(PROGRAM = extproc)
)
(SID_DESC =
(GLOBAL_DBNAME = ora816)
(ORACLE_HOME = D:\oracle)
(SID_NAME = ora816)
)
)
Windows NT/2000字符方式登录步骤:
1)先启动服务器端监听器:
lsnrctl start
2)用下面命令显式登录到 SQL*PLUS:
$sqlplususername/password
或
$SQLPLUS username/password@connect_string
Windows NT/2000图形方式登录步骤:
1)点击“开始”=》“程序”=》“Oracle –OraHome81”=》“Application on Development”=》“SQL Plus”进入如下屏幕:
|
在WINDOWSNT/98/2000上的登录界面(sqlplus图)
可以在用户名框内输入用户、口令及主机字串;也可以分别进行输入。
§1.5 常用SQL*PLUS 附加命令简介
Oracle公司提供的附加语句(或称命令),可以满足程序人员和管理员的一些特殊操作要求。比如,在显示超过上百行记录信息时,可以采用每屏“暂停”来实现。要达到这样的目的,就要在SQL>下发 set pause on 命令。由于SQL*PLUS命令较多,下面仅给出最常用的几个命令的说明,详细的请参考附录。
§1.5.1 登录到SQL*PLUS
可以用下面命令登录到 SQL*PLUS,SQL*PLUS命令的简单语法如下:
SQLPLUS [ [logon] | [start] ]
logon可以是:
{username[/password][@connect_identifier]|/} [AS {SYSOPER|SYSDBA}]
|/NOLOG
注1:SQLPLUS主要是在命令方式下使用,在NT、WINDOWS/2000、UNIX的用法都一样。
注2:如果在UNIX下,SQLPLUS命令不被识别(不存在),则问题在环境变量PATH没有设置正确或者没有设置。SQLPLUS 可执行程序在$ORACLE_HOME/bin 目录下。
§1.5.2 EXIT和QUIT
可以用 exit 或quit 来终止SQL*PLUS的操作(会话)。语法如下:
{EXIT|QUIT} [SUCCESS|FAILURE|WARNING ]
{EXIT|QUIT} 可以用exit 或quit ,目前它们的功能一样。
SUCCESS 正常退出
FAILURE 带提示错误代码的退出
WARNING 带提示警告代码的退出
COMMIT 退出前将未提交进行保存
例如:
SQL>exit
§1.5.3 DESCRIBE(显示表、视图结构)
DESCRIBE可以用(只要用DESC即可)来显示表、视图的列的定义,也可以显示同义词、函数或存储过程的说明。语法如下:
DESC[RIBE] {[schema.]object[@connect_identifier]}
Schema:用户名,如果省去,则为对象的所有者。
object
可以是表(table), 视图(view),类型( type), 存储过程(procedure),函数( function), 包(package)或同义词( synonym)
@connect_identifier
数据库连接字串
例:显示emp 表的结构:
SQL>desc emp
§1.5.4 LIST(列出)命令
可以用 LIST 命令来列出当前SQL缓冲区中的一行或多行命令语句。
L[IST] [n|n m|n *|n LAST|*|* n|* LAST|LAST]
n 列出第n行
nm 列出n到m行
n * 列出第n行到当前行
n LAST 列出第n行到最末行
* 列出所有行
* n 列出当前行到第n行
*LAST列出当前行到最末行
LAST 列出最末行
例:
SQL> LIST
1 SELECT ENAME, DEPTNO, JOB
2 FROM EMP
3 WHERE JOB = ’CLERK’
4* ORDER BY DEPTNO
§1.5.5 Change(替换字符串)命令
可以用Change命令来改变字符串(即替换字符串)。语法如下:
C[HANGE] sepchar old [sepchar [new[sepchar]]]
Sepchar 为分隔符,可以是”/” 或”!” -- 请使用者特别注意
Old 旧字串
New 新字串
例:将 除号(/)改为 乘号( * ),则需要命令为c!/!*! 。即:
SQL> l
1* select sal,sal/100 from emp
SQL> c !/!*!
提醒:对于修改 / 字符的只能用 ! 来作分隔符(上例)。
例:将乘号( * )改为 加号( + ),则需要命令为c !/!*! 。即:
SQL> l
1* select sal,sal*100 from emp
SQL> c/*/+/
1* select sal,sal+100 from emp
SQL>
§1.5.6 Append(追加字符串)命令
可以用 Append命令来完成在当前行的末尾追加字符串。语法如下:
A[PPEND] text
Text 所要求追加的字符串
例:在当前行selectsal,sal+100 from emp 后加 where sal>=2000,则:
SQL> l
1* select sal,sal+100 from emp
SQL> a where sal>=2000
1* select sal,sal+100 from emp wheresal>=2000
SQL>
§1.5.7 Save保存当前缓冲区命令到文件
可以用SAVE命令将当前的命令行保存到操作系统的文件中。语法如下:
SAV[E] filename[.ext][CRE[ATE]|REP[LACE]|APP[END]]
其中:
filename:你将把缓冲区中的内容存入到操作系统目录的文件名。
ext:若使用文件后缀,缺省的文件后缀为SQL。
例:
SQL>select table_namefrom dict where table_name like ‘%ROLE%’;
SQL>save c:\get_role
§1.5.8 GET将命令文件读到缓冲区
可以用GET 命令将操作系统的目录下的命令文件读到缓冲区(但不执行)。语法如下:
GET filename [.ext][LIS[T]|NOL[IST]]
其中:
filename: 希望加载到SQL缓冲区的文件名
ext: 文件的扩展名,缺省为 SQL.
例:
SQL>get c:\get_role
§1.5.9 SPOOL将信息记录到文件中
Oracle的SPOOL 命令可以实现将屏幕所出现的一切信息记录到操作系统的文件中直到SPOOL OFF为止。语法如下:
SPO[OL] [filename[.ext] |OFF | OUT]
其中:
filename:你想输出(spool)的文件名。
ext:文件的后缀。缺省的后缀是LST(或LIS)。
SQL>coltable_name for a20
SQL>col commentsfor a80
SQL>set linesize110
SQL>SPOOlc:\all_dict
SQL>selecttable_name,comments from dict;
. . . .. . (系统查询信息)
SQL>SPOOL OFF
§1.5.10 再运行当前缓冲区的命令
在SQL>方式下,如果希望在运行当前的命令,可用Run(或R)或用 / 来实现,如:
SQL> set lin 120
SQL> select table_name from dict where table_name like '%ROLE%';
TABLE_NAME
------------------------------
DBA_ROLES
DBA_ROLE_PRIVS
USER_ROLE_PRIVS
ROLE_ROLE_PRIVS
ROLE_SYS_PRIVS
ROLE_TAB_PRIVS
SESSION_ROLES
已选择7行。
SQL> l
1* select table_name from dict where table_name like '%ROLE%'
SQL> /
TABLE_NAME
------------------------------
DBA_ROLES
DBA_ROLE_PRIVS
USER_ROLE_PRIVS
ROLE_ROLE_PRIVS
ROLE_SYS_PRIVS
ROLE_TAB_PRIVS
SESSION_ROLES
已选择7行。
§1.6 常用数据字典简介
ORACLE 提供许多内部数据字典, 用以管理系统的各种信息和参数(即数据库对象),下面列出几个常用的数据字典供初学者参考,其它方面的数据字典将在DBA管理中介绍。
ORACLE 数据字典的命名说明:
USER 为前缀----记录用户的所有对象信息
ALL 为前缀----记录包括 USER 记录和授权给PUBLIC或该用
户的所有对象的信息。
DBA 为前缀----记录关于数据库对象(非用户对象)的信息。
V$ 公共系统动态视图,用于系统优化和调整参考.
V_$ 动态性能视图,你可用 CATALOG.SQL 脚本建立动态视图建立同义词。
GV$ 新(oracle 8)的附加的固定视图(Global V$).在并行环境下反应的是
V$视图的信息。如:
SELECT * FROMGV$LOCK WHERE INST_ID = 2 OR INST_ID = 5 ;
返回的是instances2 和5的V$的信息。所以GV$反应一组Instances的参数.GV$视图的限制是参数PARALLEL_MAX_SERVERS必须大于0 。
详见 OracleEnterprise Manager Administrator's Guide.
注:请注意下面的总结:
l 一般DBA_的视图内容都包含USER_和ALL_为前缀的视图;
l DBA_为前缀的视图的内容基本上是大写;
l 以V$_为前缀的视图的内容基本上是小写。
1.USER_TABLEs(=TABS) 用户的所有表的信息。
2.USER_TAB_COLUMNS(=COLS)有关各表的列(字段)的信息
3.USER_VIEWS 用户的所有视图
4.USER_SYNONYMS(=SYN) 用户同义词
5.USER_SEQUENCES(=SEQ) 用户序列
6.USER_CONSTRAINTS 记录创建表结构时建立的限制。
7.USER_TAB_COMMENTS 表的注释。如:
Comment on table emp is '职工表';
8. USER_COL_COMMENTS 列(字段)注释。如:
Comment on column emp.ename is '姓名';
9. USER_INDEXES(=IND) 用户索引的简要信息
10. USER_IND_COLUMNS 用户索引的列信息
11. USER_TRIGGERS 用户触发器信息
12. USER_SOURCE 用户存储过程
13. USER_TABLESPACE 用户可以使用的表空间信息
14. USER_TS_QUOTAS 用户使用系统资源的信息
15. USER_SEGMENTS 用户对象所使用空间信息
16. USER_EXTENTS 用户的扩展段信息
17. USER_OBJECTS 用户对象
=USER_TABLES+USER_VIEWS+USER_INDEXES+
USER_SOURCE+USER_TRIGGERS+USER_JAVA
18. USER_PART_TABLES 用户分区信息
20. USER_PART_COL_STATISTICS
21. USER_IND_PARTITIONS
22. USER_FREE_SPACE
23. CAT(=USER_CATALOG) 用户可以访问的所有的基表。
24. TAB 用户创建的所有基表,视图,同义词等。
25. DICT(=DICTIONARY)构成数据字典的所有表的信息。
提示:虽然Oracle提供可以用Comment on column tablename.column is 'xxxx'; 等来实现对表或列进行注释,但不建议设计者采用这样的工作方式。而建议将注释写到脚本中更为直观。
§1.7 ORACLE数据类型
Oracle数据库的数据类型与其它的数据库系统相比,它的数据类型不多,Oracle在表示数据方面比其他数据库系统来说要省去许多关键字。Oracle只用NUMBER(m,n)就可以表示任何复杂的数字数据。其它如日期类型等也简单得多,只DATE就表示日期和时间。下面以列表形式给出各个版本的Oracle系统数据类型的表示方法。下面给出Oracle旧版本的数据类型的目的是让读者了解Oracle的变化,另外就是你在对旧版本进行升级或数据转换时要注意各个版本的差别。
ORACLE5、ORACLE6数据类型
数据类型 | 说明 |
Char | 可变长字符型,≤254 |
Varchar2 | 可变长字符型,≤2000 |
Number(m,n) | 数字类型,含整数、小数等 |
Date | 日期型,含时间,缺省格式为mmm-dd-yyyy hh:mi:ss(占7字节) |
Long | 存储大型可变长字符串,≤2GB |
Raw | 存储短二进制串,≤2GB |
Long raw | 存储长二进制串,≤2GB |
ORACLE7数据类型
数据类型 | 说明 |
Char | 定长字符,≤255个字符 |
Varchar | 变长字符,≤2000个字符 |
Varchar2 | 变长字符,≤2000个字符 |
Number(m,n) | 数字类型,含整数、浮点、双精度等 |
Long | 存储大型可变长字符串,≤2GB |
Raw | 存储可变短二进制数,≤2000 |
Long raw | 存储大型可变长二进制数,≤2GB |
ORACLE8/8i 数据类型
数据类型 | 说明 |
Char | 定长字符,≤2000个字符 |
Varchar | (同Varchar2)可变字符,≤4000个字符 |
Varchar2 | 变长字符,≤4000个字符 |
Date | 固定长度(7字节)的日期型 |
Number | 数字型,可存放实型和整型 |
Long | 可变字符,≤2GB个字符 |
Raw | 可变二进制数据,≤4000字节 |
Long raw | 可变二进制数据,≤2GB |
MLSLABEL | 仅Trusted Oracle 用长度在2~5字节间 |
Blob | 大二进制对象,≤4GB |
Clob | 大字符串对象,≤4GB |
Nclob | 多字节字符集的Clob,≤4GB |
Bfile | 外部二进制文件,大小由OS决定 |
CHAR(<size>)
定长字符型(在Oracle5、Oracle6 是变长),字符长度不够自动在右边加空格符号。当字符长度超出2000个则错误。不指定大小缺省为 1。
VARCHAR(<size>)
可变字符型,当前与VARCHAR2(<size>)相同。
VARCHAR2(<SIZE>)
可变字符型,当前与VARCHAR(<size>)相同。VARCHAR2 类型的字段(列)可存放4000个字符;但是VARCHAR2 变量可以存放32,767 个字符。大小必须指定。
NCHAR(<size>)和NVARCHAR2(<size>)
NCHAR 和NVARCHAR2 分别与CHAR和VARCHAR2 有相同的大小。并用于于存放National Language Support (NLS)数据,Oracle 允许以本地语言存放数据和查询数据。
如果将列名声明成NCHAR、NVARCHAR2这样的类型,则insert和 select 等语句中的具体值前加N,不能直接按照普通字符类型进行操作。看下面例子:
SQL> create tablenchar_tst(name nchar(6),addr nvarchar2(16),sal number(9,2));
表已创建。
SQL> insert intonchar_tst values(N'赵元杰',N'北京市海淀区',9999.99);
已创建 1 行。
SQL> select * fromnchar_tst where name like N'赵%';
NAME ADDR SAL
---------------------- ----------
赵元杰 北京市海淀区 9999.99
SQL> select *from nchar_tst where name like '赵%';
select * fromnchar_tst where name like '赵%'
*
ERROR 位于第 1 行:
ORA-12704: 字符集不匹配.
提示:虽然Oracle可以使用nchar, nvarchar2 类型来存放字符数据,但建议设计者不要使用NCHAR和NVARCHAR2。因为CHAR和VARCHAR2就能存放汉字。
NUMBER(<p>,<s>)
<p>是数据的整数部分,<s>是数据的精度(即小数)部分,注意,<s>部分可以表示负的精度。用<s>可以表示从小数点往右或往左保留多少位。如下表:
实际值 | 数据类型 | 存储值 |
1234567.89 | Number | 1234567.89 |
1234567.89 | Number(8) | 1234568 |
1234567.89 | Number(6) | 出错 |
1234567.89 | Number(9,1) | 1234567.9 |
1234567.89 | Number(9,3) | 出错 |
1234567.89 | Number(7,2) | 出错 |
1234567.89 | Number(5,-2) | 1234600 |
1234511.89 | Number(5,-2) | 1234500 |
1234567.89 | Number(5,-4) | 1230000 |
1234567.89 | Number(*,1) | 1234567.9 |
Sal number(7,2), --表示5位整数,2位小数.
DATE
Oracle 的日期型用7个字节表示,每个日期型包含如下内容:
l Century (世纪)
l Year (年)
l Month(月)
l Day (天)
l Hour (小时)
l Minute (分)
l Second (秒)
日期型字段有下面特点:
l 日期型字段的插入和更新可以数据型或字符并带to_date 函数说明即可。
l 缺省的日期格式有NLS_DATE_FORMAT参数控制,它的缺省格式为DD-MON-YY。
l 缺省的时间是夜里 00:00:00 (即0点 0分0秒)。
l sysdate返回的是服务器的时间,见下面例子。
l 日期格式的显示可以设置,见下面例子。
l 日期型可以运算,见下面例子。见下面例子。
l 世纪用cc 表示;年用yyyy表示,月用mm表示,日用dd表示,小时用hh24表示,分用mi表示,秒用ss表示。
例子:
SQL> create tablesave_info(per_id varchar2(20),name varchar2(20),tran_date date,
2 tran_val number(12,2));
表已创建。
SQL> insert into save_info values ('110105540609811','赵元杰',
2 to_date('2001.06.18','yyyy.mm.dd'),12345.66);
已创建 1 行。
SQL> select *from save_info;
PER_ID NAME TRAN_DATE TRAN_VAL
---------------------------------------- ---------- ----------
110105540609811 赵元杰 18-6月 -01 1234.66
SQL> selectper_id,name,to_char(tran_date,'yyyy/mm/dd'),tran_val from save_info;
PER_ID NAME TO_CHAR(TR TRAN_VAL
---------------------------------------- ---------- ----------
110105540609811 赵元杰 2001/06/18 12345.66
SQL> showparameter nls_date_format
NAME TYPE VALUE
------------------------------------------- ------------------------------
nls_date_format string
SQL> altersession set nls_date_format=
2 '"公元"yyyy"年"mm"月"dd"日"';
会话已更改。
SQL> selectsysdate from dual;
SYSDATE
------------------
公元2001年05月18日
SQL> selectto_char(sysdate,'cc yyyy.mm.dd') from dual;
TO_CHAR(SYSDA
-------------
21 2001.05.18
关于日期型的使用方法详细请参考《Oracle8i National Language Support Guide》。
BLOB
大二进制对象,每条记录可存储达4GB的数据,详细见后面章节。
CLOB
大字符对象,每条记录可存储达4GB的数据,详细见后面章节。
BFILE
外部二进制文件,每条记录可存储达4GB的数据(与OS有关),详细见后面章节。
RAW
非结构的二进制数据,这些数据不被数据库系统解释。RAW可以存储达2,000字节。
LONGRAW
大的二进制类型数据,LONGRAW是非结构的二进制数据,这些数据不被数据库系统解释。LONGRAW可以存储达2GB字节。LONGRAW不能被索引,而RAW可以被索引。
ROWID
ROWID在Oracle数据库中是一个虚的列,即系统用的特殊的列,不是我们建立的列。用于对数据库中的每条记录进行定位。详细见“Rowid的使用”章节。
UROWID
UROWID 是Universal ROWID 的意思。即全球ROWID,它支持逻辑和物理ROWID,也作为外部表的(通过getway 访问的非Oracle 表)的ROWID。UROWID类型可以存储所有的ROWID类型的数据。
%TYPE类型的匹配:
books_printed number(6);
books_sold books_printed%TYPE;
books_sold的数据类型与book_printed的类型一致;
( %TYPE类型 在 PL/SQL中介绍 )。
空值与字符型、数字型的运算:
null + <数字>=null (空值+数字仍是空值)
null > <数字>=null (空值与数字比较结果为空值)
null || '字符串' = 字符串
number类型与以下类型具有同等的值域:
DEC
Decimal
Double PREcision
Float
Integer
Int
Numeric
Real
Smallint
提示:虽然Oracle可以使用上面的子数据类型,但建议还是采用NUMBER(n,m)为好。因为如果使用子数据类型定义字段类型不当,可能引起数据在运算方面的问题。
Long数据类型的限制:
l select中可以用long;
l update中可以用select子句;
l insert中可以用Valus子句;
l 每个表只能允许一个long列;
l long列不能列出完整性约束(null、not null除外);
l long列不能被索引;
l 过程或存储函数不能接收long型函数;
l 存储函数不能返回long型值。
long 目前不能出现在以下情况中:
l select中的Where,Group by,order by,Connect by,distinct;
l 不能对long列作substr,instr;
l 表达式或条件;
l 子查询或集合中不能用long;
l Create table ...as select 中不能用long;
§1.8 SQL 语句基础
下面给出SQL语句的基本介绍,更详细的描述见后面的章节。
§1.8.1 SQL语句所用符号
操作符 | 用途 | 例子 |
+ - | 表示正数或负数,正数可省去 + | -1234.56 |
+ | 将两个数或表达式进行相加 | A=c+b |
- | 将两个数或表达式进行相减 | 34-12 |
* | 将两个数或表达式进行相乘 | 12*34 |
/ | 除以一个数或表达式 | 18*11 |
NULL | 空值判断 | Where name is null; |
|| | 字符串连接 | ‘101-’||tel_num |
= | 等于测试 | Select * from emp where name=’赵元杰’; |
!= 或<>或^= | 不等于测试 | Select * from emp where name !=’赵元杰’; |
< | 小于测试 | Select * from emp Where sal < 5000; |
> | 大于测试 | Select * from emp Where sal > 5000; |
<= | 小于等于测试 | Select * from emp Where sal <= 5000; |
>= | 大于等于测试 | Select * from emp Where sal >= 5000; |
Not in | 测试某值是否在一个指定的结果集中 | Select name,addr from expert where local not in(‘北京’,’上海’); |
ANY | 将一个值与一组值进行比较,返回满足条件的结果。必须跟!=,<,>,<=,>= | select ename,sal from emp where sal<= any(select sal from emp where deptno=10) |
SOME | 同ANY,必须跟!=,<,>,<=,>= |
|
ALL | 将一个值与一组值比较,返回满足条件的所有列值。必须跟!=,<,>,<=,>= | Select name,sal from emp Where sal<= all ( 500,800,1200); |
Not between A and B | 判断某个值是否界于两者之间。 | Select name,sal from emp Where sal between 500 and 1200; |
[not]exists | 判断某个列是否存在于一组值中。 | select dname,deptno from dept where exists (select * from emp where dept.deptno=emp.deptno) |
A[not]like b [Escape ‘char’] | 比较两个模式是否相似,当使用like 语句时Oracle不去访问索引。 | Select * from emp Where ename like ‘TH%’; |
Is [not] null | 测试值是否为空。 | Select ename,deptno from emp Where comm. Is null or comm.=0; |
Not | 对结果的否定。 | Select * from emp Where sal not(sal<1000); 等价于 select ename,sal from emp where sal>=1000; |
AND | 用于判断两个条件十分都满足。 | Select * from emp where Ename=’SIMTH’ and sal>=1000; |
OR | 用于判断两个条件中是否有一个满足。 | Select * from emp where Ename=’SIMTH’ or ename=’SCOTT’; |
UNION | 用于返回(组合)两个查询中所有唯一的行。 | Select ename from emp union Select ename from emp; |
UNION ALL | 用于返回(组合)两个查询中所有所有的行。 |
|
INTERSECT | 用于返回两个查询中相同的行。 | Select ename from emp1 intersect select ename from emp2; |
MINUS | 用于返回两个查询中的不同的行。 |
|
§1.8.2 简单select查询
当我们可以用SQL*PLUS登录到SQL>下后,我们可以用DESC 显示某表的结构,也可以用select 语句简单查询表中的一些列的内容。
例:要查询EMP表中员工的姓名、工资及出生日期,则:
SQL>select ename, sal, hiredate from emp;
§1.9 伪列及伪表
Oracle系统为了实现完整的关系数据库功能,系统专门提供了一组称为伪列(Pseudocolumn)的数据库列,这些列不是在建立对象(如建表)时由我们完成的,而是在我们建立对象时由自动Oracle完成的。Oracle目前有以下的伪列:
l CURRVALand NEXTVAL 使用序列号的保留字
l LEVEL 查询数据所对应的级
l ROWID 记录的唯一标识
有关伪列的详细解释和使用见相关章节。
Oracle 还提供了一个DUAL 的伪表,该表主要目的是保证在使用SELECT 语句中语句的完整性而提供的,如:我们要查询当前的系统日期及时间,而系统的日期和时间并是放在一个指定的表里。所以在 from 语句后就没有表名给出。为了使用 from 后有个表名,我们就用DUAL代替。如:
例1:查询Oracle系统日期及时间:
SQL> select to_char(sysdate,'yyyy.mm.dd hh24:mi:ss') fromDUAL;
TO_CHAR(SYSDATE,'YY
-------------------
2001.06.02 07:28:09
例2:计算一下 5000+5000*0.1 的结果是多少,则:
SQL> select 5000+5000*0.1 from DUAL;
5000+5000*0.1
-------------------
5500
§1.10 使用SQLWorksheet工作
在新的Oracle版本里,系统提供了一个叫SQLWorksheet 的图形SQL工作环境,它是ORACLEEnterprise Manager 的一部分。使用它可以免去在SQL>下编辑行命令的不便。它完全是在图形方式进行。它的启动有以下几种方法:
l 从ORACLEEnterprise Manager中启动
l 从Administrtor工具中启动
l 从操作系统中启动
下面给出在操作系统下启动SQL Worksheet 工具工作的步骤:
1) 点击 SQL Worksheet 图标,即选择 开始->程序->Oracle-OraHome81->databaseAdministration->SQLPLUS Worksheet。选中SQLPLUS Worksheet后出现如下画面:
图 1.10-1 使用SQL Worksheet 的登录画面
在用户名、口令及服务输入ORACLE的有效用户名、口令及连接字串再点击确定后进入如图1.10-2所示的画面。
2)当进入图1.10-2 所示的画面后。你就可以工作了。目前的版本是在屏幕的上方输入命令,屏幕的下方是系统显示结果。
3)我们将光标点击到屏幕上方,输入SQL命令,如输入:
select ename, sal from scott.emp;
输入完成命令后,用光标点击 左边的执行图标,Oracle 会结果显示在屏幕的下方:
图 1。10-2 登录成功后的画面
图1.10-3 查询命令及显示结果画面
从SQL Worksheet 工具中可以看出,该工具提供了相当丰富的功能。下面给出个功能的简要说明:
1.文件:
l 改变数据库连接:可以连接到另外的用户和数据库上去;
l 打开:可以打开现有文件;
l 将输入另存为:将当前命令存为一个文件;
l 将下方显示的结果存为一个文件。
2.编辑:
l 剪切:将当前选中字符进行剪切;
l 复制:将选中字符进行剪切;
l 粘贴:将缓冲区字符进行粘贴;
l 全选:对当前画面所有字符全选;
l 全部清除:清除目前的所有内容。
3.工作单:
l 执行:执行当前的命令;
l 运行本地脚本:运行操作系统下某个.SQL文件(点击后提示输入文件名);
l 命令历史记录:查看命令历史记录;
l 上一条命令:显示上一条命令。
提示:如果你的SQLWorksheet显示的不是汉字(出现乱字符),请你将你的NT/2000/98环境下的汉字变量设置与服务器的一样就好了。在WINDOWS NT环境设置汉字变量步骤:
用regedit修改注册表中的所有ORACLE字符集项,如:
1)点击:开始=》运行=》输入regedit;
2)点击“HEY_LOCAL_MACHINE”左边的“+”以展开目录;
3)点击“SOFTWARE”左边的“+”以展开目录;
4)点击“ORACLE”项;
5)修改ORACLE树下所有“NLS”变量与props$中的一致。
关于汉字环境变量的设置,请参阅《Oracle8i/9I初级数据库管理》。
第二章 查询基础
下面给出查询的一些基本的操作描述,如果你是位Oracle老手则可以跳这一章。
§2.1 SELECT语句
在关系数据库中,使用频率最高要算SELECT语句了。尽管SELECT 语句的使用非常简单,但确很有学问。下面简单介绍有关SELECT 语句的常用方法。
1.命令语法:
SELECT 的简单语法:
SELECT [DISTINCT | ALL] {* | column1[,column2]...}
FROM {table_1 | (subquery)} [alias]
[, {table_2 | (subquery)} [alias]]...
[WHERE condition]
[CONNECT BY condition [START WITH condition]
[GROUP BY expn] [HAVING expn]
[{ UNION [ALL] | INTERSECT | MINUS } SELECT . .. ]
[ ORDER BY [expn ] [ ASC | DESC]
[ FOR UPDATE [OF [user.]table | view] column ]
[NOWAIT]
详细语法结构需查阅最新ORACLE 原版《ORACLE8iSQL REFERENCE MANUAL》或《ORACLE9i SQL REFERENCE MANUAL》
§2.2 SQL中的单记录函数
许多资料(包括Oracle 的资料)把Oracle的SQL语句中用到的函数分为单值函数和多值函数,单值函数又分为字符函数和数字函数。下面分别介绍它们。
§2.2.1 单记录字符函数
函 数 | 说 明 |
ASCII | 返回对应字符的十进制值 |
CHR | 给出十进制返回字符 |
CONCAT | 拼接两个字符串,与 || 相同 |
INITCAT | 将字符串的第一个字母变为大写 |
INSTR | 找出某个字符串的位置 |
INSTRB | 找出某个字符串的位置和字节数 |
LENGTH | 以字符给出字符串的长度 |
LENGTHB | 以字节给出字符串的长度 |
LOWER | 将字符串转换成小写 |
LPAD | 使用指定的字符在字符的左边填充 |
LTRIM | 在左边裁剪掉指定的字符 |
RPAD | 使用指定的字符在字符的右边填充 |
RTRIM | 在右边裁剪掉指定的字符 |
REPLACE | 执行字符串搜索和替换 |
SUBSTR | 取字符串的子串 |
SUBSTRB | 取字符串的子串(以字节) |
SOUNDEX | 返回一个同音字符串 |
TRANSLATE | 执行字符串搜索和替换 |
TRIM | 裁剪掉前面或后面的字符串 |
UPPER | 将字符串变为大写 |
NVL | 以一个值来替换空值 |
ASCII(<c1>)
<c1>是字符串。返回与指定的字符对应的十进制数。
SQL> selectascii('A') A,ascii('a') a,ascii('0') zero,ascii(' ') space from dual;
A a ZERO SPACE
---------- -------------------- ----------
65 97 48 32
SQL> select ascii('赵')zhao,length('赵') leng from dual;
ZHAO LENG
---------- ----------
54740 1
CHR(<I>[NCHAR])
给出整数,返回对应字符。如:
SQL> selectchr(54740) zhao,chr(65) chr65 from dual;
ZH C
-- -
赵 A
CONCAT(<c1>,<c2>)
SQL> selectconcat('010-','8801 8159')||'转23' 赵元杰电话 from dual;
赵元杰电话
-----------------
010-8801 8159转23
INITCAP(<c1>)
返回字符串c1 并第一个字母变为大写。例如:
SQL> selectinitcap('simth') upp from dual;
UPP
-----
Simth
INSTR(<c1>,<c2>[,<I>[,<j>] ] )
在一个字符串中搜索指定的字符,返回发现指定的字符的位置。
C1: 被搜索的字符串
C2: 希望搜索的字符串
I: 搜索的开始位置,缺省是1
J: 出现的位置,缺省是1。
SQL> SELECT INSTR('Oracle Training', 'ra', 1, 2) "Instring" FROM DUAL;
Instring
----------
9
INSTRB(<c1>,<c2>[,<I>[,<j>] ] )
除了返回的字节外 ,与INSTR 相同,
LENGTH( <c> )
返回字符串 c 的长度。
SQL> l
1 select name,length(name),addr,length(addr),sal,length(to_char(sal))
2* from nchar_tst
SQL> /
NAME LENGTH(NAME) ADDR LENGTH(ADDR)SAL LENGTH(TO_CHAR(SAL))
------ ------------ ---------------- ------------ ---------- ----------------
赵元杰 3 北京市海淀区 6 99999.99 8
LENGTHB( <c> )
以字节返回字符串的字节数。
SQL> selectname,lengthb(name),length(name) from nchar_tst;
NAME LENGTHB(NAME) LENGTH(NAME)
------ ------------- ------------
赵元杰 6 3
LOWER ( <c> )
返回字符串并将所有字符变为小写。
SQL> selectlower('AaBbCcDd') AaBbCcDd from dual;
AABBCCDD
--------
aabbccdd
UPPER( <c>)
与 LOWER 相反,将给出字符串变为大写。如:
SQL> select upper('AaBbCcDd') AaBbCcDd from dual;
AABBCCDD
--------
AABBCCDD
RPAD和LPAD(粘贴字符)
RPAD(string,Length[,'set'])
LPAD(string,Length[,'set'])
RPAD在列的右边粘贴字符;
LPAD在列的左边粘贴字符。
例1:
SQL>select RPAD(City,35,'.'),temperature from weather;
RPAD(City,35,'.') temperature
------------------------------------------
CLEVELAND...... 85
LOS ANGELES.. 81
.........................
(即不够35个字符用'.'填满)
LTRIM(左截断)RTRIM(右截断) 函数
LTRIM (string [,’set’])
Left TRIM (左截断)删去左边出现的任何set 字符。
RTRIM (string [,’set’])
Right TRIM (右截断)删去右边出现的任何set 字符。
例1:
SELECT RTRIM (‘Mother Theresa, The’, ‘The’) “Example of Right
Trimming” FROM DUAL;
Example of Right
----------------
Mother Theresa,
SUBSTR Substr(string,start[,Count])
取子字符串中函数
对字串(或字段),从start字符 开始,连续取 count 个字符并返回结果,如果没有指count则一直取到尾。
select phone,substr(phone,1,3) || ‘0’ || substr(phone,4)
from telecommunication where master=’中国电信’;
SUBSTRB(string,start[,Count])
对字串(或字段),从start字节 开始,连续取 count 个字节并返回结果,如果没有指count则一直取到尾。
REPLACE (‘string’ [,’string_in’,’string_out’])
String:希望被替换的字符串或变量。
String_in:被替换字符串。
String_out:要替换字符串。
SQL> selectreplace('Informaix中国公司','Informaix','IBM Informix')
2 IBM数据库from dual;
IBM数据库
--------------------
IBM Informix中国公司
SOUNDEX( <c> )
返回一个与给定的字符串读音相同的字符串(不管拼写是否一样)。
SELECT DPL_NAME FROMDENIED_PARTIES_LIST WHERE
SOUNDEX(DPL_NAME) =SOUNDEX(‘Saddam Hussain’) ;
DPL_NAME
----------------------------------------------
Al Husseni
Sadda Al Sada.
REPLACE (‘string’ [,’string_in’,’string_out’])
String:希望被替换的字符串或变量。
String_out:要替换字符串。
SELECTREPLACE (‘Oracle’, ‘Or’, ‘Mir’) “Example“ FROM DUAL;
Example
-------
Miracle
TRIM( [<leading>]<trailing> FROM <trim_char> )
TRIM可以使你对给定的字符串进行裁剪(前面,后面或前后)。
l 如果指定 LEADING, Oracle 从trim_char 中裁剪掉前面的字符;
l 如果指定TRAILING, Oracle 从trim_char 中裁剪掉尾面的字符;
l 如果指定两个都指定或一个都没有给出,Oracle从trim_char 中裁剪掉前面及尾面的字符;
l 如果不指定 trim_character, 缺省为空格符;
l 如果只指定trim_source, Oracle Oracle从trim_char 中裁剪掉前面及尾面的字符。
例子:将下面字符串中的前面和后面的‘0‘字符都去掉:
SELECT TRIM (0FROM 0009872348900) "TRIM Example" FROM DUAL;
TRIM example
--------------------------------
98723489
§2.2.2 单记录数字函数
函数 | 说明 |
Value1 + value2 Value1 - value2 Value1 * value2 Value1 / value2 ABS(value) CEIL(value) COS(value) COSH(value) EXP(value) FLOOR(value) LN(value) LOG(value) MOD(value,divisor) NVL(value,substitute) POWER(value,exponent) ROUND(value,precision) SIGN(value) SIN(value) SINH(value) SQRT(value) TAN(value) TANH(value) TRUNC(value,按precision) VSIZE(value) | 加 减 乘 除 绝对值 大于或等于value的最小整数 余弦 反余弦 e的value次幂 小于或等于value的最大整数 value的自然对数 value的以10为底的对数 求模 value为空时以substitute代替 value的exponent次幂 按precision 精度4舍5入 value为正返回1;为负返回-1;为0返回 0. 余弦 反余弦 value 的平方根 正切 反正切 按照precision 截取value 返回value在ORACLE的存储空间大小 |
ABS( <n> )
返回指定值的绝对值。如:
SQL> selectabs(100),abs(-100) from dual;
ABS(100) ABS(-100)
---------- ----------
100 100
ACOS( <n> )
给出反余弦的值。如:
SQL> Select acos(-1)acos from dual;
ACOS
----------
3.14159265
ASIN( <n> )
给出反正弦的值。如:
SQL> select asin(-1)"arc sine" from dual;
arc sine
----------
-1.5707963
ATAN (<N> )
返回一个数字的反正切值。如:
SQL> select atan(-1)"arc tangent" from dual;
arc tangent
-----------
-.78539816
CEIL( <n> )
返回大于或等于给出数字的最小整数。如:
SQL> selectceil(3.14159) from dual;
CEIL(3.14159)
-------------
4
COS ( <n> )
返回一个数字余弦值。如:
SQL> selectcos(-3.1415926) from dual;
COS(-3.1415926)
---------------
-1
COSH ( <n> )
返回一个数字双曲余弦值。如:
SQL> select cosh(20)cosh from dual;
COSH
----------
242582598
EXP ( <n> )
返回一个数字 e 的 n 次方的值。如:
SQL> selectexp(2),exp(1) from dual;
EXP(2) EXP(1)
---------- ----------
7.3890561 2.71828183
FLOOR ( <n> )
对给定的数字取整数,如:
SQL> selectfloor(123.45),floor(45.56) from dual;
FLOOR(123.45) FLOOR(45.56)
------------- ------------
123 45
LN( <n> )
返回一个数字的对数值,n 是大于 0 的数字,如:
SQL> selectln(1),ln(2),ln(3) from dual;
LN(1) LN(2) LN(3)
---------- ---------- ----------
0 .693147181 1.09861229
LOG( <n1>, <n2> )
返回一个以n1为底的n2的对数,n1不是0或1的正数。如:
SQL> selectlog(2,1),log(2,2) from dual;
LOG(2,1) LOG(2,2)
---------- ----------
0 1
MOD( <n1>,<n2> )
SQL> Selectmod(10,3), mod(10,2), mod(10,4) from dual;
MOD(10,3) MOD(10,2) MOD(10,4)
---------- ---------- ----------
1 0 2
POWER ( <n1>, <n2> )
返回 n1 的 n2 次方值,如:
SQL> selectpower(2,10),power(3,3) from dual;
POWER(2,10) POWER(3,3)
----------- ----------
1024 27
ROUND(value,precision)
按照指定的精度进行舍入;
selectround(55.5),round(-55.5),trunc(55.5),trunc(-55.5)
from dual;
round(55.5)round(-55.5) trunc(55.5) trunc(-55.5)
----------------------- ----------- -------------
56 -56 55 -55
SIGN(<n>)
取数字n 的符号,大于0 返回 1;小于0 返回-1; 等于0 返回0。 如:
例:
select sign(123),sign(-100),sign(0) from dual;
sign(123) sign(-100) sign(0)
-------------------- ----------
1 -1 0
SIN ( <n> )
返回一个数字的正弦值。如:
SQL> selectsin(1.57079) from dual;
SIN(1.57079)
------------
1
SINH( <n> )
返回双曲余弦的值,如:
SQL> selectsin(20),sinh(20) from dual;
SIN(20) SINH(20)
---------- ----------
.912945251 242582598
SQRT( <n> )
返回 数字 n 的根,如:
SQL> selectsqrt(64),sqrt(10) from dual;
SQRT(64) SQRT(10)
---------- ----------
8 3.16227766
TAN( <n> )
返回数字 n的正切值,如:
SQL> selecttan(20),tan(10) from dual;
TAN(20) TAN(10)
---------- ----------
2.23716094 .648360827
TANH( <n> )
返回数字 n的双曲正切值,如:
SQL> selecttanh(20),tan(20) from dual;
TANH(20) TAN(20)
---------- ----------
1 2.23716094
TRUNC(value,precision)
按照指定的截取一个数。如:
SQL> SELECT TRUNC(124.16666, -2) trunc1, trunc(124.16666,2) from dual;
TRUNC1 TRUNC(124.16666,2)
----------------------------
100 124.16
§2.2.3 单记录日期函数
Oracle 用到的日期函是:
函 数 | 描 述 |
ADD_MONTH | 在日期date上增加count个月 |
GREATEST(date1,date2,. . .) | 从日期列表中选出最晚的日期 |
LAST_DAY( date ) | 返回日期date 所在月的最后一天 |
LEAST( date1, date2, . . .) | 从日期列表中选出最早的日期 |
MONTHS_BETWEEN(date2,date1) | 给出 Date2 - date1 的月数(可以是小数) |
NEXT_DAY( date,’day’) | 给出日期date之后下一天的日期,这里的day为星期,如: MONDAY,Tuesday等。 |
NEW_TIME(date,’this’,’other’) | 给出在this 时区=Other时区的日期和时间 |
ROUND(date,’format’) | 未指定format时,如果日期中的时间在中午之前,则将日期中的时间截断为12 A.M.(午夜,一天的开始),否则进到第二天。时间截断为12 A.M.(午夜,一天的开始),否则进到第二天。 |
TRUNC(date,’format’) | 未指定format时,将日期截为12 A.M.( 午夜,一天的开始). |
ADD_MONTHS( <d>,<I> )
增加月份和减去月份,如:
SQL> selectto_char( add_months(to_date('199712','yyyymm'), 1),'yyyymm') add_month
2 fromdual;
ADD_MO
------
199801
SQL> selectto_char(add_months(to_date('199712','yyyymm'), -1 ),'yyyymm') add_mo
2 fromdual;
ADD_MO
------
199711
LAST_DAY( date)
返回日期date 所在月的最后一天,如:
SQL> selectto_char(sysdate,'yyyy.mm.dd'),to_char((sysdate)+1,'yyyy.mm.dd')
2 fromdual;
TO_CHAR(SYTO_CHAR((S
--------------------
2001.05.182001.05.19
MONTHS_BETWEEN(date2,date1)
给出 Date2 -date1 的月数(可以是小数);
SQL> select months_between('19-12月-1999','19-3月-2000')mon_betw from dual;
MON_BETW
----------
-3
SQL> selectmonths_between(to_date('2000.05.20','yyyy.mm.dd'),
2 to_date('2005.05.20','yyyy.mm.dd') ) mon_bet from dual;
MON_BET
----------
-60
NEW_TIME(date,’this’,’other’)
给出在this 时区=Other时区的日期和时间
This和other 是时区,它们可以是下面的值:
时区缩写 | 代表的时区 |
AST/ADT | 大西洋标准/日期时间 |
BST/BDT | 白令海标准/日期时间 |
CST/CDT | 中部标准/日期时间 |
GMT | 格林威治时间 |
HST/HDT | 阿拉斯加-夏威夷标准/日期时间 |
MST/MDT | 山区标准/日期时间 |
NST | 新大陆标准时间 |
PST/PDT | 太平洋标准/日期时间 |
YST/YDT | Yukon标准/日期时间 |
SQL> selectto_char(sysdate,'yyyy.mm.dd hh24:mi:ss') bj_time,
2 to_char(new_time(sysdate,'PDT','GMT'),'yyyy.mm.dd hh24:mi:ss')los_angles
3 from dual;
BJ_TIME LOS_ANGLES
--------------------------------------
2001.05.19 06:25:252001.05.19 13:25:25
NEXT_DAY( date,’day’)
给出日期date和星期x之后计算下一星期x的日期,这里的day为星期,如: MONDAY,Tuesday等。但在中文环境下,要写成’星期x’这样的格式,如:
例:比如今天是5月18日星期五,计算下一个星期五是几号:
SQL> select next_day('18-5月-2001','星期五')nxt_day from dual;
NXT_DAY
----------
25-5月 -01
SYSDATE
用来得到系统的当前日期,如:
SQL> selectto_char(sysdate,'dd-mon-yyyy day') from dual;
TO_CHAR(SYSDATE,'DD
-------------------
18-5月 -2001 星期五
TRUNC(<date>,[,<fmt>])
按照 给出的 fmt 的要求将日期截断。如果 fmt=’mi’ 则表示截断掉秒保留至分。如:
SQL> selectto_char(trunc(sysdate,'hh'),'yyyy.mm.dd hh24:mi:ss') hh,
2 to_char(trunc(sysdate,'mi'),'yyyy.mm.dd hh24:mi:ss') hhmm from dual;
HH HHMM
--------------------------------------
2001.05.18 22:00:002001.05.18 22:27:00
§2.2.4 单记录转换函数
函 数 | 描 述 |
CHARTOROWID | 将 字符转换到 rowid类型 |
CONVERT | 转换一个字符节到另外一个字符节 |
HEXTORAW | 转换十六进制到raw 类型 |
RAWTOHEX | 转换raw 到十六进制 |
ROWIDTOCHAR | 转换 ROWID到字符 |
TO_CHAR | 转换日期格式到字符串 |
TO_DATE | 按照指定的格式将字符串转换到日期型 |
TO_MULTIBYTE | 把单字节字符转换到多字节 |
TO_NUMBER | 将数字字串转换到数字 |
TO_SINGLE_BYTE | 转换多字节到单字节 |
CHARTOROWID(<c>)
将字符数据类型转换为ROWID类型,如:
1* select rowid,rowidtochar(rowid),ename fromscott.emp
SQL> /
ROWID ROWIDTOCHAR(ROWID) ENAME
----------------------------------- --------------------------------------------------
AAAFXDAABAAAHVaAAA AAAFXDAABAAAHVaAAA SMITH
AAAFXDAABAAAHVaAAB AAAFXDAABAAAHVaAAB ALLEN
AAAFXDAABAAAHVaAAC AAAFXDAABAAAHVaAAC WARD
CONVERT( <c>,<dset>[,<sset>] )
将源字符串sset从一个语言字符集转换到另一个目的dset字符集。
SELECT CONVERT (‘strutz’, ‘ WE8HP’, ‘ F7DEC ‘) “Conversion”
FROM DUAL;
Conversion
---------------
Strutz.
HEXTORAW( <x> )
将一个十六进制构成的字符串转化为二进制。如:
Insert into printers(printer_nbr,manufacturer,model,init_string)
Values ( 12,’HP’,’Laserjet’,”HEXTORAW(‘1B45’));
RAWTOHEX( <x> )
将一个二进制构成的字符串转化为十六进制。如:
select rawtohex (init_string) hext from printers
where model=LaserJet’and manufacturer=’HP’;
hext
-----------
1B45
ROWIDTOCHAR(<x> )
将ROWID数据类型转换为字符类型,见 CHARTOROWID。
TO_CHAR(date,’format’)
根据format 重新格式日期date的格式。如:
SQL> selectto_char(sysdate,'yyyy/mm/dd hh24:mi:ss') from dual;
TO_CHAR(SYSDATE,'YY
-------------------
2001/05/18 23:05:36
日期格式比较多,详细内容请参考原版资料。下面给出常用的日期格式代码:
日期格式代码表
日期代码 | 格式说明 | 例子 |
AD 或 BC | AD=Anno Domini公元,BC=Before Christ公元前。不带点的公元或公元前 | ‘YYYY AD’=1999 AD |
A.D. 或B.C. | 带点的公元或公元前 | ‘YYYY A.D.’=1999 A.D. |
AM或PM | AM= ante meridiem 上午,PM=post meridiem下午。不带点的上午或下午 | ‘HH12AM’=09AM |
A.M.或P.M. | 带点的上午或下午 | ‘HH12A.M.’=09A.M. |
DY | 星期几的缩写 | Mon,Tue,... |
DAY | 星期几的全拼 | Monday,Tuesday,... |
D | 一周的星期几,星期天=1,星期六=7 | 1,2,3,4,5,6,7 |
DD | 一月的第几天,1à31 | 1,2,... 31 |
DDD | 一年的第几天,1à366 | 1,2,3,...366 |
J | 公元前的第几天(从公元前4712起 ?) | 2451514,2451515,... |
W | 一个月的第几周,1à 5 | 1,2,3,4,5 |
WW,IW | 一年的第几周,一年的ISO的第几周 | 1,2,3,4,... 52 |
MM | 两为数的月 | 01,02,03,...12 |
MON | 月份的缩写 | Jan,Feb,Mar ,...Dec |
MONTH | 月份的全拼 | January,February,... |
RM | 罗马数字的月份,I à XII | I,II,III,IV,...XII |
YYYY,YYY,YY,Y | 四位数的年,三位数的年 | 1999,999,99,9 |
YEAR | 年的全拼 | Nineteen Ninety-nine |
SYYYY | 如果是公元前(BC),年份前负号 | -1250 |
RR | 当前年份的后两位数字 | 01代表2001年 |
HH,HH12 | 12小时制,1à12 | 1,2,3,...12 |
HH24 | 24小时制,0à23 | 0,1,2,3,...23 |
MI | 一小时中的第几分,0à59 | 0,1,2,3...59 |
SS | 一分中的第几秒,0à59 | 0,1,2,3,...59 |
SSSSS | 一天中的第几秒,0à86399 | 0,1,2,3,...86399 |
../-;: | 标点符号表示法 | 文字显示 |
‘text’ | 引号表示法 | 文字显示 |
TO_DATE(string,’format’)
将一和字串转换为ORACLE的日期。如:
Insert into demo(demo_key,date_col)
Values(1 , to_date(’04-Oct-1999’,‘DD-Mon-yyyy’) );
TO_MULTI_BYTE(<c>)
将字符串中的单字节字符转换为多字节字符, 如:
TO_NUMBER(<c>)
将给出的字符转换为数字,如:
SELECT TO_NUMBER (‘1947’) “FISCAL_YEAR” FROM DUAL;
FISCAL_YEAR
-----------
1947
TO_MULTI_BYTE(<c>)及TO_SINGLE_BYTE
将单字节转换为多字节或从多字节转换为单字节。
§2.2.5 其它的单记录函数
BFILENAME( <dir>, <file> )
指定一个外部二进制文件。如:
INSERT INTOfile_tbl
VALUES (BFILENAME(’lob_dir1’, ’image1.gif’));
CONVERT(‘x’,’desc_set’ [, ‘source_set’])
将x 字段或变量的源 source 转换为 desc,如:
selectsid,serial#,username,
DECODE(command,
0,’None’,
2,’Insert’,
3,’Select’,
6,’Update’,
7,’Delete’,
8,’Drop’,
‘Other’) cmd
from v$session wheretype != ‘BACKGROUND’;
关于DECODE 在优化方面的内容在《Oracle8i/9i 高级数据库管理》中查阅。
DUMP( s,[,fmt [,start [, length ] ] ] )
DUMP 函数以fmt指定的内部数字格式返回一个VARCHAR2类型的值。如:
SQL> col global_namefor a30
SQL> col DUMP_STRINGfor a50
SQL> set lin 200
SQL> select global_name,dump(global_name,1017,8,5)dump_string
2 from global_name;
GLOBAL_NAME DUMP_STRING
-----------------------------------------------------------------------------------------------------
ORA816.US.ORACLE.COM Typ=1 Len=20 CharacterSet=ZHS16GBK: U,S,.,O,R
EMPTY_BLOB() 和EMPTY_CLOB() 函数
这两个函数都是用来对大数据类型字段进行初始化操作的函数,一般有:
BLOB数据类型 --- EMPTY_BLOB()
CLOB数据类型 --- EMPTY_CLOB()
NCLOB数据类型 --- EMPTY_CLOB()
Insert into proposal
( proposal_id,recipient_name,proposal_name,short_description,
proposal_text,budget, cover_letter )
values(2,’BRAD OHMONT’,’REBUILD FENCE’,NULL,
EMPTY_CLOB(),EMPTY_BLOB(),
BFILENAME(‘proposal_dir’,’P2.DOC’) );
GREATEST(<exp_list> )
返回一组表达式中的最大值,即比较字符的编码大小。如:
SQL> selectgreatest('AA','AB','AC') from dual;
GR
--
AC
SQL> selectgreatest('啊','安','天') from dual;
GR
--
天
即 “天”的编码比“安”和“啊”都大。
LEAST (<exp_list> )
返回一组表达式中的最小值,即比较字符的编码大小。如:
SQL> select least('啊','安','天')from dual;
LE
啊
UID 函数
返回标识当前用户的唯一整数,如:
SQL> show user
USER 为"SYSTEM"
SQL> l
1* select username,user_id from dba_userswhere user_id=UID
SQL> /
USERNAME USER_ID
------------------------------ ----------
SYSTEM 5
USER 函数
返回当前用户的名字,如:
SQL> select userfrom dual;
USER
------------------------------
SYSTEM
USERENV(<opt> )
返回当前用户环境的信息,opt 选项可以是:
ENTRYID 返回当前用户会话的入口ID
SESSIONID 返回当前用户会话的ID
TERMINAL 返回当前系统会话的操作系统标识
OSDBA 如果当前用户有DBA权限,则返回TRUE
LABLE 返回当前用户会话的标号
LANGUAGE 返回当前用户的语言和区域
CLIENT_INFO 为当前用户会话返回 client-info 域的值,这个值由
dbms_application_info,set_client_info 过程来设置。
LANG 以ISO 的三个字符表示当前用户会话所使用的语言。
VSIZE 返回表达式的字节大小。
ISDBA 函数
查看当前用户是否是 DBA ,当SYSDBA 角色有效是才返回 TRUE,如:
SQL> show user
USER is"SYSTEM"
SQL> selectuserenv('ISDBA') from dual;
USEREN
------
FALSE
SQL> connectsys/sys@ora816
Connected.
SQL> selectuserenv('ISDBA') from dual;
USEREN
------
FALSE
SQL> connectinternal
Connected.
SQL> selectuserenv('ISDBA') from dual;
USEREN
------
TRUE
SESSIONID函数
返回审计会话标识,如:
select userenv(‘SESSIONID’) aud_id from dual;
aud_id
---------
47343
ENTRYID 函数
返回审计会话入口标识,当initsid.ora 文件中的 audit_trail=TRUE 时可以用,如:
select userenv(‘ENTRYID’)from dual;
USERENV(‘ENTRYID’)
-------------
835641
INSTANCE函数
返回当前INSTANCE 的标识,如:
SQL> selectuserenv('INSTANCE') from dual;
USERENV('INSTANCE')
-------------------
1
LANGUAGE函数
返回当前环境的语言,如:
SQL> selectuserenv('LANGUAGE') from dual;
USERENV('LANGUAGE')
----------------------------------------------------
SIMPLIFIEDCHINESE_CHINA.ZHS16GBK
LANG函数
返回当前环境的语言的缩写,如:
SQL> l
1* select userenv('LANG') from dual
SQL> /
USERENV('LANG')
----------------------------------------------------
ZHS
TERMINAL函数
返回用户的终端或机器的标识,如:
SQL> selectuserenv('TERMINAL') from dual;
USERENV('TERMINA
----------------
ZHAOYUANJIE
VSIZE( <x>)
返回 x 的大小(字节)数,如:
SQL> selectvsize(user),user from dual;
VSIZE(USER) USER
----------- -----------------------------
6 SYSTEM
§2.3 SQL中的组函数
§2.3.1 多记录组函数
AVG( [ { DISTINCT | ALL}] )
求平均值,ALL表示对所有求平均值,DISTINCT 只对不 同的求平均值,相同只取一个。
SQL> l
1* select avg(sal) from emp
SQL> /
AVG(SAL)
----------
2073.21429
MAX( [ { DISTINCT | ALL}] )
求最大值,ALL表示对所有求最大值,DISTINCT 只对不 同的求最大值,相同只取一个。
SQL> select max(sal)from emp;
MAX(SAL)
----------
5000
MIN( [ { DISTINCT | ALL}] )
求最小值,ALL表示对所有求最小值,DISTINCT 只对不同的求最小值,相同只取一个。
SQL> select min(sal)from emp;
MIN(SAL)
----------
800
STDDEV( [ { DISTINCT | ALL}] )
求标准差,ALL表示对所有求标准差,DISTINCT 只对不同的求标准差,相同只取一个。
SQL> selectstddev(sal) from emp;
STDDEV(SAL)
-----------------
1182.50322
VARIANCE( [ { DISTINCT | ALL}] )
求协方差,ALL表示对所有求协方差,DISTINCT 只对不同的求协方差,相同只取一个。
SQL> selectvariance(sal) from emp;
VARIANCE(SAL)
------------------------
1398313.87
§2.3.2 带 GROUP BY 的计算
可以用 GROUP By 来实现对一组数进行分组统计(如SUM,count(*)等),如:
要列出部门代码、部门人数,部门工资总和,则用到GROUP BY :
SQL> selectdeptno,count(*) ,sum(sal) from emp group by deptno;
DEPTNO COUNT(*) SUM(SAL)
---------- --------- ----------
10 3 8750
20 5 10875
30 6 9400
§2.3.3 用HAVING 来限制分组的计算
在分组GROUP BY 中,一般都不管 统计的结果是多少都要全显示,我们可以在GROUP BY 前或后加 HAVING 子句来限制结果的统计,比如要求被统计的人数有5个人以上,则有两方法可以实现:
SQL> selectdeptno,count(*) ,sum(sal) from emp group by deptno
2 having count(*)>=5;
DEPTNO COUNT(*) SUM(SAL)
---------- ---------- ---------
20 5 10875
30 6 9400
SQL> selectdeptno,count(*) ,sum(sal) from emp having count(*)>=5
2 groupby deptno;
DEPTNO COUNT(*) SUM(SAL)
---------- ---------- ----------
20 5 10875
§2.4 控制和格式化输出
§2.4.1 用 ORDER BY 子句来对结果进行排序
Oracle 提供ORDERBY 子句,可用于对查询到的结果进行排序输出。这样的操作是在内存中进行的。比如按照部门代码顺序和员工的工资多少顺序进行输出,需要命令:
SQL> selectdeptno,ename,sal from emp order by deptno,sal desc;
DEPTNO ENAME SAL
--------- ---------- ----------
10 KING 5000
10 CLARK 2450
10 MILLER 1300
20 SCOTT 3000
20 FORD 3000
20 JONES 2975
20 ADAMS 1100
20 SMITH 800
30 BLAKE 2850
30 ALLEN 1600
30 TURNER 1500
30 WARD 1250
30 MARTIN 1250
30 JAMES 950
这里的 DESC 表sal(工资) 按照降序排列。
§2.4.2 用 BREAK 命令 来对结果进行排列
一般可以用 BREAK ON column SKIP xx 来对查询结果进行排列,BREAK ON 命令的参数如下:
clear breaks 清除所有的 break 定义
break on column 在该列上中断
break on row 在每一行上中断
break on Page 在每一页上中断
break on report 在每一报告上中断
skip n 跳过n行
skip page 跳过未用完的页
使用方法请看下面例子:
SQL> break ondeptno
SQL> set pagesize100
SQL> selectdeptno,ename,sal from emp order by deptno;
DEPTNO ENAME SAL
---------- --------------------
10 CLARK 2450
KING 5000
MILLER 1300
20 SMITH 800
ADAMS 1100
FORD 3000
SCOTT 3000
JONES 2975
30 ALLEN 1600
BLAKE 2850
MARTIN 1250
JAMES 950
TURNER 1500
WARD 1250
已选择14行。
SQL> break ondeptno skip 2
SQL> selectdeptno,ename,sal from emp order by deptno;
DEPTNO ENAME SAL
-------------------- ----------
10 CLARK 2450
KING 5000
MILLER 1300
20 SMITH 800
ADAMS 1100
FORD 3000
SCOTT 3000
JONES 2975
30 ALLEN 1600
BLAKE 2850
MARTIN 1250
JAMES 950
TURNER 1500
WARD 1250
SQL> break ondeptno skip page
SQL> select deptno,ename,sal from emp order by deptno;
DEPTNO ENAME SAL
-------------------- ----------
10 CLARK 2450
KING 5000
MILLER 1300
DEPTNO ENAME SAL
-------------------- ----------
20 SMITH 800
ADAMS 1100
FORD 3000
SCOTT 3000
JONES 2975
DEPTNO ENAME SAL
-------------------- ----------
30 ALLEN 1600
BLAKE 2850
MARTIN 1250
JAMES 950
TURNER 1500
WARD 1250
已选择14行。
§2.4.3 用 COMPUTE 命令对结果进行格式化
COMPUTE 命令 的语法如下:
COMP[UTE] [function[LAB[EL] text] ...
OF {expr|column|alias} ...
ON {expr|column|alias|REPORT|ROW} ...]
function 可以是下面参数之一
AVG 数字类型平均值
COU[NT] 所有类型的个数
MIN[IMUM] NUMBER,CHAR,NCHAR, VARCHAR2(VARCHAR),
NVARCHAR2 (NCHARVARYI NG) 类型的最小值
MAX[IMUM] NUMBER,CHAR,NCHAR, VARCHAR2(VARCHAR),
NVARCHAR2 (NCHARVARYI NG) 类型的最大值
NUM[BER] 计算所有类型的行数
SUM 计算所有非空数字类型的总和
STD 计算数字类型的标准差
DEV[IANCE] 计算数字类型的协方差
LAB[EL] text
显示的字符串。用它可以替换掉字段的显示。
OF {expr|column|alias} ...
OF子串或表达式或别名
ON {expr|column|alias|REPORT|ROW} ...
ON子串或表达式或别名或REPORT或ROW
例子:
按照员工是 "clerk"、"analyst"、"analyst"及 "salesman"进行工资小计 ,并加标记"TOTAL", 则:
SQL> BREAK ON JOB SKIP 1
SQL> COMPUTE SUM LABEL ’TOTAL’ OF SAL ON JOB
SQL> SELECT JOB, ENAME, SAL
2 FROM EMP
3 WHERE JOB IN (’CLERK’, ’ANALYST’, ’SALESMAN’)
4 ORDER BY JOB, SAL;
The following output results:
JOB ENAME SAL
--------- ---------- ----------
ANALYST SCOTT 3000
FORD 3000
********* ----------
TOTAL 6000
CLERK SMITH 800
JAMES 950
ADAMS 1100
MILLER 1300
********* ----------
TOTAL 4150
SALESMAN WARD 1250
MARTIN 1250
TURNER 1500
ALLEN 1600
********* ----------
TOTAL 5600
计算工资小于 1,000 的总和:
SQL> COMPUTE SUM OF SAL ON REPORT
SQL> BREAK ON REPORT
SQL> COLUMN DUMMY HEADING ’’
SQL> SELECT ’ ’ DUMMY, SAL, EMPNO
2 FROM EMP
3 WHERE SAL < 1000
4 ORDER BY SAL;
SAL EMPNO
--- ---------- -----------
800 7369
950 7900
----------
sum 1750
计算平均和最大工资、部门:
SQL> BREAK ON DNAME SKIP 1
SQL> COMPUTE AVG LABEL ’Dept Average’ ->
MAX LABEL ’Dept Maximum’ ->
OF SAL ON DNAME
SQL> SELECT DNAME, ENAME, SAL
2 FROM DEPT, EMP
3 WHERE DEPT.DEPTNO = EMP.DEPTNO
4 AND DNAME IN (’ACCOUNTING’, ’SALES’)
5 ORDER BY DNAME;
DNAME ENAME SAL
-------------- ---------- ----------ACCOUNTING
CLARK 2450
KING 5000
MILLER 1300
************** ----------Dept
Average 2916.66667
Dept Maximum 5000
SALES ALLEN 1600
BLAKE 2850
MARTIN 1250
JAMES 950
TURNER 1500
WARD 1250
************** ----------Dept
Average 1566.66667
Dept Maximum 2850
9 rows selected.
计算部门10和20 的工资总和,不打印:
SQL> COLUMN DUMMY NOPRINT
SQL> COMPUTE SUM OF SAL ON DUMMY
SQL> BREAK ON DUMMY SKIP 1
SQL> SELECT DEPTNO DUMMY, DEPTNO, ENAME, SAL
2 FROM EMP
3 WHERE DEPTNO <= 20
4 ORDER BY DEPTNO;
DEPTNO ENAME SAL
---------- ---------- ----------
10 CLARK 2450
10 KING 5000
10 MILLER 1300
----------
8750
20 SMITH 800
20 ADAMS 1100
20 FORD 3000
20 SCOTT 3000
20 JONES 2975
----------
10875
8 rows selected.
在报告结束不打印计算工资总和:
SQL> COLUMN DUMMY NOPRINT
SQL> COMPUTE SUM OF SAL ON DUMMY
SQL> BREAK ON DUMMY
SQL> SELECT NULL DUMMY, DEPTNO, ENAME, SAL
2 FROM EMP
3 WHERE DEPTNO <= 20
4 ORDER BY DEPTNO;
DEPTNO ENAME SAL
---------- ---------- ----------
10 CLARK 2450
10 KING 5000
10 MILLER 1300
20 SMITH 800
20 ADAMS 1100
20 FORD 3000
20 SCOTT 3000
20 JONES 2975
----------
19625
8 rows selected.
§2.5 配置会话环境
一般在SQL>下进行SQLPLUS 操作,都需要进行必要的环境设置才能完成我们所需要的输出。所有环境的设置由 SET命令加相应的环境变量来完成。下面是常用的环境设置:
§2.5.1 ARRAYSIZE(取回的行数)
SET ARRAY[SIZE]{integer}
一次可以提取(Fetch)的行的数目,1->5000,当有较长字段时应设小些。
§2.5.2 AUTOCOMMIT(自动提交)
SET AUTO [COMMIT] {[ OFF | ON | IMM | n] }
用于在操作中是自动提交或是部分提交或是不自动提交。
1) ON或IMM使得在完成每条SQL语句时将未提交的改变立刻提交给数据库系统。
2) N 允许在用户发出COMMIT后,可以执行命令的数量(将n条SQL语句所做的改变进行提交)。
3) OFF 停止自动提交,用户必须用COMMIT命令才能被提交。
§2.5.3 LINESIZE(行显示宽度)
可以设置 LINESIZE 环境变量来控制行的显示宽度,缺省是80个字符。
SET Lin[esize]{80|integer}
Integer = 设置行宽度(字符个数),最大值999,如:
SQL>set linesize160
§2.5.4 LONG(长类型显示字节数)
在缺省的SQL> 状态下,SQL>缓冲区用于显示LONG 的字节数只有80个字符。如果我们需要查询的列中含有 LONG类型的字段的话,就需要将LONG缓冲区设置大些。
SET LONG{80|integer}
Integer 是 显示或拷贝long值的最大宽度,n=1->32767(但必须小于Maxdata值)
SQL>show Maxdata (最大行宽)
SQL>set long 2000
§2.5.5 PAGESIZE(页行数)
在缺省的SQL> 状态下,SQL>缓冲区显示页的行数是24行,其中22行显示数据,2行显示标题和横线。我们将pagesize 设置大些以减少提示标题和横线。
SET pag[esize] {24|integer}
SQL>SET pagesize66
§2.5.6 PAUSE(暂停)
可以设置 PAUSE 为ON 或OFF来控制屏幕显示。当设置为ON时,在select 语句发出后需要按Enter键才能显示一屏。
SET PAUSE [ ON | OFF]
SQL> set pause on
提示:在发出select 语句并按Enter键后 还要再按Enter键才能显示结果.
§2.5.7 SPACE(列间空格)
可用 set space 来设置各列间的空格数,语法为:
SET SPA[CE] {1|n}
N为设置输出行列间的空格数,最大为10。
SQL>set space 2
建议:在一般情况下,不用设置space参数。
§2.5.8 Termout (启/停屏幕显示)
TERMOUT用于设置在屏幕上显示或不显示所输出的信息。
SET TERMOUT { ON | OFF }
set termout off
set termout on
set termout off 常用SPOOL XXX时,即关闭报表在屏幕上的显示(节省时间)
set termout on 常用SPOOL off之后,即恢复报表在屏幕上的显示
§2.5.9 ECHO (启/停命令显示)
可以用ECHO命令来显示或不显示所执行的SQL命令。语法如:
SET ECHO{OFF|ON}
显示执行当中的各命令( 即用start 时)
set echo 受到 set termout 的影响
set pagesize 100
set echo on
select table_namefrom dict where rownum<20;
select * from catwhere rownum<30;
set echo off
--下面只显示结果不显示命令:
select table_namefrom dict where rownum<20;
select * from catwhere rownum<30;
§2.5.10 TRANSACTION(启动事务)
一个很重要的事务环境设置是TRANSACTION。它包括两个部分的内容:
SET TRANSACTION { READ ONLY | USE ROLLBACK SEGMENTsegment_name }
READ ONLY 是用于保证读的一致性。即其他用户的修改不影响当前查询结果。
USE ROLLBACK SEGMENT segment_name 是为当前所处理的事务指定专门的回滚段。这主要是在进行大量的Insert 或Delete 或Update时,需要一个大的回滚段以保证事务正常完成。详细见数据库管理员。
§2.5.11 SHOW ALL(列出所有参数)
可以用SHOW ALL 来显示当前的所有参数情况。它的用法很简单。比如:
SQL> show all
appinfo为ON并且已设置为"SQL*Plus"
arraysize 15
autocommit OFF
autoprint OFF
autorecovery OFF
autotrace OFF
blockterminator "."(hex 2e)
btitle OFF and 为下一条SELECT 语句的前几个字符
cmdsep OFF
colsep ""
compatibilityversion NATIVE
concat"." (hex 2e)
copycommit 0
COPYTYPECHECK 为 ON
define"&" (hex 26)
describe DEPTH 1LINENUM OFF INDENT ON
markup HTML OFFSPOOL OFF ENTMAP ON PREFORMAT OFF
echo OFF
editfile"afiedt.buf"
embedded OFF
escape OFF
flagger OFF
flush ON
heading ON
headsep"|" (hex 7c)
instance"local"
linesize 80
lno 14
loboffset 1
logsource""
long 80
longchunksize 80
newpage 1
null ""
numformat""
numwidth 10
pagesize 14
PAUSE 为OFF
pno 0
recsep WRAP
recsepchar "" (hex 20)
release 801070000
repfooter OFF and为 NULL
repheader OFF and为 NULL
serveroutput OFF
shiftinoutINVISIBLE
showmode OFF
spool OFF
sqlblanklines OFF
sqlcase MIXED
sqlcode 0
sqlcontinue"> "
sqlnumber ON
sqlprefix"#" (hex 23)
sqlprompt"SQL> "
sqlterminator";" (hex 3b)
suffix"sql"
tab ON
termout ON
time OFF
timing OFF
trimout ON
trimspool OFF
ttitle OFF and 为下一条SELECT 语句的前几个字符
underline"-" (hex 2d)
USER 为"SYS"
verify ON
wrap : 行将为已换行
SQL>
你可以从上面的参数中看到其当前值,也可以修改某些参数的值。
§2.6 格式化输出
§2.6.1 一般数据的格式化输出
在Oracle的SQL> 下,经常用COLUMN命令来对所输出的列进行格式化,即按照一定的格式进行显示。COLMUN命令语法如下:
COL[UMN] [{ column | expr } [ option_1 ... option_n ] ]
column :列名
expr :有效的 SQL 表达式
option_1... option_n:可以是下列之一:
ALI[AS] alias
CLE[AR]
FOLD_A[FTER]
FOLD_B[EFORE]
FOR[MAT] format
HEA[DING] text
JUS[TIFY] {L[EFT]|C[ENTER]|C[ENTRE]|R[IGHT]}
LIKE {expr|alias}
NEWL[INE]
NEW_V[ALUE] variable
NOPRI[NT]|PRI[NT]
NUL[L] text
OLD_V[ALUE] variable
ON|OFF
WRA[PPED]|WOR[D_WRAPPED]|TRU[NCATED]
下面给出常用的关键字的解释:
Alias 给出列的别名,BREAK和COUMN可以引用所定义的别名。
CLEAR 取消列的定义。
FORMAT 列显示格式,format为:
9999990 9或0的个数决定最多显示多少位
9,999,999.99 按照逗号和小数点来 显示数据,若是0以空格显示
099999 显示前面补0
$999,999.99 数字前加美圆号
B99999 若为0 ,则结果为空白
99999Mi 若数字为负,则负号放在数字后(右边),缺省放在左边
99999PR 负号将以括号括起
9.999EEEE 以科学记数法表示(必须有4个E)
999V99 数字乘以 10n ,如 1234变为 123400
DATE 采用日期数字格式(MM/DD/YY)
Heading 重新标记列的显示标题,如:
SQL> col ename heading 姓名 format a10
SQL> selectename,sal from emp;
例子:
SQL
COLUMN SALARY FOR $9,999,999.99
COLUMN LAST_NAME FOR A35.
§2.6.2 日期的格式化输出
Oracle系统提供了一个 NLS_DATE_FORMAT的环境变量来设置日期的显示格式。用它可以完成按照不同格式要求的显示,比如按照中国的习惯为 yyyy年mm月dd日等。
1.系统日期sysdate 的显示
用sysdate 可以显示ORACLE RDBMS 所在机器的日期及时间,如:
SQL> altersession set nls_date_format ='"公元"yyyy"年"mm"月"dd"日"';
会话已更改。
SQL> selectsysdate from dual;
SYSDATE
------------------
公元2001年05月30日
2.日期类型的显示
selectsysdate,to_char(sysdate,’yyyy.mm.dd hh24:mi;ss’)from dual;
SQL>connect scott/tiger
已连接。
SQL>alter session set nls_date_format ='yyyy"年"mm"月"dd"日生"';
会话已更改。
SQL>col HIREDATE heading 生日
SQL>col sal heading 工资
SQL>col sal ename 姓名
SQL>select ename,sal,hiredate from emp;
姓名 工资 生日
---------- ---------- -----------------
SMITH 800 1980年12月17日生
ALLEN 1600 1981年02月20日生
WARD 1250 1981年02月22日生
JONES 2975 1981年04月02日生
MARTIN 1250 1981年09月28日生
BLAKE 2850 1981年05月01日生
CLARK 2450 1981年06月09日生
SCOTT 3000 1987年04月19日生
KING 5000 1981年11月17日生
ADAMS 1100 1987年05月23日生
JAMES 950 1981年12月03日生
FORD 3000 1981年12月03日生
MILLER 1300 1982年01月23日生
已选择14行。
§2.7 加标题
有时在输出一些结果时,可能需要加一些标题,如表上面的顶标题,落款等。这样的要求可由Ttitle和Btitle来完成。
ttitle和btitle
ttitle [center|left|right]string 顶标题
btitle [center|left|right]string 底标题
ttitle center 'XX公司人员情况表'
btitle left '制表人:xxxx' right '日期:xxxx年xx月'
Clear ttitle
§2.8 建立简单报告
我们可以用TTITLE、BTITLE、COLUMN、BREAKON、COMPUTE SUM及SET LINESIZE、SET PAGESIZE、SETNEWPAGE来设置查询结果的显示格式;在用 SPOOL 命令将显示结果输出到一个操作系统文件中去,一般输出文件的类型为.LST。
建立简单报告主要使用下面命令来实现:
1. SPOOL命令
SPOOL filename 将缓冲区的内容写到文件中
SPOOL off 终止写命令
2. COLUMN 命令
column col_name[,heading] format format_spe
把字段的结果指定为一种输出格式
COL name heading '姓名' for a10
COL sal heading '工资' for 9,999.99
3.ttitle、btitle
ttitle [center|left|right]string 顶标题
btitle [center|left|right]string 底标题
ttitle center 'XX公司人员情况表'
btitle left '制表人:赵元杰' right '日期:1998.11月'
Clear ttitle
4.break、compute
clear breaks,clear computes
break on column 在该列上中断
break on row 在每一行上中断
break on Page
break on report
skip n 跳过n行
skip page 跳过未用完的页
compute avg
compute count
compute max
compute min
compute std
compute sum
compute var
compute num 计算所有行
compute sum of sal on deptno
5.set 在报表中的设置
l set termout off、set termout on 命令
l set termout off 常用SPOOL XXX前,即关闭报表在屏幕上的显示(节省时间)
l set termout on 常用SPOOL off之后,即恢复报表在屏幕上的显示
l set ECHO{OFF|ON} 显示执行当中的各命令(即用start 时),set echo 受到 set termout 的影响
l set Lin[esize]{80|integer} 设置行宽度,最大值999
l set pag[esize] {24|integer} 设置页的大小
例子:
SQL>COL enameheading ‘姓名’ for a12
SQL>COL sal heading ‘工资’for a999,999.99
SQL>COLhiredate heading ‘出生’
SQL>SET LINESIZE 200
SQL>SET PAGESIZE 60
SQL>SPOOL c:\all_emp
SQL>selectename,sal,deptno,hiredate from emp order by deptno;
SQL>SPOOL OFF
§2.9 输入变量
Oracle提供一种在处理SQL语句时可以将参数作为变量来对待的技术,即在条件句中可以是变量而不是具体的值,这样的处理就是输入变量。这样做的目的就是可以重复使用同样的语句,每次只要输入相应的值即可。要实现将参数写成为变量,只要在变量前加一个&号即可。看下面语句:
Select sid, serial#,username, command from v$session
Where USERNAME = upper(‘&usr’);
这样的语句在运行中,系统会自动提示你回答变量的具体值,上面语句运行时提示和回答时显示的信息如下:
SQL> Select sid, serial#,username, command from v$session
2 Where USERNAME = upper('&usr');
输入 usr 的值: sys
原值 2: Where USERNAME = upper('&usr')
新值 2: Where USERNAME = upper('sys')
SID SERIAL# USERNAME COMMAND
---------- ---------------------------------------- ----------
7 26 SYS 3
在变量说明中,可以使用多个变量,比如:
Alter systemkill session ‘&sid,&ser’;
或
Alter systemkill session ‘&会话号,&序列号’;
它的运行情况如下:
SQL> Select sid,serial#,username, command from v$session;
SID SERIAL# USERNAME COMMAND
---------- ---------- ------------------------------ ----------
1 1 0
2 1 0
3 1 0
4 1 0
5 1 0
6 1 0
7 26 SYS 3
8 16 ZHAO 0
已选择8行。
SQL> Alter system kill session '&sid,&ser';
输入 sid 的值: 8
输入 ser 的值: 16
原值 1: Alter system kill session'&sid,&ser'
新值 1: Alter system kill session '8,16'
系统已更改。
一般系统缺省下是使用 “&”符号来定义变量,你也可以使用另外的符号来代替,比如不喜欢用 & 而要用 ?,则有:
SQL> set define ?
SQL> selectsid,serial#,username from v$session where username='?usr';
输入 usr 的值: SYS
原值 1: select sid,serial#,username fromv$session where username='?usr'
新值 1: select sid,serial#,username fromv$session where username='SYS'
SID SERIAL# USERNAME
-------------------- ------------------------------
7 26 SYS
次时由于我们修改了会话环境的参数值,可以用下面命令查看:
SQL> show define
define "?"(hex 3f)
第三章 表及索引的定义操作
在ORACLE数据库系统中,表是数据库的基本对象,与桌面数据库中的文件类似,我们可以把所有的基本实体都看成为表,不管应用中的表有多复杂,都可以使用(拆成)一个或多个表来表示。用以存放实体的数据。下面针对建表所需要的知识作简单的介绍。
§3.1 建立表结构
建立表结构是每个应用系统都必须进行的工作。由于建立表结构是一项统一规划和统一设计的工作。应该是由总设计师根据用户的具体应用需要来定。表的设计是否合理关系到应用系统将来的成败与性能问题。因此,任何担当总设计师角色的人都不要轻视这项工作。
§3.1.1 建立表结构命令
由于创建表的命令非常长,这里仅给出一些主要的部分,详细的请参考《ORACLE8i SQL REFERENCE》 。
CREATE TABLE 命令简要语法如下:
CREATE TABLE [USER.]table_name
( { COLUMN1 DATATYPE
[DEFAULT EXPN][COLUMN_CONSTRAINT] |TABLE_CONSTRAINT }
[, { COLUMN1DATATYPE
[DEFAULT EXPN][COLUMN_CONSTRAINT] |
TABLE_CONSTRAINT }]... )
[CLUSTER CLUSTER(COLUMN1 [,COLUMN2] ...) ]
[PCTFREE N]
[PCTUSED N]
[INITRANS N]
[MAXTRANS N]
[STORAGE N]
[TABLESPACETABLESPACE]
[ ENABLE | DISABLE]
[ AS QUERY]
其中:
schema 包括基表的模式(缺省:当前用户的帐号)
table_name 表名
column 列名(字段名),ORACLE7最多254列,ORACLE8可达1000个列。
DEFAULT 当前列的缺省值(常数)
Columnconstraint 列约束
Table_constraint 表约束
PCTFREE 用于更新(update)的空间百分比(1-99)
0表示在插入时完全填满数据块,缺省为10
PCTUSED 为表的每个数据块保留的可用空间的最小百分比. 取值1-99,缺省为40。
PCTFREE和PCTUSED的组合决定了将插入的数据放入已存在的数据块还是放入一个新的块中。
INITRANS 指定一个在每一个数据块中分配的事务入口的初始数1-255,
缺省为1,每一个更新块的事务都需要在块中有一个事务入口
(大小依OS),一般不需要指此参。
MAXTRANS 指定用于更新分配给表的数据块的并发事务的最大数,1-
255,用户一般不应改此参。
TABLESPACE 表空间。如果缺省则表建在用户缺省的表空间(如果建立用户不指定表空间
则该用户的缺省表空间为system)。
STORAGE 存储分配参数
INITIAL integer 初始大小
NEXT integer 下一次的大小
MINEXTENTS integer 最小分配次数
MAXEXTENTS integer 最大分配次数
PCTINCREASE integer 增长百分比(>=0)
ENABLE 激活完整性约束
DISABLE 取消完整性约束
As subquery 建表中查出数据给新表,此语句如果使用,则表的数据类型不需指定,
而是继承原表的类型。
FREELIST GROUP 在并行服务器中指定表或分类、索引组的列表数目。
FREEUST 在并行服务器中指定表、簇、索引的列表数。
提示1:
一般情况下,如果表含有long字段,这样势必需大量的空间,系统会在每次插入新记录时,经常分配空间给表,不久就会出现:
"ORA-01547:Failed to allocate extent of size xxxxx in
tablespace 'xxxx' "
此种情况如果表空间还剩较多的连续空间的话。则可能是该表分配的空间次数已达最大值。为了对该表能插入新数据,需对该表的存储参数作修改,比如:
SQL>alter table xxx storage(MAXEXTENTS 999 );
提示2:建议不要对表结构或索引使用 pctincrease大于0的参数以避免将来在运行中产生空间超支问题.
提示3:建立表结构最重要的部分是存储参数(STORAGE)的说明。设置者要特别重视存储参数的估计,设置合理的大小。详细见〈Oracle8i/9i 初级数据库管理〉
§3.1.2 建立表结构例子
例1:在SCOTT模式下建立表emp,并指定表空间和存储参数:
Create table scott.emp
(
Empno number(5) primary key,
Ename varchar2(15) not null,
Job varchar2(10),
Mgr number(5),
Hiredate date default sysdate,
Sal number(7,2) CHECK(sal>100),
Comm number(3) default 0.0 ,
Dept number constraint
dept_fkey References scott.dept
)
Tablespace users
PCTFREE 10
PCTUSED 70
STORAGE
(
INITIAL 50K
NEXT 50k
MAXEXTENTS 10
);
例2:在建立表过程中对有限制的列使用NOTNULL:
CREATE TABLECHECKUP_HISTORY
(CHECKUP_NONUMBER(10,0) NOT NULL,
ID_NO NUMBER(10,0),
CHECKUP_TYPEVARCHAR2(30),
CHECKUP_DATE DATE,
DOCTOR_NAMEVARCHAR2(50));
本例除了要求CHECKUP_NO非空外,其它无任何限制.
例3:在建立表时指定列CHECKUP_TYPE为外部列:
CREATE TABLESEAPARK.CHECKUP_HISTORY
(
CHECKUP_NONUMBER(10) NOT NULL,
ID_NO NUMBER(10,0),
CHECKUP_TYPEVARCHAR2(30),
CHECKUP_DATE DATE,
DOCTOR_NAMEVARCHAR2(50),
FOREIGN KEY(CHECKUP_TYPE) REFERENCES
SEAPARK.CHECKUP(CHECKUP_TYPE),
PRIMARY KEY(CHECKUP_NO)
)
PCTFREE 20
PCTUSED 60
INITRANS 2
MAXTRANS 255
STORAGE ( INITIAL1250K
NEXT 2K
MINEXTENTS 1
MAXEXTENTS 121
Pctincrease 0)
TABLESPACE user_data;
例子指定了所有者,主键,外部键,表空间及存储参数等,主键和外部键在后面章节介绍。
§3.1.3 建立临时表结构
Oracle现在可以使用 CREATE GLOBAL TEMPORARY TABLE命令来实现建立临时表结构。这样的表它的数据只在用户会话期间存在,当会话完成后就自动清除。看下面例子:
SQL> create global temporary table myemp as select * fromemp;
表已创建。
SQL> desc myemp
名称 空? 类型
------------------------------------------------- --------------
ENAME VARCHAR2(20)
SAL NUMBER(9,2)
DEPTNO NUMBER(4)
TEL VARCHAR2(20)
SQL> select *from myemp;
未选定行
SQL> insert intomyemp values('赵元杰',32456.99,10,'12');
已创建 1 行。
SQL> select *from myemp;
ENAME SAL DEPTNO TEL
------------------------------ ---------- ------------------
赵元杰 32456.99 10 12
SQL> connectsys/sys
已连接。
SQL> connectzhao/zhao
已连接。
SQL> l
1* select * from myemp
SQL> /
未选定行
从上面可看出当连接到SYS在连接回来后数据就不存在了。对于临时表,可以用DROP TABLE来删除其结构。如:
SQL> drop tablemyemp;
表已丢弃。
§3.3 修改表结构
修改表结构是对已经创建完成(实际是存放在数据库字典里)的表的结构进行修改。不同的Oracle版本允许对表的修改也不一样。新版的Oracle8i可以对表中的列进行删除。
§3.3.1 修改表结构命令
修改表结构的命令由ALTERTABLE来完成。该命令的参数较多,下面仅给出一些基本的部分。详细请参考《ORACLE8i SQLREFERENCE》 。
ALTER TABLE [user.] table
[ADD ({colum_element|table_constraint}
[,{column_element|table_constraint}]...)]
[MODIFY(column_element[,column_element]...)]
[DROP CONSTRAINT constraint]...
[PCTFREE integer][PCTUSED integer]
[INITRANS integer][MAXTRANS integer]
[STORAGE storage]
[BACKUP]
ALTER TABLE 可以作的操作有:
l 增加一个列(字段)宽度;
l 减少一个列(字段)宽度(该列必须无数据);
l 增加一个列(字段);
l 或一个限制;(如数据类型,NOT NULL);仅当某列的值为空时才能修改其类型;
l 去掉限制;
l 修改存储分配;
l 记录表已作过BACKUP;
l 删除已存在的列(仅Oracle8i及以后版本);
l 重新定位和组织表(仅Oracle8i及以后版本);
l 将表标识为不可用(仅Oracle8i及以后版本)。
§3.3.2 修改表结构例子
例1:对已经存在的表增加一新的列:
SQL>alter table dept add ( headcount number(3) );
例2:对表的列修改其大小:
SQL>alter table dept modify( Dname char(20) );
如果被修改的列没有空(已有数据),则被提示:
ORA-01439: Column tobe modified must be empty to change
datatype
ORA-01441: Column tobe modified must be empty to decrease
column length
例3:复制一个表:
CREATE TABLE HOLD_TANK AS SELECT TANK_NO, CHIEF_CARETAKER_NAME
FROM TANK;
例4:参照某个已存在的表建立一个表结构(不需要数据)
create table emp2 asselect * from emp where rownum<1;
例5:修改已存在表存储参数:
Alter table emp2 storage( next 256k pctincrease 0 );
例6:删除表中的列:
这是Oracle8i的新功能,它的基本语法为:
ALTER TABLE . .. . . . DROP COLUMN [ CASCADECONSTRAINTS ];
如:
Alter table emp drop column comm ;
例7:重新定位和组织表:
这是Oracle8i的新功能,可以实现:
l 将未分区的 表从一个表空间移到另一个表空间;
l 重新组织一个未分区表的存储。
它的基本语法为:
ALTER TABLE . .. . . . MOVE TABLESPACE ;
如:
Alter table emp move tablespace users;
例8:将表标识为不可用:
这是Oracle8i的新功能,可以实现对空间的收回等。
基本语法为:
ALTER TABLE . .. . . . SET UNUSED COLUMN;
如:
Alter table emp set UNUSED COLUMN xyz;
提示:虽然Oracle允许用户对表的结构进行修改。但建议你在工作中不要采用方式。因为表结构被多次修改会影响应用系统的性能。
§3.3.3 删除表结构
Oracle提供DROP TABLE命令可以实现删除表数据和结构。提醒初学者,不要轻易使用DROP TABLE命令。DROP TABLE 命令语法:
DROP TABLE[user.]table_name[CASCADE CONSTRAINTS]
CASCADE CONSTRAINTS表示所有指向本表的主键,外部键被删掉。当删除一个表时,下面的对象也随之被删掉。
l 表的索引;
l 指向本表的外部键;
l 本表的触发器;
l 本表中的分区;
l 本表的角色和用户权限;
l 加在本表的所有限制。
提示:如果你在定义表结构时,采用了主键、外部键来定义了一序列表。则在删除表结构时要小心。不要轻易用CASCADE子句。
§3.3.4 使用CHECK作限制约束
Oracle提供了一个很有用的子句CHECK,它可以实现对数据的自动检查。它的用法是在创建表结构时使用。如:
Create table worker
( empno number(4) primary key,
name varchar2(10),
age number(2) CHECK(age between 18 and 65 ),
/* age number(2) CHECK( age >=18 and age<=65 ) */
lodging char(15) References LODGING(lodging)
);
Create table emp3
( empno number(4) constraint abc primary key,
ename varchar2(10),
job varchar2(10),
sex char(2) check ( sex=‘男’ or sex= ’女’),
mgr number(4),
hiredate date,
sal number(7,2), /* 工资 */
comm number(7,2), /* 奖金 */
deptno number(2),
CHECK ( sal+comm >0 and sal+comm<=5000)
);
建议:在设计数据库表结构时,建议你分析用户的数据的取值范围,从而将那些取值范围一定的字段用CHECK进行描述。以保证以后数据的正确性。
§3.3.5 使用UNRECOVERABLE创建表
对于特殊的需要,可以考虑将表创建成为不需恢复(UNRECOVERABLE)的表。如复制一个已存在的表就可以采用这种方法以减少系统的开销。如:
例:参考emp表创建一个新的emp_new 表:
SQL> create table new_emp as select * from emp UNRECOVERABLE;
表已创建。
或
CREATE TABLEnew_emp AS select * from emp NOLOGGING;
注:虽然上面提到UNRECOVERABLE,但是Oracle推荐你使用NOLOGGING或LOGGING;
§3.3.6 将表移动到新的数据段或新的表空间
最新的Oracle8i 版本可以用Alter table … MOVE语句将表移动到一个新的段或新表空间上,这样可以实现对不合理存储参数进行修改,包括用一般的ALTER TABLE 不能修改的参数。如:
例1:通过移动来实现存储参数的修改:
STORAGE(INITIAL 1m next 512k minextents 1 maxextents 999 pctincrease 0 );
例2:将那些使用system表空间 的对象移动到合适的表空间中:
1)移动前表所使用的表空间情况:
SQL> selecttablespace_name,table_name,initial_extent from user_tables;
TABLESPACE_N TABLE_NAME INITIAL_EXTENT
---------------------- ----------------------- ----------------------------
SYSTEM ABC 65536
SYSTEM BONUS 65536
SYSTEM DEPT 65536
SYSTEM EMP 65536
SYSTEM EMP2 65536
SYSTEM EMP3 65536
SYSTEM EMP4 65536
USERS PAY_LST_DET 1048576
SYSTEM PLAN_TABLE 65536
SYSTEM SALGRADE 65536
USERS UNIT_INF 1048576
11 rows selected.
2)用Alter table . . . MOVE 语句对表进行移动,下面例子对表进行移动并重新指定存储参数。
SQL> altertable emp move tablespace user_data
2 storage(initial 128k next 128k minextents 1 pctincrease 0);
Table altered.
SQL> altertable dept move tablespace user_data
2 storage(initial 128k next 128k minextents 1 pctincrease 0);
Table altered.
SQL> alter tableBONUS move tablespace user_data
2 storage(initial 128k next 128k minextents 1 pctincrease 0);
Table altered.
3)移动后的表及表空间的情况:
SQL> selecttablespace_name,table_name,initial_extent from user_tables;
TABLESPACE_N TABLE_NAME INITIAL_EXTENT
------------------------------------------------ ---------------------------
SYSTEM ABC 65536
USER_DATA BONUS 131072
USER_DATA DEPT 131072
USER_DATA EMP 131072
SYSTEM EMP2 65536
SYSTEM EMP3 65536
SYSTEM EMP4 65536
USERS PAY_LST_DET 1048576
SYSTEM PLAN_TABLE 65536
SYSTEM SALGRADE 65536
USERS UNIT_INF 1048576
11 rows selected.
§3.3.7 手工分配表的存储空间
使用ALTER TABLE 加ALLOCATEEXTENT 选项来实现分配一个指定的空间。如:
ALTERTABLE emp
ALLOCATE EXTENT (SIZE 5K INSTANCE4);
§3.3.8 标记不使用的列和删除不使用的列
前面介绍过,新版的Oracle8I可以删除某个 列。如:
例:从LONG_TAB表中将LONG_PICS列删除掉:
ALTER TABLE LONG_TAB DROP COLUMN LONG_PICS;
可以使用 ALTER TABLE . . . SET UNUSED 语句实现将表中的列设置为不用的状态以达到快速处理的目的。其结果是:
1)在显示结果时看不到该列;
2)不删除该列的数据(但可以将该列删掉);
例:
SQL> select *from emp;
EMPNOENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ------------ ----------------------------- ---------------- ------------ ----------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
10 rows selected.
SQL> altertable emp2 set unused(comm);
Table altered.
SQL> select *from emp;
EMPNO ENAME JOB MGR HIREDATE SAL DEPTNO
------------ ----------- ------------------------- --------------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 30
10 rows selected.
SQL> desc emp
Name Null? Type
------------------------------------------------------------------------- ------------
EMPNO NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
DEPTNO NUMBER(2)
可以用ALTER TABLE . . .DROP UNUSED COLUMNS 来删除不使用的列。它可以在物理上删除表的未使用的列,并重新声明磁盘空间。在使用该语句时可以在后面加上checkpoint 检查点关键字。它可以产生一个检查点。使用检查点可以在删除数据列的操作过程中减少恢复日志的容量积累,从而避免回滚段的空间消耗。
如:
SQL> altertable emp drop unused columns checkpoint;
Table altered.
注意:删除表中未使用列时不需要指定列名,它是根据alter table emp set unused(comm); 语句来删除未使用的列。
§3.3 主键
ORACLE 系统提供一个关键字 primary key 来建立一个主键。所谓主键,就是在一个表内该列具有唯一的值。一旦你为一个表的一列或几个列建立了主键,则ORACLE就自动为该表建立一个唯一索引。
§3.3.1 创建主键
要想为表的某个列建立主键,可以用Alter table 或 CREATE TABLE 命令完成。
1在建表结构中创建主键
CREATE TABLE[schema.]table_name …
[SCOPE IS[user.]scope_table_name][column_constraint]…
. . . . . .
例1:
CREATE TABLE dept
(deptno number(2),
dname varchar2(20),
loc varchar2(20),
CONSTRAINT pk_deptPRIMARY KEY (deptno)
);
2 用alter table 创建主键
ALTER TABLE[schema.]tablename
ADD (constraint_name PRIMARY KEY (column1 [,column2,...])
例2:
ALTER TABLEPARK_REVENUE
ADD(park_rev_pk PRIMARY KEY ( ACCOUNT_NO));
例3:
create table dept
(deptno number(5) primary key,
dname varchar2(20),
loc varchar2(30))
disable primary key;
注:当主键被说明为 disable primary key 时,不能建立相应的外部键。一定先用:
altertable dept enable primary key 后方可使用:
deptno constraint fk_deptnoReferences dept(deptno)
3唯一索引和主键区别
唯一索引:唯一索引使用CREATE UNIQUE INDEX命令完成,能标识数据库表中一行的关键字。在数据字典中建立了唯一索引名字。
主 键:主键使用primary key来指定,能标识数据库表中一行的关键字。在数据字典中也建立了唯一索引名字。
差 别:被定义为唯一索引的列可以空,而被定义为主键的列不能空。
4建立索引、主键的方法:
在建表命令中用Constraint说明详见《oracle8i sever SQL Reference》或者 Help constraint得到语法说明。
Create table dept
(deptno number(2),
dname varchar2(40) constraint unq_dname unique,
loc varchar2(50)
);
constraint unq_dname unique可以允许dname没有值,这样未指定空间分配参数的语句,oracle 采用缺省参数为 unq_dname 分配空间.
同样可以用下面命令达到如上的效果:
Create table dept
(deptno number(2),
dname varchar2(20),
loc varchar2(20),
constraint unq_dname unique(dnam)
using index pctfree 20
tablespace users_x
STORAGE(initial 8K next 6k)
);
constraint unq_dname unique可以允许dname没有值,并指定空间分配参数的语句,oracle将根据参数为 unq_dname 分配空间.
Create table dept
(dept number(2) constraint pk_dept primary key,
dname varchar2(20),
loc varchar2(20)
);
同样可以用下面命令完成上面的说明:
Create table dept
(deptno number(2),
dname varchar2(20),
loc varchar2(20),
constraint pk_dept primary key (deptno)
);
建立完表结构后再建索引、主键:
优点:索引可以放在另一表空间中,如果在表中直接写,则必须用using index 说明分配大小、表空间等。
Alter table ship_cont
Add primary key(ship_no,container_no) Disable
一般声明主键时,可以让其有效(缺省),也可以使其无效(Disable)
建议:在程序开发调试中,经常先将主键设为Disable,上面的ship_no,container_no一起组成主键,这种两个以上的字段组成的叫组合键(最多16个字段),不要指定过多的组合键以避免出现性能下降.
§3.3.2 改变主键
命令语法详见 altertable 命令
限制: 不许修改作为主键的列;
不许修改作为主键的名字
可以: 可以定义一主键;或使主键无效
ALTER TABLE[schema.]tablename
DISABLEconstraint_name
例1:
Alter table dept
Disable scott.pk_dept;
这样,如果有一外部键依赖于该主键,则系统给出下列错误:
ORA-02297: Cannot disable constraint (scott.pk_dept)-
Depentencies exist.
在这种情况下,必须先删掉依赖于该主键的外部键并使该外部键无效,然后查才能使主键无效。
§3.3.3 删除主键
ALTER TABLE[schema.]tablename
DROP CONSTRAINTconstraint_name [CASCADE]
删除顺序:
1) 使该外部键无效,删掉依赖于该主键的外部键;
2) 使该主键无效,删掉该主键
或:
当在删掉主键命令后加参数CASCADE , 则在删掉主键的同时把依赖于该主键的外部键一起删掉。
DROP INDEX index_name ;
§3.4 外部键
建立外部键是保证完整性约束的一种唯一方法,也是关系数据库的精髓所在。许多曾使用过桌面数据库(如Dbase, Foxpro )的软件人员不太习惯或不使用关系数据库的主键与外部键的方法来设计自己的数据库结构,这是很不好的方法。应该在使用关系数据库中将习惯改过来。否则设计的应用结构和效率就不可能达到用户的要求。
§3.4.1 建立外部键
外部键的建立与主键的建立类似,都可以在 CREATE TABLE命令或ALTER TABLE命令中来说明,详细语法见《Oracle SQL reference 》Create table 命令9和Altertable 命令。
1.在 CREATE TABLE 命令语句中建立外部键
2.用 ALTER TABLE 命令语句建立外部键
ALTER TABLE [schema.]table_name
ADD ( CONSTRAINT_NAME FOREIGN KEY (Column1 [,column2,. . .])
REFERENCES [schema.]table_name (Column1 [,column2,. . .]);
CREATE TABLE dept
(deptno NUMBER(2),
dname VARCHAR2(9),
loc VARCHAR2(10),
CONSTRAINT pk_dept PRIMARY KEY (deptno) );
Create table emp
( empno number(4),
ename varchar2(10),
job varchar2(10),
mgr number(4),
hiredate date,
sal number(7,2),
comm number(7,2),
deptno constraint fk_deptno References dept(deptno)
);
同样下面语句的效果与上面一样:
Create table emp
( empno number(4),
ename varchar2(10),
job varchar2(10),
mgr number(4),
hiredate date,
sal number(7,2),
comm number(7,2),
deptno,
constraint fk_deptno
Foreign key(deptno) References dept(deptno)
);
使用Delete cascade管理引用完整性
Create table emp
( empno number(4),
ename varchar2(10),
job varchar2(10),
mgr number(4),
hiredate date,
sal number(7,2),
comm number(7,2),
deptno number(2) constraint fk_deptno
References dept(deptno)
On Delete CASCADE
);
常用组合键的完整性约束:
Alter table phone_calls
ADD constraint fk_areaco_phoneno
Foreign key(areaco,phoneno)
References austomers(areano,phoneno)
Exceptions into wrong_numbers
Create table TROUBLE
( City varchar2(10),
Sampledate date,
Noon number(4,1),
Midnight number(4,1),
Precipitation number,
Constraint TROUBLE.PK PRIMARY KEY(city,Sampledate)
);
§3.4.2 修改外部键
由于ORACLE不允许改变已被定义的外部键的列,也不允许改变外部键的名字,所以你只能用ALTER TABLE 定义一个新的外部键或者使一个已存在的外部键无效:
ALTER TABLE[schema.]table_name
DISABLECONSTRAINT_NAME;
§3.4.3 删除外部键
要删除已定义的外部键,要用Alter table 命令中的 DROP 关键字来实现,命令语法见Alter table 命令。
ALTER TABLE [schema.]table_name
DROP CONSTRAINTconstraint_name;
提示:关系数据库的核心主要体现在主键和外部键上。在进行数据库结构设计时,建议要采用主键和外部键来定义那些有关系的表。这样可以保证应用系统数据的完整性和一致性。
§3.5 索引
索引是关系数据库中用于存放每一条记录的一种对象,主要目的是加快数据的读取速度和完整性检查。建立索引是一项技术性要求高的工作。一般在数据库设计阶段的与数据库结构一道考虑。应用系统的性能直接与索引的合理直接有关。下面给出建立索引的方法和要点。
§3.5.1 建立索引
1. CREATE INDEX命令语法:
CREATE INDEX
CREATE [unique]INDEX [user.]index
ON [user.]table(column [ASC | DESC] [,column
[ASC | DESC] ] ... )
[CLUSTER[scheam.]cluster]
[INITRANS n]
[MAXTRANS n]
[PCTFREE n]
[STORAGE storage]
[TABLESPACEtablespace]
[NO SORT]
Advanced
其中:
schema ORACLE模式,缺省即为当前帐户
index 索引名
table 创建索引的基表名
column 基表中的列名,一个索引最多有16列,long列、long raw
列不能建索引列
DESC、ASC 缺省为ASC即升序排序
CLUSTER 指定一个聚簇(Hash cluster不能建索引)
INITRANS、MAXTRANS 指定初始和最大事务入口数
Tablespace 表空间名
STORAGE 存储参数,同create table 中的storage.
PCTFREE 索引数据块空闲空间的百分比(不能指定pctused)
NOSORT 不(能)排序(存储时就已按升序,所以指出不再排序)
2.建立索引的目的:
建立索引的目的是:
l 提高对表的查询速度;
l 对表有关列的取值进行检查。
但是,对表进行insert,update,delete处理时,由于要表的存放位置记录到索引项中而会降低一些速度。
注意:一个基表不能建太多的索引;
空值不能被索引
只有唯一索引才真正提高速度,一般的索引只能提高30%左右。
Create index ename_in on emp (ename,sal);
例1:商场的商品库表结构如下,我们为该表的商品代码建立一唯一索引,使得在前台POS收款时提高查询速度。
Create table good(good_id number(8) not null,/* 商品条码 */
Good_desc varchar2(40), /* 商品描述 */
Unit_cost number(10,2) /* 单价 */
Good_unit varchar2(6), /* 单位 */
Unit_pric number(10,2) /* 零售价 */
);
注:提高查询速度的方法还有在表上建立主键,主键与唯一索引的差别
在于唯一索引可以空,主键为非空,比如:
Create table good(good_id number(8)primary key,
Good_desc Varchar2(40),
Unit_cost number(10,2),
Good_unit char(6),
Unit_pric number(10,2)
);
§3.5.2 修改索引
对于较早的Oracle版本,修改索引的主要任务是修改已存在索引的存储参数适应增长的需要或者重新建立索引。而Oracle8I及以后的版本,可以对无用的空间进行合并。这些的工作主要是由管理员来完成。
简要语法结构如下,更详细的语法图见电子文档《Oracle8i Reference 》 中的 Alter index.
ALTER [UNIQUE] INDEX[user.]index
[INITRANS n]
[MAXTRANS n]
REBUILD
[STORAGE n]
REBUILD 是 根据原来的索引结构重新建立索引,实际是删除原来的索引后再重新建立。
提示:DBA经常用 REBUILD 来重建索引可以减少硬盘碎片和提高应用系统的性能。
例:
alter index pk_detnorebuild storage(initial 1m next 512k);
ALTER INDEX emp_ixREBUILD REVERSE;
Oracle8i 的新功能可以对索引的无用空间进行合并,它由下面命令完成:
ALTER INDEX . . . COALESCE;
例如:
ALTER INDEX ename_idx COALESCE;
§3.5.3 删除索引
当不需要时可以将索引删除以释放出硬盘空间。命令如下:
DROP INDEX[schema.]indexname
例如:
sql> drop indexpk_dept;
注:当表结构被删除时,有其相关的所有索引也随之被删除。
§3.6 新索引类型
Oracle8i为了性能优化而提供新的创建新类型的索引。这些新索引在下面介绍:
§3.6.1 基于函数的索引
基于函数的索引就是存储预先计算好的函数或表达式值的索引。这些表达式可以是算术运算表达式、SQL或PL/SQL函数、C调用等。值得注意的是,一般用户要创建函数索引,必须具有GLOBAL QUERY REWRITE和CREATE ANY INDEX权限。否则不能创建函数索引,看下面例子:
例1:为EMP表的ename 列建立大写转换函数的索引idx :
CREATE INDEX idx ON emp ( UPPER(ename));
这样就可以在查询语句来使用:
SELECT * FROM EMPWHERE UPPER(ename) LIKE ‘JOH%’;
例2:为emp的工资和奖金之和建立索引:
1) 查看emp 的表结构:
SQL> desc emp
Name Null? Type
------------------------------------------------- ------------------
EMPNO NOT NULL NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)
2)没有授权就创建函数索引的提示:
SQL> createindex sal_comm on emp ( (sal+comm)*12, sal,comm)
2 tablespace users storage(initial 64k next 64k pctincrease 0);
create indexsal_comm on emp ( (sal+comm)*12, sal,comm)
*
ERROR at line 1:
ORA-01031:insufficient privileges
3) 连接到DBA帐户并授权:
SQL> connectsys/sys@ora816
Connected.
SQL> grantGLOBAL QUERY REWRITE to scott;
Grant succeeded.
SQL> grantCREATE ANY INDEX to scott;
Grant succeeded.
4)在连接到scott帐户,创建基于函数的索引:
SQL> connectscott/tiger@ora816
Connected.
SQL> createindex sal_comm on emp ( (sal+comm)*12, sal,comm)
2 tablespace users storage(initial 64k next 64k pctincrease 0);
Index created.
1)在查询中使用函数索引:
SQL> selectename,sal,comm from emp where (sal+comm)*12 >5000;
ENAME SAL COMM
-------------------------------------- ----------------
ALLEN 1600 300
WARD 1250 500
MARTIN 1250 1400
TURNER 1500 0
赵元杰 1234.5 54321
§3.6.2 反向键索引
反向键索引通过反向键保持索引的所有叶子键上的插入分布。有时,可用反向键索引来避免不平衡的索引。对于反向键索引可以进行下面操作:
l 通过在ALTERINDEX命令后加REBUILD NOREVERSE或REBUILDREVERSE子句来使索引边为反向键索引或普通索引;
l 采用范围扫描的查询不能使用反向键索引;
l 位图索引不能反向;
l 索引编排表不能反向。
例1:创建一个反向键索引:
CREATE INDEX i ON t (a,b,c) REVERSE;
例2:使一个索引变为反向键索引:
ALTER INDEX i REBUILD NOREVERSE;
§3.6.3 索引组织表
与普通的索引不一样,索引组织表(Index_Organized Table)是根据表来存储数据,即将索引和表存储在一起。这样的索引结构表(Index_organizedtable—IOT)的特点是:对表数据的改变,如插入一新行、删除某行都引起索引的更新。
索引组织表就象带一个或多个列所有的普通表一样,但索引组织表在B-树索引结构的叶节点上存储行数据。通过在索引结构中存储数据,索引组织表减少了总的存储量,此外,索引组织表也改善访问性能。
由于表中的行与B_树索引存放在一起,每个行都没有ROWID,而是用主键来标识。但是Oracle会“猜”这些行的位置并为每个行分配逻辑的ROWID。此外,你可以为这样的表建立第二个索引。
创建索引结构表也是用CREATE TABLE 命令加ORGANIZATIONINDEX关键字来实现。但是,这样的表在创建完后,你还必须为该表建立一个主键。
例子:
CREATE TABLE IOT_EXPAMPLE
(
Pk_col1 number(4),
Pk_col2 varchar2(10),
Non_pk_col1 varchar2(40),
Non_pk_col2 date,
CONSTRAINT pk_iot PRIMARY KEY
( pk_col1, pk_col2)
)
ORGANIZATION INDEX
TABLESPACE INDEX
STORAGE( INITIAL 1M NEXT 512K PCTINCREASE 0 );
索引组织表有些限制:
l 不能使用唯一约束;
l 必须具有一个主键;
l 不能建立簇;
l 不能包含LONG类型列;
l 不支持分布和复制。
提示:如果建立了索引组织表,则会在DBA_TABLES中的IOT_TYPE和IOT_NAME列上记录有索引组织表的信息。
例1.修改索引结构表 docindex 的索引段的INITRANS参数:
ALTER TABLE docindex INITRANS 4;
例2.下面语句加一个的溢出数据段到索引组织表 docindex中:
ALTER TABLE docindex ADD OVERFLOW;
例3.下面语句为索引组织表 docindex的溢出数据段修改INITRANS参数:
ALTER TABLE docindex OVERFLOW INITRANS 4;
§3.7 抽象数据类型的使用
Oracle 提供了一种新的类型结构,叫做抽象数据类型。用于定义许多复杂的对象。
1.创建抽象类型的命令语法:
CREATE OR REPLACETYPE [schema.]type_name [IS|AS] OBJECT ( element_list );
详细命令语法见《ORACLE8i SQL REFERENCE》
2.例子:
例:为地址建立一个抽象数据类型 add_type
create type add_typeas object
(
street varchar2(10),--街道名
city varchar2(20), -- 城市名
state char(2), --州代码
zip number --邮编
);
在建立表时使用add_type 数据类型
create tablecustomer
(
name varchar2(20),
address add_type
);
向具有抽象数据类型的表插入数据
insert into customer values
(‘1’,add_type('mystaree','some city','st',10001));
§3.8 大数据类型的使用
除了 long 能在一行上存储多达2GB的字符和long raw 能存储多达2GB的二进制数据外,ORACLE还提供四种大的数据类型来完善大数据的存储。
§3.8.1 可用数据类型
下表列出在ORACLE8中可用的数据类型:
LOB数据类型 | 描述 |
BLOB | 二进制LOB,最多为4GB |
CLOB | 字符LOB, 最多为4GB |
BFILE | 二进制文件,存储在数据库之外的只读二进制文件,大小与OS有关 |
NCLOB | 支持多字符集(Multibyte Characters)的CLOB列 |
与LONG数据类型不同,在一个表内可以建立多个 LOB列,如:
Create tableproposal
(
proposal number(10) promary key,
recipient_name varchar2(25),
proposal_name varchar2(25),
short_description varchar2(1000),
proposal_text clob, /* 提议内容 */
budget blob, /* 预算电子数据报表 */
cover_letter bfile /* 与提议有关的封面函件 */
);
§3.8.2 为LOB数据类型指定存储
由于LOB占用空间较大,所以应该为LOB所在的表制定存储空间。如:
Create tableproposal
(
proposal number(10) promary key,
recipient_name varchar2(25),
proposal_name varchar2(25),
short_description varchar2(1000),
proposal_text clob, /* 提议内容 */
budget blob, /* 预算电子数据报表 */
cover_letter bfile /* 与提议有关的封面函件 */
)
storage( initial1m next 500k pctincrease 0 )
tablespacePROPOSALS;
上面例子是将整个表存储在一个表空间中,为了达到高效目的,可以将LOB列存储在另外的表空间中,如:
Create tableproposal
(
proposal number(10) promary key,
recipient_name varchar2(25),
proposal_name varchar2(25),
short_description varchar2(1000),
proposal_text clob, /* 提议内容 */
budget blob, /* 预算电子数据报表 */
cover_letter bfile /* 与提议有关的封面函件 */
)
storage( initial1m next 500k pctincrease 0 )
tablespace PROPOSALS
LOB(proposal_text,budget) STORE AS
( TABLESPACEproposal_lobs
STORAGE( initial 2m next 1m pctincrease 0 )
CHUNK 16k PCTVERSION 10 NOCACHE LOGGING
);
其中:
LOB(proposal_text,budget) STORE AS 是将 LOB列出并指定存储在proposal_lobs中。
CHUNK 16k 指出分配给LOB的空间,它的取值范围最小为1k;最大为32k。
PCTVERSION 10 创建新版本的百分比。缺省时老版本的数据不被覆盖,直到有10%的可用LOB存储空间被使用。
NOCACHE 在读、写操作中数据不被存储在内存中,否则为 CACHE。
LOGGING 对于LOB的所有操作都被记录在重做日志文件(REDO)中,否则为 NOLOGGING。
§3.8.3 操作和检索LOB数据
可以有多种方法来检索和操作LOB数据,主要有:
1)通过DBMS_LOB包;
2)使用API(ApplicationProgramming Interfaces);
3)OCI(Oracle Call Interfaces)。
1.初始化值:
对于每个表中的LOB列,ORACLE都有一个定位器(Locator Value)来告诉数据库在哪里可以找到对于每个记录任意分开存储的数据。当在包括LOB的表插入一个记录时,可以使用DBMS_LOB包来告诉Oracle 对于内部存储的LOB列,生成一个空的定位器值。
例如我们要向PROPOSAL表插入一个记录,但还没有Budget 电子表格和信函封面,则可以使用下面命令:
Insert into proposal
( proposal_id,recipient_name,proposal_name,short_description,
proposal_text,budget, cover_letter )
values(1,’DOT PHILLIPS’,’CLEAR PHILLIPS FIELD’,NULL,
‘This is the text of a proposal to clear Phillipsfield.’,
EMPTY_BLOB(), NULL);
为了将budget 设置为一个空的定位器值,使用了EMPTY_BLOB函数,如果希望将CLOB数据类型设置为一个空的定位器,则可使用EMPTY_CLOB函数。由于cover_letter 是一个外部存储的BFILE值,所以设置为NULL。
BLOB 数据类型 --- EMPTY_BLOB()
CLOB 数据类型 --- EMPTY_CLOB()
NCLOB数据类型 --- EMPTY_CLOB()
可以使用BFILENAME来指向目录和文件(需要有 DBA角色或 CREATE DIRECTORY 权限)。为了创建一个目录,要使用Create directory命令。如:
Create directory ‘proposal_dir’ for ‘/u01/proposal/letters’;
当插入数据项时可以使用proposal_dir 逻辑目录(而不使用/u01/proposal/letters)。
现在可以向 proposal表输入第二条带有cover_letter的记录:
Insert into proposal
( proposal_id,recipient_name,proposal_name,short_description,
proposal_text,budget, cover_letter )
values(2,’BRAD OHMONT’,’REBUILD FENCE’,NULL,
EMPTY_CLOB(),EMPTY_BLOB(),
BFILENAME(‘proposal_dir’,’P2.DOC’) );
2.带子查询的数据插入
与LONG类型不一样,LOB数据类型可以使用 select 语句来进行数据的插入。如:
Insert into proposal
( proposal_id,recipient_name,proposal_name,short_description,
proposal_text,budget, cover_letter )
select 3,’SKIP GATES’,’CLEAR GATES FIELD’,NULL,
proposal_text,buaget,cover_letter
from proposal
where proposal_id =1;
3.更新LOB值
update proposal
set proposal_text=’Thisis the new proposal text.’
Where proposal_id=3;
Update proposal
Set cover_letter=BFILENAME(‘proposal_dir’,’P3.DOC’)
Where proposal_id =3;
4.使用DBMS_LOB来操作LOB值
可以用DBMS_LOB来改变或检索LOB列值。在DBMS_LOB包中可以使用下面过程或函数来对LOB进行操作:
过程或函数 | 描述 |
READ | 用来读入一个LOB值的过程 |
SUBSTR | 用来在LOB值上执行SQL语句的SUBSTR函数 |
INSTR | 用来在LOB值上执行SQL语句的INSTR函数 |
GETLENGTH | 用来在LOB值上执行SQL语句的GETLENGTH函数 |
COMPARE | 比较两个LOB值的函数 |
WRITE | 用于将一个LOB值的指定点写入数据到表的LOB列值中 |
APPEND | 用来将LOB值添加到表的LOB中(追加在后面) |
ERASE | 用来删除所有的LOB值 |
TRIM | 用来在一个LOB值中执行TRIM(裁剪)函数 |
COPY | 用来将一个LOB值从一个列拷贝到另一个LOB列 |
BFILE使用的附加函数:
在使用BFILE类型时还要另外一些附加的函数。并且要在INITsid.ora 参数文件中修改:
SESSION_MAX_OPEN_FILES xxx
缺省时,只允许打开10个BFILE文件。
BFILE用的函数和过程如下:
过程或函数 | 描述 |
FILEOPEN | 用于打开所需读入的文件的过程 |
FILECLOSE | 用于关闭所需读入的文件的过程 |
FILECLOSEALL | 用于关闭所有打开的文件的过程 |
FILEEXISTS | 用来确认所打开的文件是否存在的函数 |
FILEGETNAME | 用来得到一个BFILE定位器值所引用的外部文件名的过程 |
FILEISOPEN | 用来确认一个外部文件是否已打开 |
用DBMS_LOB包来读入 LOB的PL/SQL例子:
先看PL/SQL读入LOB的结构:
declare
locator value 变量;
amount 总字节变量;
offset 偏移量变量;
output 输出变量;
begin
set amount :=…;
set offset := . . .;
select locator_value into locator from table;
DBMS_LOB.READ(locator_value,amount,offset,output );
DBMS_OUTPUT.PUT_LINE(‘output:’||output );
end;
例子:
declare
locator_var CLOB;
amount_var integter;
offset_var integer;
output_var varchar2(10);
begin
amunt_var :=10;
offset_var := 1;
select proposal_textinto locator_var from PROPOSAL
where proposal_id=1;
DBMS_LOB.READ(locator_var,amount_var,offset_var,output_var);
DBMS_OUTPUT.PUT_LINE(‘Start of proposal text:’||output_var);
End;
/
§3.9 表和索引有关的数据字典
当我们创建了表、主键、外部键和索引后,相关的信息就被记录到Oracle的数据字典中,应用系统设计人员、程序人员和数据库管理员,应该了解有关数据字典的基本查询方法.下面给出表和索引有关的数据字典的简单介绍,更详细的内容请参考《Oracle8i/9I数据库管理》。
§3.9.1 表和索引数据字典
l DBA_TABLES,ALL_TABLES,USER_TABLES存放表的基本信息,主要包括创建表结构时描述的信息,如表名,表空间,存储参数等;此外,还有一些信息是在分析表时由系统自动写进去的,比如,表的行数量、行平均字节等。
l DBA_INDEXES,ALL_INDEXES,USER_INDEXES存放索引的基本信息,主要包括创建索引时描述的信息和用ANALYZE分析索引由系统自动写进去的信息。
l DBA_IND_COLUMNS 存放有索引的列的信息,因为Oracle在分析创建索引的命令正确后就将表名、索引名等存放到DBA_INDEXES数据字典中,而将索引的列名存放到DBA_IND_COLUMNS数据字典中。所以,查询者需要了解两个数据字典才能查到索引的详细信息。
l ALL_CONSTRAINTS 存放表的限制信息。
l ALL_CONS_COLUMNS 存放表的列的限制信息。
§3.9.2 数据字典查询例子
了解数据字典的目的就是查询有关表和索引的信息,下面是简单的查询例子。
例1.查询当前用户的表的基本信息,包括表名、存放的表空间、存储参数:
SQL> selecttable_name ,tablespace_name,initial_extent,next_extent
2* from user_tables
SQL>
TABLE_NAME TABLESPACE_NAME INITIAL_EXTENT NEXT_EXTENT
------------------------------------ -------------- -----------
ACCESS$ SYSTEM 16384 106496
AQ$_MESSAGE_TYPES SYSTEM 65536 65536
AQ$_PENDING_MESSAGESSYSTEM 65536 65536
. . . . . .
例2.查询当前用户的索引的基本信息,包括表名、索引名及表空间、存储参数:
SQL> selectindex_name,tablespace_name,initial_extent,next_extent
2 fromall_indexes where owner=user;
INDEX_NAME TABLESPACE_NAME INITIAL_EXTENT NEXT_EXTENT
------------------------------------------------ -------------- -----------
AQ$_MSGTYPES_PRIMARY SYSTEM 65536 65536
AQ$_PROPAGATION_STATUS_PRIMARYSYSTEM 65536 65536
AQ$_QTABLE_AFFINITIES_PK SYSTEM 65536 65536
AQ$_QUEUE_STATITICS_PK SYSTEM 65536 65536
AQ$_SCHEDULES_PRIMARY SYSTEM 65536 65536
ASSOC1 SYSTEM 16384 16384
ASSOC2 SYSTEM 16384 16384
. . . . . .
这里的whereowner=user 表示只查当前用户的索引.
例3.查询当前用户的索引及索引的列名:
SQL> colcolumn_name for a40
SQL> colindex_name for a18
SQL> selectindex_name,table_name,column_name from all_ind_columns
2* where table_owner=user;
INDEX_NAME TABLE_NAME COLUMN_NAME
------------------------------------ -----------------------------
I_ACCESS1 ACCESS$ D_OBJ#
I_ACCESS1 ACCESS$ ORDER#
AQ$_MSGTYPES_PRIMARYAQ$_MESSAGE_TYPES QUEUE_OID
. . . . . .
例4.查询当前用户的限制信息,当我们创建表结构时,如果描述了限制,则这些限制就被存放到DBA_CONSTRAINTS数据字典中,看下面例子:
创建下面表结构:
Create table worker
( empno number(4) primary key,
name varchar2(10),
age number(2) CHECK(age between 18 and 65 ),
/* age number(2) CHECK( age >=18 and age<=65 ) */
lodging char(15) References LODGING(lodging)
);
查询数据字典信息可以得到:
SQL> selectowner,constraint_name,table_name from user_constraints;
OWNER CONSTRAINT_NAME TABLE_NAME
-------------------------------------------------- -----------------
ZHAO SYS_C001009 WORKER
ZHAO SYS_C001010 WORKER
SQL> set long1000
SQL> selectSEARCH_CONDITION from user_constraints;
SEARCH_CONDITION
--------------------------------------------------------
age between 18 and 65
例5.创建表结构时描述了限制,则这些限制就被存放到DBA_CONSTRAINTS数据字典中,再看下面例子:
CREATE TABLE dept
(deptno number(2),
dname varchar2(20),
loc varchar2(20),
CONSTRAINT pk_dept PRIMARY KEY (deptno)
);
Create table empl
(
Empno number(5) primary key,
Ename varchar2(15) not null,
Job varchar2(10),
Mgr number(5),
Hiredate date default sysdate,
Sal number(7,2) CHECK(sal>100),
Comm number(3) default 0.0 ,
Dept number constraint
dept_fkey References zhao.dept
);
SQL> colCONSTRAINT_NAME for a12
SQL> selectconstraint_name,table_name,SEARCH_CONDITION
2* from user_constraints;
CONSTRAINT_NTABLE_NAME SEARCH_CONDITION
------------------------------ -------------------------
PK_DEPT DEPT
SYS_C001013 EMPL "ENAME" IS NOT NULL
SYS_C001014 EMPL sal>100
SYS_C001015 EMPL
DEPT_FKEY EMPL
SYS_C001009 WORKER age between 18 and 65
SYS_C001010 WORKER
已选择7行。
第四章 视图、同义词和序列
视图、同义词和序列是Oracle的常用对象,在Oracle系统安装完成后,就已经建立许多Oracle系统所用的视图、同义词和序列。此外,在应用系统设计中,也经常需要创建视图、同义词和序列来满足应用的需要。下面给出简要介绍。
§4.1 视图
视图的一个主要目的就是简化用于查询所使用的语句,另外就是可以实现安全和保密的目的。利用视图,我们可以在查询处理中完成复杂的操作。
§4.1.1 使用视图来修改表中数据
可以用视图修改表中数据:
l 带有集合操作,如 intersect,union和minus的视图;
l 带有 groupby,connect by,或 start with子句的视图;
l 带有组合功能,如 avg , sum 或 max 功能的视图;
l 使用 distinct 功能的视图。
§4.1.2 创建一个新视图
1 建立视图命令语法:
CREATE [OR REPLACE][FORCE/NO FORCE] VIEW [schema.]view
[column_name1,column_name2] AS query
[WITH OBJECT OID |DEFAULT]
[WITH CHECK OPTION]
[CONSTRAINTconstraint]
[WITH READ ONLY]
OR REPLACE 替换掉原来的视图(不需删除)
FORCE 强行创建一视图,无论视图的基表是否存在或拥有者是
否有权限,但作select、insert、update、delete前条件
必须为真。
Schema 帐户、缺省为当前登录的帐户。
VIEW 视图名
Alias 视图的列名(唯一),缺省为列名
As subquery 查询表达式(不含order by, For update)
WITH CHECK OPTION 在视图上作insert,update时必须是视图,
查询所得到的结果,有子查询时可能不正确。
Constraint 约束名称,缺省为sys_Cn. N为整数(唯一)。
注:视图只是一个逻辑表,它自己不包含任何数据,目的在于:
l 通过限制存取基表中预定的一组行或列,提供安全的附加功能;
l 隐藏数据的复杂性,例如,经常对几个表的数据作某种运算后查询
时,可以使用视图使得操作仿佛是在单表上进行;
l 省去一些复杂的连接操作
==============================================================================
注意:下面情况在视图中受到限制:
l 视图查询不能选取Currval,nextval伪列;
l 只有加别名才能使用rowid,rownum,level;
l 如果在子查询中使用 * 代替选择的表的所有列,则后来该表新加的列不会自动被加到视图中,只有重新创建视图后该新增的列才能被加到视图中;
l 如果视图建立(即查询)时包括任何以下结构之一,则该视图不能作insert,update,delete(目前的新版可以,需作特别的说明限制):
连接运算;
集合运算符;
组函数;
GROUP BY,CONNECTBY,START WITH;
DISTINCT。
==============================================================================
提示:不要在视图中再建视图,理论上虽可以对视图再建视图,但这样在查询时影响速度。
Create view emp_vi as select * from emp;
例1:为表emp建立视图dept20,此视图可以显示部门20的雇员和他们
的年薪。
Create view dept10 As select ename,deptno,job,sal*12 sal12
From emp where deptno=10;
例2:
Create view clerk (id_number, person, depart, position )
As select empno,ename,deptno,job
From emp where job='clerk'
With check option constraint wco;
用户不能往clerk视图中作insert(或update)非'clerk'的记录。
§4.1.3 删除一个视图
1.用命令删除视图
语法:
DROP VIEW [SCHEMA.]view_name;
如
drop view view_data;
建议:一般视图不占用多少空间,可以不必删除。
2.用SchemaManager 删除视图
1)启动Schema Manager ,以DBA登录;
2)双击 View 文件夹,出现包含视图的模式列表;
3)双击包含要改变的视图的名字;
4)点击要被删除的视图名;
5)点红X;
6)在确定是否要删除中回答 Yes;
有关的数据字典user_views(dba_views,all_views) 视图:
Column Datatype NULL 说明
------------ ------------- ---------- ------------------
OWNER VARCHAR2(30) NOT NULL 视图创建者
VIEW_NAME VARCHAR2(30) NOT NULL 视图名
TEXT_LENGTH NUMBER 视图主体长度
TEXT LONG 视图内容
TYPE_TEXT_LENGTH NUMBER 类型文本长度
TYPE_TEXT VARCHAR2(4000) 视图的类型
OID_TEXT_LENGTH NUMBER OID 视图类型的长度
OID_TEXT VARCHAR2(4000) 视图类型的OID
VIEW_TYPE_OWNER VARCHAR2(30) 视图类型的所以者
VIEW_TYPE VARCHAR2(30) 视图类型
§4.1.4 改变视图
当视图的状态不可用('INVALID')时,需要用ALTER VIEW . . . COMPILE 对视图进行编译。如:
SQL>ALTER VIEW SCHEMA.view COMPILE;
你可以用下面语句查询那些无效的视图,然后有针对性地进行编译:
SQL> SELECT OWNER,OBJECT_NAME,OBJECT_TYPE,STATUS FROM DBA_OBJECTS
WHERE object_type=’VIEW’ and STATUS='INVALID';
§4.2 实体视图(MATERIALIZED VIEW)
Oracle8i版本提供可以创建实体视图(MATERIALIZED VIEW),它确实存放有物理数据。实体视图包含定义视图的查询时所选择的基表中的行。在普通的视图中,Oracle在执行查询时临时进行查询操作来返回结果;而对实体视图的查询是直接从该视图中取出行。
在Oracle9i版本里,对实体视图进行了增强,如提供快速刷新等。下面简单介绍实体视图的使用。
§4.2.1 创建实体视图
1.关键内容:
使用实体视图需要了解下面几个关键点:
l 实体视图存放有物理数据;
l 实体视图背后的查询只在视图建立或刷新时执行,即如果创建后不进行刷新则只得到创建时的数据;
l 实体视图使用DBMS_MVIEW 程序包中含有刷新和管理实体视图的过程来进行管理;
l 在导出和导入(EXP、IMP)中使用MVDATA参数来实现实体视图数据的导出和导入;
l 使用CREATEMATERIALIZED VIEW 语句创建实体视图;
l 实体视图中的查询表叫主表(master tables)(复制项)或详细表(数据仓库项)。为一致起见,这些主表叫主数据库(master databases.);
l 为了复制目的,实体视图允许你在本地管理远程拷贝;
l 所复制的数据可以使用高级复制特性进行更新;
l 在复制环境下,通常创建的实体视图都是主键、ROWID和子查询实体视图。
2.创建实体视图前提:
l 要有授权创建实体视图的权限(CREATE MATERIALIZED VIEW 或CREATE SNAPSHOT);
l 必须有访问各个主表的权限,即 有SELECT ANY TABLE 的系统权限。
如果在另外的用户模式下创建实体视图,则:
l 需要有CREATE ANY MATERIALIZED VIEW或CREATE ANY SNAPSHOT、SELECTANY TABLE 权限;
l 必须有CREATETABLE、SELECT ANY TABLE系统权限。
如果带查询重写有效来创建实体视图,则:
l 主表的主人必须有QUERY REWRITE系统权限;
l 如果你不是主表主人,则必须有GLOBAL QUERY REWRITE系统权限;
l 如果模式主人没有主表,则该模式主人必须有GLOBAL QUERY REWRITE权限。
3.创建实体视图语法:
下面给出Oracle9i版本的实体视图的创建语法:
CREATE MATERIALIZEDVIEW [schema.] materializede_view
[ OF [schema .]object_type ]| [(scoped_table_ref_constraint)] |
ORGANIZATION_INDEXindex_org_table_clause |
[
[
[[segment_attribute_cluase|column_properties ] | [CACHE|NOCACHE ] ] |
[ CLUSTER cluster(column,) ]
]|
[partitioning_clause|parllel_cluse|build_clause]|
[
ON PREBUILT TABLE [[WITH|WITHOUT] | REDUCED PRECISION ]
]|
[
[ USING INDEX[physical_attribute_clause |TABLESPACE tablespace] ] |
[ USING NO INDEX]
] refresh_cluse
[ [ FOR UPDATE ] | [ DISABLE | ENABLE ] QUERYREWRITE ] ]
AS subquery;
其中:
scoped_table_ref_constraint为:
SCOPE FOR ([ref_column|ref_attribute] ) IS [schema.] scpe_table_name
Index_org_table_clause为:
[(mapping_table_clause) | PCTTHRESHOLD integer | [COMPRESS integer|NOCOMPRESS] ]
[ INCLUDINGcolumn_name ] OVERFLOW [ segment_attribute_clause]
refresh_clause 为:
[ NEVER REFRESH |
[ REFRESH | [ USING [ DEFAULT [LOCAL | MASTER] ROLLBACK SEGMENT ] |
[LOCAL | MASTER] ROLLBACK SEGMENT ] rollback_segment ]
| WITH [ PRIMARY KEY | ROWID ]
| NEXT [ START WITH ] date
| ON [ DEMAND | COMMIT ]
| [ FAST | COMPLETE|FORCE ]
]
参数说明:
schema 模式名
materialized_view 实体视图名
segment_attributes_clause 建立PCTFREE、PCTUSED、INITRANS和MAXTRANS参数。
TABLESPACE 表空间
LOB_storage_clause 大对象存储参数
LOGGING | NOLOGGING 指定创建实体视图时是否需要建立日志
CACHE | NOCACHE 实体视图的数据是否被缓存
CLUSTER cluster 名
partitioning_clauses 用于指定实体视图的分区范围或一个HASH函数。实体视图分区与表分区类似。
parallel_clause指定实体视图的并行操作和设置查询并行度。
build_clause 当移植实体视图时使用。
NOPARALLEL 指定顺序执行(缺省值),
PARALLEL 如果选择并行度时可指定并行。
THREADS_PER_CPU 初始参数
PARALLEL integer 指定并行度。
Build_clause 指定重建实体视图时的选项:
IMMEDIATE 指定为IMMEDIATE 表示实体视图是立即移植(缺省值)。
DEFERRED 指定为DEFERRED 表示实体视图是在下次刷新时移植。第一次延期总是一个完全的刷新。一直到被刷新为止该实体视图的值都是旧的值,所以它是不可查询重写的。
ON PREBUILTTABLE 此项可以使你以原初始化实体视图(preinitializedmaterialized view)来注册一个存在的表。这对于大表来说非常有用。它有下面限制:
l 每个列的别名必须与表的列名一样;
l 如果使用ONPREBULT TABLE,则不能对列再指定 NOT NULL。
WITH REDUCED PRECISION 允许指定表或实体视图精度可以丢失。实体视图的列不能与子查询所返回的精度一致。
WITHOUT REDUCED PRECISION 表示不允许指定表或实体视图精度可以丢失。实体视图的列要与子查询所返回的精度一致。这是缺省值。
USING INDEX 用此项可以为索引建立INITRANS、MAXTRANS及STORAGE参数。如果不指定本参数,则系统使用原索引。
限制:不能在USING INDEX字句里指定PCTUSED或PCTFREE参数。
refresh_clause 用于指定缺省方法、模式及Oracle刷新实体视图的次数。如果一个实体视图的主表被修改。则实体视图必须被更新才能反映当前的数据。这项可以实现指定时间表和刷新方法。
FAST 指定增量刷新方法,该刷新是根据主表的改变来进行。这种改变存储在任何一个实体视图的日志里或加载日志里。
即使还没有在主表下建立实体视图日志,也可以建立一个总和的实体视图。然而,如果你建立其它类型的实体视图时,CREATE 语句就会失败。除非实体视图日志已经存在。
如果在创建实体视图时存在适合的实体视图日志,Oracle将执行快速的刷新。
为了使DML改变和直接的加载都能有效,就要适当限制实体视图的刷新。
COMPLETE 指定刷新方法,如果指定了完全刷新,即使已经指定了快速刷新,Oracle也执行完全刷新。
FORCE 表示强行刷新。它是FAST、COMPLETE、FORCE三种刷新的缺省值。
4.创建实体例子:
例1.创建实体汇总视图:
下面语句建立一个移植的实体视图,并指定缺省的刷新方法、模式及时间:
CREATE MATERIALIZED VIEW mv1 REFRESH FAST ONCOMMIT
BUILD IMMEDIATE
AS SELECT t.month, p.prod_name, SUM(f.sales) ASsum_sales
FROM time t, product p, fact f
WHERE f.curDate = t.curDate AND f.item = p.item
GROUP BY t.month, p.prod_name;
例2.创建实体汇总视图:
下面语句建立和移植一个实体视图sales_by_month_by_state,这个实体视图根据数据语句一旦执行成功就进行移植。接着就完成实体视图的查询:
CREATE MATERIALIZED VIEW sales_by_month_by_state
TABLESPACE my_ts PARALLEL (10)
ENABLE QUERY REWRITE
BUILD IMMEDIATE
REFRESH COMPLETE
AS SELECT t.month, g.state, SUM(f.sales) ASsum_sales
FROM fact f, time t, geog g
WHERE f.cur_date = t.cur_date AND f.city_id =g.city_id
GROUP BY month, state;
例3.原实体视图(即视图的名字与原来表名一样):
下面语句为先前存在的总结表sales_sum_table 建立汇总视图 sales_sum_table:
CREATE TABLE sales_sum_table
(month DATE, state VARCHAR2(25), sales NUMBER);
CREATE MATERIALIZED VIEW sales_sum_table
ON PREBUILT TABLE
ENABLE QUERY REWRITE
AS SELECT t.month, g.state, SUM(f.sales) ASsum_sales
FROM fact f, time t, geog g
WHERE f.cur_date = t.cur_date AND f.city_id =g.city_id
GROUP BY month, state;
在这个例子中,实体视图与先前建立的实体表 有相同的名字、相同的列和数据类型。
例4.实体连接视图:
声明语句建立一个连接实体视图:
CREATE MATERIALIZED VIEW mjv
REFRESH FAST
AS SELECT l.rowid as l_rid, l.pk, l.ofk, l.c1,l.c2,
o.rowid as o_rid, o.pk, o.cfk, o.c1, o.c2,
c.rowid as c_rid, c.pd, c.c1, c.c2
FROM l, o, c
WHERE l.ofk = o.pk(+) AND o.ofk = c.pk(+);
例5.子查询实体视图:
下面语句创建一个基于Order和 Customers 表的视图:
CREATEMATERIALIZED VIEW sales.orders FOR UPDATE
ASSELECT * FROM sales.orders@dbs1.acme.com o
WHEREEXISTS
(SELECT* FROM sales.customers@dbs1.acme.com c
WHEREo.c_id = c.c_id);
例6.主键的实体视图:
下面语句创建一个主键实体视图human_genome:
CREATE MATERIALIZED VIEW human_genome
REFRESH FAST START WITHSYSDATE NEXT SYSDATE + 1/4096
WITH PRIMARY KEY
AS SELECT * FROM genome_catalog;
例7.ROWID实体视图:
下面语句创建一个ROWID实体视图emp_data:
CREATEMATERIALIZED VIEW emp_data REFRESH WITH ROWID
ASSELECT * FROM emp_table73;
例8.周期性刷新的实体视图:
下面语句创建一个主键实体视图emp_sf并根据在纽约的scott的职工表来移植数据:
CREATE MATERIALIZED VIEW emp_sf
PCTFREE 5 PCTUSED 60
TABLESPACE users
STORAGE (INITIAL 50K NEXT 50K)
REFRESH FAST NEXT sysdate + 7
AS SELECT * FROM scott.emp@ny;
此语句没有START WITH参数,所以Oracle使用SYSDATE来估计下次的自动刷新时间。Oracle执行首次刷新为7天后。
例9.自动刷新的实体视图:
下面语句创建一个复杂的实体视图all_emps,它查询DALLAS和BALTIMORE中的职工表:
CREATEMATERIALIZED VIEW all_emps
PCTFREE5 PCTUSED 60
TABLESPACEusers
STORAGEINITIAL 50K NEXT 50K
USINGINDEX STORAGE (INITIAL 25K NEXT 25K)
REFRESHSTART WITH ROUND(SYSDATE + 1) + 11/24
NEXTNEXT_DAY(TRUNC(SYSDATE, ’MONDAY’) )+15/24
ASSELECT * FROM fran.emp@dallas
UNION
SELECT* FROM marco.emp@balt;
Oracle在早上11点自动刷新,接着就在周一的3点进行刷新。缺省刷新方法是FORCE,all_emps视图包含一个UNION,它是不支持快速刷新的,所以Oracle只能用完全(complete)刷新。
上面语句同样为实体视图建立存储特性:
l 第一个存储参数建立初始大小为50KB,下次大小也为50KB.
l 第二个存储参数(使用USING INDEX)建立初始大小为25KB,下次大小也为25KB.
例10.自回滚段的实体视图:
下面语句在远程建立带master_seg 回滚段的主键实体视图sales_emp,并用本地回滚段snap_seg来刷新实体视图:
CREATEMATERIALIZED VIEW sales_emp
REFRESHFAST START WITH SYSDATE NEXT SYSDATE + 7
USINGMASTER ROLLBACK SEGMENT master_seg
LOCALROLLBACK SEGMENT snap_seg
ASSELECT * FROM bar;
§4.2.2 创建实体视图日志
1.创建实体视图日志的目的
使用CREATEMATERIALIZED VIEW LOG语句可以创建实体视图日志。实体视图日志是一个包含有主表和实体视图的表。这些快照(snapshot)和实体视图(materializedview)其实都是同义词。它们都引用一个或多个包含查询结果的表,这些表可以是本地数据库或远程数据库的表。
DML的改变是由主表的数据组成的,Oracle在实体视图日志里存储那些改变行的描述,然后使用实体视图日志去刷新基于主表的实体视图,这个过程叫快速刷新。如果没有实体视图日志,Oracle必须重新执行实体视图查询,这个过程叫完全刷新。通常快速刷新要比完全刷新用的时间少。
一般,实体视图日志与模式中的主表放在一起。你需要为每个主表建立实体视图日志。因为Oracle 要使用这个实体视图日志来进行快速刷新。
2.要求
l 如果你拥有主表,则可以建立实体视图日志。
l 如果你为其他人建立实体视图日志,则必须有CREATE ANY TABLE 和 COMMENT ANY TABLE权限。
3. CREATE MATERIALIZED VIEW LOG语法
( 创建实体视图日志命令语法 见《Oracle9i SQL Reference 》 )p982
4.实体视图日志例子
例1.主键的例子:
下面语句在雇员表上建立实体视图日志:
CREATE MATERIALIZED VIEW LOG ON emp WITH PRIMARYKEY;
例2.建立仅包含更新行主键的实体视图日志
Oracle可以用实体视图日志在任何简单主键的实体视图中来执行一个快速刷新。下面语句建立一个只包含更新行主键的实体视图日志:
CREATE MATERIALIZED VIEW LOG ON emp
PCTFREE 5
TABLESPACE users
STORAGE (INITIAL 10K NEXT 10K);
下面语句建立一个只包含更新行主键的实体视图日志:
CREATE MATERIALIZED VIEW LOG ON sales WITHROWID, PRIMARY KEY;
下面语句建立一个包含更新行主键和更新列ZIP的实体视图日志:
CREATE MATERIALIZED VIEW LOG ON address WITH(zip);
下面语句建立一个主表,然后建立一个带INCLUDING NEW VALUES的实体视图日志:
CREATE TABLE agg
(u NUMBER, a NUMBER, b NUMBER, c NUMBER, dNUMBER);
CREATE MATERIALIZED VIEW LOG ON agg
WITH ROWID (u,a,b,c,d)
INCLUDING NEW VALUES;
下面语句使用agg日志来建立实体视图:
CREATE MATERIALIZED VIEW sn0
REFRESH FAST ON COMMIT
AS SELECT SUM(b+c), COUNT(*), a, d, COUNT(b+c)
FROM agg
GROUP BY a,d;
§4.2.3 修改实体视图
1.修改实体视图目的
实体视图是Oracle的一个数据库对象。它包含有一个或多个表的查询结果。使用
ALTER MATERIALIZED VIEW 可以对已经存在的实体视图进行修改。修改方法如下:
l 修改存储特性;
l 修改刷新方法、模式及时间
l 改变实体视图的结构以使它有不同类型;
l 使查询重写有效。
2.修改实体视图命令语法
ALTER MATERIALIZEDVIEW [schema.] materializede_view
[
[physical_attributes_clause|
LOB_storage_clause[,...] |
Modify_LOB_storage_clause [,...]|
Partition_clause |
Parallel_clause |
[LOGGING|NOLOGGING] |
allocate_extent_clause |
[CACHE|NOCACHE ]
]|
[
alter_iot_cluse |
USING INDEXphysical_attribute_clause |
MODIFYscoped_table_ref_constraint |
REBUILD |
Refresh_cluse
]|
[
[ DISABLE | ENABLE ]QUERY REWRITE |
COMPILE |
CONSIDER FRESH
]
详细见《Oracle9iSQL Reference》p502
3.修改实体视图例子
例1:
CREATE MATERIALIZED VIEW hq_emp
REFRESH COMPLETE
START WTIH SYSDATE NEXT SYSDATE +1/4096
AS SELECT * FROM hq_emp;
ALTER MATERIALIZED VIEW hq_emp
REFRESH FAST;
例2:修改下次刷新:
ALTER MATERIALIZED VIEW branch_emp
REFRESH NEXT SYSDATE+7;
例3:修改完全刷新:
ALTER MATERIALIZED VIEW sf_emp
REFRESH COMPLETE
START WITH TRUNC(SYSDATE+1) + 9/24
NEXT SYSDATE+7;
例4:使查询重写有效:
ALTER MATERIALIZED VIEW mv1
ENABLE QUERY REWRITE;
例5:使用回滚段:
ALTER MATERIALIZED VIEW inventory
REFRESH USING MASTER ROLLBACK SEGMENTmaster_seg;
ALTER MATERIALIZED VIEW sales
REFRESH USING DEFAULT MASTER ROLLBACK SEGMENT;
例6:使用主键:
ALTER MATERIALIZED VIEW emp_rs
REFRESH WITH PRIMARY KEY;
例7:使用完全刷新:
ALTER MATERIALIZED VIEW store_mv COMPILE;
例8:修改刷新方法:
ALTER MATERIALIZED VIEW store_mv REFRESH FAST;
例9:修改考虑刷新(CONSIDER FRESH)方法:
ALTER MATERIALIZED VIEW mv1 CONSIDER FRESH;
§4.2.4 修改实体视图日志
ALTER MATERIALIZED VIEW LOG
1.修改实体视图日志目的
使用 ALTER MATERIALIZEDVIEW LOG 可以对已经存在的实体视图日志进行修改。可以修改存储特性、刷新模式、时间或已经存在实体视图日志的类型。
2.修改实体视图日志命令语法
( 创建实体视图日志命令语法 见《Oracle9iSQL Reference 》 )
3.修改实体视图日志命令例子
例1:修改扩展次数:
ALTER MATERIALIZED VIEW LOG ON dept
STORAGE MAXEXTENTS 50;
例2:修改已经存在的ROWID:
ALTER MATERIALIZED VIEW LOG ON sales
ADD PRIMARY KEY;
§4.2.5 实体视图完整例子
要在应用中使用实体视图,除了要实体视图的语句外,还需要进行数据库实例的初始化参数。并重新启动数据库实例才能使所写的实体视图有效。下面是操作步骤:
1.修改实例初始化参数initsid.ora 有关参数
与实体视图有关的参数与数据库作业一样,都是job_queue_processes和job_queue_interval 。第1个参数是队列的进程数,一般要设大于 0 ;第2个参数是刷新间隔秒数。Oracle9i可以是小于1000的整数。例如在initora817.ora初始化中将该二参数设置为:
job_queue_processes = 2
job_queue_interval = 5
2.关闭实例和重启动实例
在Oracle8i版本,可用svrmgrl服务器实用程序来关闭和启动数据库实例;在Oracle9i版本可用SQL>CONNECT AS SYSDBA实现关闭和启动数据库实例。
3.运行实体视图
CREATE MATERIALIZED VIEW emp_stat
TABLESPACE users
STORAGE (INITIAL 8K NEXT 5K)
REFRESH FAST START WITHSYSDATE NEXT round(SYSDATE + 16/24)
AS SELECT deptno,sum(sal)
from emp group by deptno;
实体化视图已创建。
22:29:28 SQL> create materialized viewlog on emp pctfree 5 tablespace users;
实体化视图日志已创建。
SQL> select * from emp_stat;
DEPTNO SUM(SAL)
---------- ----------
10 14116
20 54537
30 9400
SQL> altermaterialized view emp_stat
2 refresh complete
3 start with trunc(sysdate)+15/24 next sysdate+30/24*60*60;
实体化视图已更改。
SQL>
SQL> select * from emp_stat;
DEPTNO SUM(SAL)
---------- ----------
10 14116
20 64536
30 9400
修改下次刷新时间为下午3点半(startwith trunc(sysdate)+15.5/24),则:
SQL> set time on
15:23:34 SQL>alter materialized view emp_stat
15:23:50 2 refresh complete
15:23:50 3 start with trunc(sysdate)+15.5/24 next sysdate+30/24*60*60;
实体化视图已更改。
现在虽然视图已更改,但由于没到时间。所以视图数据还是原来的旧数据:
15:27:05 SQL> /
DEPTNO SUM(SAL)
---------- ----------
10 14116
20 64536
30 9400
15:27:07 SQL>
15:27:07 SQL>
由于时间到了3点刷新点,可查出新的统计结果:
15:30:06 SQL> /
DEPTNO SUM(SAL)
---------- ----------
10 24115
20 64536
30 9400
15:30:09 SQL>
15:36:09 SQL> insert into emp values(555,'zhaojie','enginner',null,null,20000,5000,20);
已创建 1 行。
15:36:39 SQL> commit;
15:38:08 SQL> select * from emp_stat;
DEPTNO SUM(SAL)
---------- ----------
10 24115
20 64536
30 9400
15:38:34 SQL>
每一小时一次则next 表达式为 sysdate+1*60分*60秒/24 *60分* 60秒=next sysdate+1/24
每半小时一次 next sysdate + 30*60/60*60*24 = sysdate+1/48
每15分钟一次 next sysdate+1/96
希望刷新是4点半,接着是15分一次:
alter materializedview emp_stat
refresh complete
start withtrunc(sysdate)+16.5/24 next sysdate+1/96;
16:10:49 SQL>alter materialized view emp_stat
16:13:29 2 refresh complete
16:13:29 3 start with trunc(sysdate)+16.5/24 next sysdate+1/96;
实体化视图已更改。
16:13:31 SQL>select * from emp_stat;
DEPTNO SUM(SAL)
---------- ----------
10 24115
20 64536
30 9400
16:13:43 SQL>
16:30:20 SQL>/
DEPTNO SUM(SAL)
---------- ----------
10 24115
20 64536
30 19399
16:30:27 SQL>
16:30:27 SQL>
16:31:03 SQL> insert into empvalues(555,'zhaojie','enginner',null,null,20000,5000,20);
已创建 1 行。
16:32:04 SQL> commit;
提交完成。
16:32:15 SQL>
希望在16:45时重新刷新。得到新结果,在时间到后,视图自动刷新,deptno=20的总和已改变:
16:46:22 SQL> select * from emp_stat;
DEPTNO SUM(SAL)
---------- ----------
10 24115
20 84536
30 19399
与实际表查出一样:
16:46:29 SQL> select deptno,sum(sal)from emp group by deptno;
DEPTNO SUM(SAL)
---------- ----------
10 24115
20 84536
30 19399
4.停止实体视图的自动运行
§4.3 序号(sequence)
序号是一个发布唯一数字的ORACLE 对象,在需要时,每次按1或一定增量增加。序号通常用于产生表中的唯一主键或唯一索引等。
§4.3.1 建立序号
建立序号可以在SQL*PLUS 中用命令来完成,也可以使用SchemaManager 工具来完成。
1. 命令语法:
CREATE SEQUENCE[user.]sequence
[INCREMENT BY{1|integer}]
[START WITH integer]
[MAXVALUEinteger|NOMAXVALUE]
[MINVALUEinteger|NOMINVALUE]
[CYCLE|NOCYCLE]
[CACHE{20|integer}|NOCACHE]
[ORDER|NOORDER]
2. 建立序号
例1:建立Sequence
Create sequence emp_sequence
Increment by 1
Start with 1
No maxvalue
No cycle
Cache 10;
Create sequence order_seq
Start with 1
Incremant by 1
Nomaxvalue
Nocycle
Cache 20;
§4.3.2 修改序号
3. 修改序号
有时需要对已建立的序号进行修改,比如在系统移植或升级时可能有的序号已经增长到某个值。现在需要从原先停止的地方开始等。
例2:修改sequence
Alter sequence emp_sequence
Increment by 1
Maxvalue 10000
Cycle
Cache 20;
§4.3.3 使用序号
建立序号的目的就是使用序号,使用序号主要是在插入和查询时使用。
例3:使用sequence
insert into orders(orderno,custno)
values(order_seq.nextval,1032);
update orders set orderno-orderno=order_seq.nextval
where orderno=10112;
每使用一次,nextval自动增1,currval是多次使用的值,如果一开始就
用,则其值为0,一般情况下是在nextval使用之后才能使用currval,可以用它来产生同样的号,比如有一定货号有多种商品和数量:
insert into line items(orderno,partno,quantity)
values(order_seq.currval,20231,3);
insert into line_items(orderno,partno,quantity)
values(order_seq.currval,29374,1);
提示:在ORACLE8 中,如果在建立序列的语句中未加上NOCACHE,则有可能在关闭系统再启动后产生跳号现象。如果你的系统要求不许跳号,请在创建序列时在后面加 NOCACHE 。
§4.3.4 删除序号
当不再使用时就可以删除序号,删除序号有两种方法:
1. DROP SEQUENCE [Schema.]seguence_name;
2. 使用 Schema Manager 工具;
§4.4 同义词
同义词可以使多个用户使用同一个对象而不用将模式(Schema )作为前缀加在对象的前面,从而简化授权方面的操作。同义词有公有和私有两种。
§4.4.1 建立同义词
要建立同义词,首先要有Create any synonym和drop any synonym权限方可建立和撤消,如果某个用户不能建立同义词,则应给其授该权限。
CRAETE [PUBLIC]SYNONYM [user.]synonym
FOR [user.]table[@database_link];
例1:
Create public synonym emp
For scott.emp@sales;
例2:为当前用户的所有对象建立公共同义词,可用下面各命令来完成创建一个脚本:
set echo off
set head off
set verify off
set linesize 200
set pages 0
set feedback off
set term on
undefine p_user
def p_user =&&p_user
Prompt GeneratingScript To Drop User
set term off
SPOOL create_syn.sql
select 'drop publicsynonym '||object_name||' ;' from user_objects;
select ' createpublic synonym '||object_name||
' for sale.'||object_name||' ;' fromuser_objects;
SPOOL OFF
Start create_syn.sql
注意:当创建同义词后,还要将该同义词授权给public ,才能使其他的Oracle用户可以访问该同义词。
同义词数据字典:
DBA_SYNONYMS实例中所有同义词
USER_SYNONYMS(=SYN)用户的同义词
§4.4.2 删除同义词
DROP PUBLIC synonym[schema.]synonym ;
Drop synonym emp;
例1:为当前所有对象建立同义词。为了省去编辑,可用下面个命令来完成:
* 需具有 dba, -- create any synonym,drop anysynonym 权限
select 'drop publicsynonym '||object_name||' ;' from user_objects;
select ' createpublic synonym '||object_name||
' for sale.'||object_name||' ;' fromuser_objects;
§4.5 视图、同义词和序列有关的数据字典
当我们创建了视图、同义词和序列后,相关的信息就被记录到Oracle的数据字典中,作为程序人员和数据库管理员,应该了解有关数据字典的基本查询方法.
l DBA_VIEWS –实例中所有的视图的基本信息;
l DBA_SYNONYMS–实例中所有的同义词;
l DBA_SEQUENCES–实例中所有的序列。
第五章 簇与分区
Oracle公司在Oracle7以后的版本提供了许多数据库厂商不能具备的分区和簇的存储管理技术。使用这些技术,可以实现将大的表和索引进行拆分,使得处理速度提高和便于管理。
§5.1 簇( cluster )
簇(Cluster)是一组表,如果应用程序中的SQL 语句经常联结两个或多个表,可以把这些表以簇方式进行创建以改善性能。只要我们创建了簇并在创建表时指定到已经创建好的簇中,ORACLE 就把簇中的表存储在相同的数据块中,并且各个表中的相同的列值只存储一个。
§5.1.0 簇概念
簇(Cluster)就是将一组有机联系的表在物理上存放在一起并且相同的关键列的值只存储一份,用于提高处理效率的一项技术。如下图所示( 见Oracle8i concept ):
|
1.何时建立簇
如果通过引用完整性把两个或多个表联系起来并经常使用联结,则为这些表创建一个索引簇。如果一个表的多行经常与一个非唯一的列一起查询,则为该列创建一个单表簇,该列作为簇关键字,以提高性能。
2.有时簇会损害性能
对频繁更新或删除的表使用簇对性能有不利的影响。
3.限制:
l 簇中的每个表必须有一列与簇中指定的列的 大小和类型匹配;
l 簇码中可用列的最大数目是16,即一个簇最多有16列作为簇码;
l 列的最大长度为239字节;
l LONG 和LONG RAW 不能作为簇列码。
§5.1.1 建立簇
1.创建簇语法
CREATE CLUSTER cluster
( columndatatype[,colmn datatype]…)
[PCTUSED 40|intger] [ PCTFREE 10| intger]
[ SIZE intger ]
[INITRANS 1|intger] [MAXTRANS 255|intger]
[TABLESPACE tablespace]
[STORAGE storage]
2.创建簇及其表的步骤:
1) 用 CREATE CLUSTER创建簇
2) 用CREATE INDEX创建簇索引
3) 用CREATE TABLE 创建表,并指定簇
4) 插入数据并进行DML 操作
例1: 住房公积金实例
/******************************************************************/
/* 创建单位信息和单位职工汇缴信息所用的簇emp_unit */
/* 并为簇emp_unit 创建相应索引 unit_inf_ind */
/* 为创建单位信息和单位职工汇缴信息表作准备 */
/******************************************************************/
prompt 建立单位代码及职工 cluster
drop clusteremp_unit;
create clusteremp_unit(acc_no varchar2(15))
tablespace user_data
storage(initial 1mnext 1m maxextents 121 pctincrease 0 );
/
create indexunit_inf_ind on cluster emp_unit
tablespaceuser_indx;
/
/******************************************************************/
/* 创建单位信息表unit_inf,并指定表属于cluster emp_unit簇 */
/* 并为表unit_inf的acc_no列创建相应索引unit_inf_in */
/******************************************************************/
prompt 建立单位开户登记表(unit_inf)
drop table unit_inf;
create table unit_inf
(
bank_code varchar2(6) , -- 经办行代码
acc_no varchar2(15) , -- 公积金代号(帐号)
proc_date date , -- 处理时间
unit_name varchar2(50) , -- 单位名称
Area varchar2(20) , -- 所在区、县
Address varchar2(50) , -- 单位地址
Zip varchar2(6) , -- 邮政编码
Master varchar2(20) , -- 主管部门
Belong varchar2(20) , -- 隶属关系
economic varchar2(20) , -- 经济性质
tot_emp number(6) , -- 职工人数
sal_bank varchar2(40) , -- 发薪银行
sal_acc_no varchar2(20) , -- 发薪户帐户
sal_date number(2) , -- 发薪日
pay_tot number(6) , -- 汇缴人数
pay_money number(13,2) , -- 汇缴总金额
pro_depart varchar2(40) , -- 经办部门
pro_master varchar2(10) , -- 负责人
pro_cotact varchar2(10) , -- 联系人
pro_tel varchar2(15) , -- 电话
save_bank varchar2(40) , -- 公积金经办行名称
bank_add varchar2(40) , -- 经办行地址
bank_cotectvarchar2(10) , -- 经办行联系人
bank_tel varchar2(15) , -- 经办行电话
enter_stampvarchar2(40) , -- 填报单位章
enter_date date , -- 填报日期
center_datedate , -- 中心盖章日期
status_code char(1) , -- "0未缴存","1缴存","2封存","3销户"
oper_no varchar2(10),-- 操作员
unit_pay_ratenumber(7,4) ,-- 单位缴交率
per_pay_rate number(7,4) -- 个人缴交率
)
cluster emp_unit ( acc_no ) ;
/
prompt 建立 单位开户登记 唯一键: acc_no_in
create unique indexunit_inf_in on unit_inf ( acc_no )
storage ( initial 1Mnext 512k maxextents 121 pctincrease 0 )
/
/******************************************************************/
/* 创建单位职工汇缴信息表pay_lst_det,并指定到 emp_unit 簇 */
/* 并为表pay_lst_det创建相应唯一索引pay_det_in1 */
/******************************************************************/
prompt 建立开户单位汇缴清册( pay_lst_det )
drop tablepay_lst_det;
create tablepay_lst_det
(
bank_code varchar2(6)NOT NULL, -- 经办行代码
acc_no varchar2(15) not null, -- 公积金代码
emp_acc_no varchar2(20) not null, -- 职工帐号
table_date date , -- 编报日期
Name Varchar2(10), -- 姓名
Sex varchar2(2)check(sex='男' or sex='女'), -- 性别
Birth date, -- 出生年月
Per_id varchar2(20), -- 身份证号
Sal Number(7,2) not null, --月工资
Per_pay_rate Number(7,4), --个人缴交率
Per_pay Number(7,2), --个人交缴金额
Unit_pay_rateNumber(7,4), --单位交缴金率
Unit_pay Number(7,2), --单位交缴额
pay_money number(13,2) check(pay_money>=5.0), --月应缴额
status_code char(1) , -- "0未缴存","1缴存","2封存","3销户"
Oper_no Varchar2(10) --操作员代码
)
cluster emp_unit ( acc_no )
/
create unique index pay_det_in1 on pay_lst_det( per_id )
storage ( initial10M next 2m maxextents 121 pctincrease 0 )
/
create unique indexpay_det_in2 on pay_lst_det( emp_acc_no )
storage ( initial 1Mnext 512k maxextents 121 pctincrease 0 )
/
§5.1.2 改变簇
在用户具有 ALTER ANY CLUSTER 的权限情况下,可以对已建好的簇(CLUSTER )改变其设置,如:
l 物理属性:PCTFREE,PCTUSED,INITRANS,MAXTRANS和STORAGE;
l 为CLUSTER关键字值存储所有行所需的一般空间容量;
l 缺省平行度。
命令语法:
ALTER CLUSTERCluster_name
{ PCTUSED integer
| PCTFREE integer
| SIZE integer
| INITRANS integer
| MAXTRANS integer
| STORAGE Cluase
}
例:ALTERCLUSTER emp_dept
PCTFREE 30
PCTUSED 60;
对于用ALTER TABLE语句只能改变表中非簇列的设置,不能对簇列进行任何的修改。
§5.1.3 删除簇
只要用户具有 DROP ANY CLUSTER权限均可以对所有的CLUSTER进行删除。
语法如下:
DROP CLUSTER[user.]cluster [INCLUDING TABLES]
如果使用INCLUDING TABLES 则删除 CLUSTER 的同时也删除所包含的表。簇表可以被单个删除而不影响该表所属的簇、其他簇表或者簇索引。删除一个簇表如同删除一个普通的表一样都可以用DROP TABLE 来完成。
注意:当用户从簇中删除单个表时,ORACLE单独删除表中的每一行。删除整个簇的最有效的方法是使用带有 INCLUDING TABLE 选项的DROP CLUSTER语句删除包含所有表的簇。只有当用户仍想保留簇中其它表时才会使用DROP TABLE从簇中删除单个表。
§5.1.4 删除簇索引
一个簇的索引可以被删除而不影响表的数据,但是当簇的索引被删除后,属于该簇的表就变为不可用,所以当删除簇的索引后还须再建立该簇的索引才行。有时为了消除盘空间的碎片我们常进行删除簇索引操作。删除索引命令见 DROP INDEX 命令。
§5.1.5 收集簇信息
当应用在设计时使用了簇,则用户或数据库管理员都可以通过查询簇的信息来了解簇的情况,从而进行必要的管理。使用下面命令可以收集属于用户本人的簇信息:
l DBA_CLU_COLUMNS或USER_CLU_COLUMNS
l DBA_CLUSTERS或USER_CLUSTER
DBA_CLU_COLUMNS 存放有系统实例中所有簇的数据情况。它的结构如下:
SQL> descdba_clu_columns
名称 空? 类型
------------------------------------------------- ---------------
OWNER NOT NULLVARCHAR2(30)
CLUSTER_NAME NOT NULLVARCHAR2(30)
CLU_COLUMN_NAME NOT NULLVARCHAR2(30)
TABLE_NAME NOT NULLVARCHAR2(30)
TAB_COLUMN_NAME VARCHAR2(4000)
其中:
OWNER 创建簇的主人
CLUSTER_NAME 簇的名字
CLU_COLUMN_NAME 簇中的列名
TABLE_NAME 与之相关的表名
TAB_COLUMN_NAME 表中的列名
DBA_CLUSTERS 数据字典存放有簇的详细数据情况,包括存储参数等。结构如下:
SQL> descdba_clusters
名称 空? 类型
------------------------------------------------- --------------
OWNER NOT NULLVARCHAR2(30)
CLUSTER_NAME NOT NULLVARCHAR2(30)
TABLESPACE_NAME NOT NULLVARCHAR2(30)
PCT_FREE NUMBER
PCT_USED NOT NULLNUMBER
KEY_SIZE NUMBER
INI_TRANS NOT NULLNUMBER
MAX_TRANS NOT NULL NUMBER
INITIAL_EXTENT NUMBER
NEXT_EXTENT NUMBER
MIN_EXTENTS NOT NULL NUMBER
MAX_EXTENTS NOT NULL NUMBER
PCT_INCREASE NUMBER
FREELISTS NUMBER
FREELIST_GROUPS NUMBER
AVG_BLOCKS_PER_KEY NUMBER
CLUSTER_TYPE VARCHAR2(5)
FUNCTION VARCHAR2(15)
HASHKEYS NUMBER
DEGREE VARCHAR2(21)
INSTANCES VARCHAR2(21)
CACHE VARCHAR2(11)
BUFFER_POOL VARCHAR2(7)
SINGLE_TABLE VARCHAR2(11)
关于列的解释见《Oracle8i/9Ireference》。
此外,你还可以用ANALYZE命令来分析某个簇的数据情况,ANALYZE 命令语法如下:
ANALYZE CLUSTERCluster_name
{ COMPUTE STATISTICS
| ESTIMATESTATISTICS
| DELETE STATISTICS
| VALIDATE REFUPDATE
| VALIDATE STRUCTURE
| LIST Chained ROWINTO table_name
}
例:ANALYZECLUSTER students COMPUTE STATISTICS;
当然我们可以从数据字典中查询所统计的结果,如:
SELECTAVG-BLOCKS_PER_KEY,INSTANCES,CACHE,BUFFER_POOL
FROM USER_CLUSTER
WHERE CLUSTER_NAME=’STUDENT_DEPT’;
小结:
l ORACLE8 可以对表及其各自的索引建立簇;
l 可以对基于主键的簇和基于哈希函数建立簇;
l 簇码只能存储一次;
l 簇如果正确使用可以涉及复杂的操作和降低磁盘I/O;
l 簇索引必须在任何簇表装入数据之前创建;
l 只对使用等号操作符的查询表和表的大小相对静态使用哈希簇;
l 建立簇时,可以用 STORAGE 参数分配来优化簇的性能。
§5.2 分区
现代企业运行的数据库一般都达几个 GB数据量。我们把这写数据库称为超大型数据库(VLDB=Vary LargeDatabase)。ORACLE 公司从 ORACLE7 开始就提出了分区的方法。到ORACLE8后,分区技术已经提供了非常完善的功能。为大型应用系统提供了可能。
§5.2.1 分区的优点
oracle8i分区选项可以对超大规模数据库(VLDB=Vary large databast)进行分区、使之可以达到下面目的:
l 增强可用性,如果表的一个分区由于系统故障或者维护而得不到使用时,表的其余部分仍是可用的
l 减少关闭时间,如果系统故障只影响表的某部分,那么只有这部分需要修复,因此修复工作量减少,所以更快地完成
l 维护轻松,如果需重新连表,那么独立地管理而不是单个大型表的操作要轻松得多。
l 均衡的I/O,可以把不同分区映射到磁盘以平衡I/O并显著改善性能。
l 改善性能,对已分区对象的某些查询可以运行更快,因为搜索仅限于关心的分区。
|
表和索引分区的示意图
不是所有表都可以被分区,下面是不可分区的情况:
l 存在于oracle8簇(cluster)中的表不能被分区
l
l 如果一个表包含非结构性数据,则这个表不能被分区,下面的数据类型就不能进行分区:
l 1)LOBS
l 2)LONG RAW
3)对象类型
l 索引组织的表不能被分区。
§5.2.2 分区的方法
ORACLE 提供了对表或索引的三种分区方法:
l 范围分区;
l 散列(hash)分区;
l 复合分区。
1.范围分区方法
根据表中列值的范围进行分区,如一年中的月份,当数据在范围内均匀分布时,性能最好,否则应考虑其它的分区方法。
当创建范围分区时,要考虑:
l 分区方法;范围
l 分区列
l 分区中说明指定分区边界
2.使用散列分区
如果数据不容易进行范围分区,而出现性能原因时就要进行散列分区。
3.复合分区法
复合分区的分区数据使用范围分区法,而在每个分区或子分区内使用散列分区。
§5.2.3 创建表的分区
1.范围分区法
1)表的单列分区
create tablestudents
(
student_id integer not null,
student_first_name varchar2(25),
student_last_name varchar2(25),
student_dept_id integer,
student_address varchar2(50),
student_city varchar2(25),
…
)constraint Dept_id_pk primary key (student_dept_id)
partition By Range (student_dept_id)
(partition Dept_id_1 values less than(100) tablespace om1,
partition Dept_id_2 values less than (250) tablespace om2,
partition Dept_id_3 values less than (500) tablespace om3
partition Dept_id_4 values less than (750) tablespace om4
partition Dept_id_5 values less than (max value)table space om5);
Student 表根据students_Dept_id到值进行分区创建的五个分区的名字和范围值如下:
Dept_id_1-范围0-99
Dept_id_2-范围100-249
Dept_id_3-范围250-499
Dept_id_4-范围500-749
Dept_id_5-范围750-MAXVALUE
可以用oracleschema Manager工具查看表的分区结果。
注:如果划分有空值,应指定MAXVALUE,这样oracle8i将具有空值的列排到大于其它分区值之后,但此MAXVALUE小。
2)表的多列分区
oracle8i把多列分区码值作为向量来决定一行应放在哪个分区,如:
例1:
create tablestudents
(
student_id integer not null,
student_first_name varchar2(25),
student_last_name varchar2(25),
student_dept_id integer,
student_address varchar2(50),
student_city varchar2(25),
…
)constraint Dept_id_pk primary key (student_dept_id)
partition By Range(student_id,student_Dept_id)
partitionDept_id_1 values lessthan(1000,100)tablespace om1,
partitionDept_id_2 values lessthan(2000,250)tablespace om2,
partitionDept_id_3 values lessthan(3000,500)tablespace om3,
partitionDept_id_1 values lessthan(4000,750)tablespace om4,
partition Dept_id_1values less than(MAXVALUE,MAXVALUE)tablespace om5);
如果一行码值是(2500,150),则该行插入Dept_id_2分区中,因为码(2500,x)小于(300,x)且比(1000,100)大。
例2:
CREATE TABLE sales
(invoice_no NUMBER,
sale_year INT NOTNULL,
sale_month INT NOTNULL,
sale_day INT NOT NULL )
PARTITION BY RANGE(sale_year,sale_month,sale_day)
(
PARTITION sales_ql VALUES LESS THAN (1999,04,01)
TABLESPACE tsa,
PARTITION sales_q2 VALUES LESS THAN (1999,07,01)
TABLESPACE tsb,
PARTITION sales_q3 VALUES LESS THAN (1999,10,01)
TABLESPACE tsc,
PARTITION sales_q4 VALUES LESS THAN (2000,01,01)
TABLESPACE tsd
)
3)等同分区
把表对应的索引按照表的分区确定索引的分区称等同分区,如students表使用
student_id和student_Dept_id来分区,则该表的索引也使用这二列来分区.优点有:
l oracle8i在复杂的连接,排序操作中改善分区表的执行计划。
l 由于相关表和索引可同时恢复,减少介质恢复时间。
2.使用散列分区法
散列分区提供了一种通过指定分区编号来均匀地分布数据的方法。基于分区键的散列将被映射到分区上。为创建和使用散列分区给用户提供了高度灵活的放置数据的方法,因为通过在I/O设备上进行散列分区,使得这些分区在大小上一致。
建立散列分区,需要指定下面信息:
l 分区方法:散列;
l 分区列;
l 分区编号或各个分区的说明。
使用 CREATE TABLE 语句加PARTITIONBY HASH 子句可以指定所建的表被散列分区。
下面例子创建一个散列分区。分区列为ID,创建了四个分区并分配了由系统生成的分区名,它们被放置在四个被命名的表空间gear1,gear2,gear3,gear4上。
例1:
CREATE TABLEscubagear
( id NUMBER,
name VARCHAR2(60)
)
PARTITION BYHASH(id)
PARTITIONS 4
STORE IN (gear1,gear2,gear3,gear4);
散列分区表的每个分区内保存在不同的节中,且散列数据被放在缺省表空间中。
例2:
CREATE TABLE (deptno NUMBER,deptname VARCHAR2(32))
STORAGE( INITIAL 10k)
PARTITION BYHASH(deptno)
(
PARTITION p1TABLESPACE ts1,
PARTITION p2TABLESPACE ts2,
PARTITION p3TABLESPACE ts1,
PARTITION p4TABLESPACE ts3
);
指定了各个分区的名字及它们所存放的表空间。所有分区都继承表空间的初始分配 INITIAL 10k 参数。
3.复合分区方法
复合分区的分区方法数据使用范围分区,而在每个分区或子分区内使用散列分区法。复合分区对历史数据和带状数据都很理想,并提供范围分区和数据放置的改进的管理性能和散列分区的并行优点。
建立复合分区,须进行:
l 分区方法:范围分区;
l 分区列;
l 指定分区边界的分区说明;
l 子分区方法;散列;
l 子分区列;
l 每个分区的子分区数量或子分区说明。
例:下面例子创建了三个范围分区,每个分区又包含八个子分区,子分区没有名字,所以赋予它们系统生成的名字,“STORE IN”子句将它们分布在四个指定的表空间 ts1,ts2,ts3,ts4中。
CREATE TABLEscubager(equipno NUMBER,equipname VARCHAR(32),price NUMBER)
PARTITION BYRANGE(equipno)SUBPARTITION BY HASH(equipname)
SUBPARTITIONS 8STORE IN ( ts1,ts2,ts3,ts4)
(
PARTITION p1 VALUESLESS THAN(1000),
PARTITION p2 VALUESLESS THAN(2000),
PARTITION p3 VALUESLESS THAN(MAXVALUE)
);
4. 对表分区小结
把表的数据范围(range)进行分区存储;
只需在Createtable或Alter table命令中指定即可;
分区名在Createtable或Alter table中指定(给出)即可,不需在任何另外的地方预先说明。
l 建立表结构时指定区
Create table emp
(emp_no number(5),
dept varchar2(2),
name varchar2(30))
storage(initial 100k next 50k) logging
PARTITION BY RANAE (enp_no)
(PARTITION acct values less than (1000) tablespace ts1,
PARTITION sales values less than (2000) tablespace ts2,
PARTITION edue values less than (3000));
Insent into empvalues (1226, ′sa′,′smith′);
Insent into empvalues (2100, ′ed′,′jones′);
l 对具有分区的表更新,有以下不足:
1) 不能进行的更新
up date emp set emp_no=1500 where name= ′JONES′;
此条语句将不能运行(引起错误),原因是′Jones′原来是(emp_no=2100)对应的分区为educ,而现要将其从educ分区移至sales中,所以不能垮分区修改。
2) 不指定分区的更新(影响效率)
update emp set emp_no=1356 where name = ′SMITH′;
3) 指定分区的更新(提高效率)
update sale partition(feb96)
set s.account_name=upper(s.account_name);
l 对具有分区的表进行删除(指定分区效率更高)
Delete From sales Partition (nov96)
Where amount_of_sale !=0;
l 对具有分区的表进行查询
Select * from emp partition acct
Where emp_no=999;
l 用日期字段作分区条件(详细见《oracle8 sql Reference》)
Create table stock_xaction
(stock_symbot CHAR(5),
stock_series CHAR(1),
num_shares Number(10)
price Number(5,2)
trade_date Date
Storage (inltial 100k next 50k)logging
PARTITION By range (trade_date)
(PARTITION sx1992 values less than (to_date(′01-JAN-1993′,
′DD-MON-YYYY′))Tablespace tso Nologging,
PARTITION sx1993 values less than(To-date(′01-JAN-1994′,
′DD-MON-YYYY′))Tablespace ts1,
PARTITION sx1994 values less than (To-date(‘01-JAN-1995′,
′DD-MON-YYYY′))Tablespace ts2);
l 用Alter对表进行分区
可以用下面语句实现分区指定:
MODIFY PARTITION partition-name [phsical-attributes-clause]
§5.2.3 创建索引的分区
可以创建两种类型的索引分区:
l 局部索引
l 全部索引
这两种均遵从下列原则:
l 分区的索引表不能应用聚类表
l 位图索引必须为局部索引
l 分区和未分区的索引可以应用于分区或未分区的表
1. 局部索引
根据表的分区个数来建立同样的索引分区叫局部索引,如:
( create index Dept_id_1 tablespace om1,
create index Dept_id_2 tablespace om2,
create index Dept_id_3 tablespace om3,
create index Dept_id_4 tablespace om4
create index Dept_id_5 tablespace om5
);
每个局部索引都与其基于的表等同分区。局部索引有下列优点:
l 如果只一个分区需要维护,则只有一个局部索引受影响
l 支持分区独立性
l 只有局部索引能支持单一分区的装入和卸出
l oracle8更好的查询
l 表分区和各自的局部索引可同时恢复
l 局部索引可以单独重建
l 位图索引仅由局部索引支持
根据前面STUDENTS表被划分为5个区DEPT_ID_1,... DEPT_ID_5 。按照上面对各个分区的进行局部索引的创建,则形象如下图:
|
局部索引DEPT_IDX ( Dept_idx图)
2. 全局索引分区
对表的多个分区建立索引分区叫全局索引(分区)一般可以这样认为,未作分区的索引被认为是全局索引如:
create index Dept_idx on students(student_dept_id)
GLOBAL partition By range (student_dept_id)
(partition Dept_id_1 values less than (1000,100)tablespace om1,
partition Dept_id_2 values less than (2000,250)tablespace om2,
partition Dept_id_3 values less than (3000,500)tablespace om3,
partition Dept_id_4 values less than (4000,750)tablespace om4,
partition Dept_id_5 values less than(MAXVALUE,MAXVALUE) tablespace om5;
下面图表示全局索引Dept_idx的情况
|
全局索引Dept_idx(Dept_idx2图)
3. 相关的数据字典
DBA_IND_PARTITIONS
DBA_TAB_PARTITIONS
DBA_PART_COL_STATISTICS
USER_TAB_PARTITONS
USER_PART_COL_STATISTICS
USER_IND_PARTITIONS
ALL_TAB_PARTITIONS
ALL_IND_PARTITIONS
ALL_PART_COL_STATISTICS
§5.2.4 维护表分区和索引分区
在了解如何建立表分区和索引分区后,在应用设计中还经常进行分区的维护。下面是维护分区的简要介绍。
1.用 ALTER TABLE 语句来维护表分区
维护操作 | 范围 | 散列 | 复合 |
加入分区 | ADD PARTITION | ADD PARTITION | ADD PARTITION MODIFY PARTITION …ADD SUBPARTITION |
合并分区 | N/a | COALESCE PARTITION | MODIFY PARTITION …ADD COALESCE SUBPARTITION |
删除分区 | DROP PARTITION | N/a | DROP PARTITION |
互换分区 | EXCHANGE PARTITION | EXCHANGE PARTITION | .EXCHANGE PARTITION .EXCHANGE UBPARTITION |
合并(merge)分区 | MERGE PARTITION | N/a | MERGE PARTITION |
修改分区缺省属性 | MODIFY DEFAULT ATTRIBUTES | MODIFY DEFAULT ATTRIBUTES | .MODIFY DEFAULT ATTRIBUTES .MODIFY DEFAULT ATTRIBUTES FOR PARTITION |
修改分区实际属性 | MODIFY PARTITION | MODIFY PARTITION | .MODIFY PARTITION .MODIFY SUBPARTITION |
移动分区 | MOVE PARTITION | MOVE PARTITION | MOVE SUBPARTITION |
重命名分区 | RENAME PARTITION | RENAME PARTITION | . RENAME PARTITION . RENAME SUBPARTITION |
拆分分区 | SPLIT PARTITION | N/a | SPLIT PARTITION |
截短分区 | TRUNCATE PARTITION | TRUNCATE PARTITION | .TRUNCATE PARTITION . TRUNCATE SUBPARTITION |
2.用 ALTER INDEX 语句来维护索引分区
维护操作 | 索引 类型 | 索 引 分 区 类 型 范围 散列 复合 | ||
删除索引 分区 | 全局 本地 | DROP PARTITION N/a |
N/a |
N/a |
修改索引 分区的缺 省值 | 全局 本地 | .MODIFY DEFAULT ATTRIBUTES .MODIFY DEFAULT ATTRIBUTES | .MODIFY DEFAULT ATTRIBUTES | .MODIFY DEFAULT ATTRIBUTES .MODIFY DEFAULT ATTRIBUTES FOR PARTITION |
修改索引分区的实际属性 | 全局 本地 | .MODIFY PARTITION .MODIFY PARTITION |
MODIFY PARTITION |
MODIFY SUBPARTITION |
重建索引分区 | 全局 本地 | REBILD PARTITION |
REBILD PARTITION |
REBILD PARTITION |
重新命名 索引分区 | 全局 本地 | RENAME PARTITION RENAME PARTITION |
RENAME PARTITION |
RENAME PARTITION |
拆分索引分区 | 全局 本地 | SPLIT PARTITION N/a |
N/a |
N/a |
§5.3 簇与分区有关的数据字典
如果你在应用系统设计时采用了分区和簇技术,则你最好要了解与簇和分区有关的数据字典。这样对管理有好处。
§5.3.1 分区、簇数据字典列表
下面给出的数据字典有三类,分别以DBA、ALL和USER开头,如果你具有DBA权限,你只要了解DBA开头即可。下面仅给相关数据字典的名字,具体的列名及介绍请见《Oracle8i/9i数据库管理》。
l ALL_CLUSTERS、DBA_CLUSTER、USER_CLUSTER 存放簇的基本信息。
l DBA_TABLES 存放索引表的信息,其中cluster_name 表示该表所对应的Cluster名字。
l ALL_TAB_PARTITIONS、DBA_TAB_PARTITIONS、USER_TAB_PARTITIONS 存放有关分区的基本信息,如表名,分区名等。
l ALL_IND_PARTITIONS、DBA_IND_PARTITIONS、USER_IND_PARTITIONS有关索引分区的信息。
l ALL_PART_INDEXES、DBA_PART_INDEXES、USER_PART_INDEXES可访问的分区的索引。
l ALL_PART_TABLES、DBA_PART_TABLES、USER_PART_TABLES存放可访问的分区表的信息。
l ALL_PART_LOBS、DBA_PART_LOBS、USER_PART_LOBS存放大对象类型的分区信息。
§5.3.2 基本的分区、簇信息查询
了解数据字典的目的是查询它们的信息和管理。下面给出几个常用查询例子。
例1.查询与簇有关的表的信息:
SQL> coltable_name for a20
SQL> coltablespace_name for a20
SQL> selectOWNER,table_name,TABLESPACE_NAME,CLUSTER_NAME from dba_tables
2* where CLUSTER_NAME is not null;
OWNER TABLE_NAME TABLESPACE_NAME CLUSTER_NAME
---------------------------------------- -------------------- --------------
SYS IND$ SYSTEM C_OBJ#
SYS CLU$ SYSTEM C_OBJ#
SYS ICOL$ SYSTEM C_OBJ#
SYS FET$ SYSTEM C_TS#
SYS CDEF$ SYSTEM C_COBJ#
. . . . . .
已选择33行。
例2.查询进行过分区的表的信息:
SQL> selectTABLE_OWNER,TABLE_NAME,PARTITION_NAME,TABLESPACE_NAME
2 fromDBA_TAB_PARTITIONS;
未选定行
SQL>
第六章 使用SQL进行数据操作
大家都知道,关系数据库中使用最频繁的SQL语句可以是SELECT 、INSERT、UPDATE及DELETE语句了,尽管它们使用简单,但也是变化最多和最能衡量一个编程人员水平的语句了。下面就增、删、改作简要介绍。
§6.1 INSERT操作
INSERT 语句可以完成对表、视图及快照(snapshot)进行数据插入。插入的数据依不同的版本而允许插入的数据类型也不同,最新版本可以在子查询中使用 LOB数据类型。现在最新的版本仍有下面限制:
l 不能在语句中使用并行,也不允许从远程进行插入;
l 在子查询中不允许使用带有long 类型的字段。
1.INSERT命令语法:
INSERT INTO[user.]table[@db_link][(column1[,column2]...)]
VALUES (express1[,express2]...|subquery...);
2 . 日期的插入
INSERT into emp_house_fund(name,emp_acc_no,tran_date,tran_val)
VALUES('赵元杰','123456',to_date('06/09/2000','dd/mm/yyyy'),99.9);
INSERT intoemp_house_fund(name,emp_acc_no,tran_date,tran_val)
VALUES('赵元杰','123456',to_date('2000.06.09','yyyy.mm.dd'),99.9);
INSERT into emp_house_fund(name,emp_acc_no,tran_date,tran_val)
VALUES('赵元杰','123456',to_date('06092000','ddmmyyyy'),99.9);
3.带select 的插入
SQL> create tableemp_house_fund_sum( per_id verchar2(20), tran_val number(9,2));
SQL> insert intoemp_house_fund_sum
select per_id,sum(tran_val)
From emp_house_fundgrou by per_id;
SQL>selecta.name, b.per_id,b.tran_val
from emp_house_funda,emp_house_fund_sum b
wherea.per_id=b.per_id;
§6.1.1 用文字插入操作
INSERTINTO dept
VALUES(50, ‘PRODUCTION’,’SANFRANCISCO’);
INSERTINTO emp (empno, ename, job, sal, comm, deptno)
VALUES(7890, ‘LINKS’,’CLERK’,1.2E3,NULL, 40);
下面语句完成同样的功能,只是用了子查询:
INSERTINTO (SELECT empno, ename, job, sal, comm, deptno FROM emp)
VALUES(7890, ‘LINKS’,’CLERK’,1.2E3,NULL, 40);
§6.1.2 用子查询插入操作
INSERTINTO bonus
SELECTename, job, sal, comm
FROMemp
WHEREcomm > 0.25 * sal
ORjob IN (‘PRESIDENT’,’MANAGER’);
分区的例子:
插入到 SALES表的 OCT98 分区中:
INSERTINTO sales PARTITION (oct98)
SELECT* FROM latest_data;
绑定变量的例子:下面例子将插入结果返回到变量:
INSERTINTO emp VALUES (empseq.nextval, ‘LEWIS’ ‘CLARK’
7902,SYSDATE, 1200, NULL, 20)
RETURNINGsal*12, job INTO :bnd1, :bnd2;
插入 LOB 数据类型的例子:
1)插入一个带 long raw 的表:
CREATETABLE long_tab (long_pics LONG RAW);
2)建立一个带 LOB的表:
CREATETABLE lob_tab (lob_pics BLOB);
3)用下面预计完成将 long raw 插入到 LOB 中:
INSERTINTO lob_tab (lob_pics)
SELECTTO_LOB(long_pics) FROM long_tab;
4)可以将带有long raw 的列删除掉:
ALTERTABLE long_tab DROP COLUMN long_pics;
插入BFILE 的例子:
INSERTINTO emp
VALUES (1, BFILENAME (‘a_dir_alias’, ‘a_filename’);
§6.2 UPDATE操作
1. UPDATE语法
UPDATE[user.]table[@db_link][alias]
SET {column1=express1[,column2=experss2]...|
(column1[,column2]...)=(subquery)}
[WHEREcondition|current of cursor];
2.一般的修改
如果起息日为空时以处理日作为该记录的起息日:
SQL>update emp_house_fund set tran_date=sysdate
Whereproc_date=sysdate and tran_date is null;
3. 带null 的修改
SQL>update empset per_id=null where length(per_id)<15 or
Substr(per_id,15,1)> 1;
§6.2.1 用文字更新操作
在一般情况下,我们经常使用文字更新操作,即在UPDATE 语句中直接将要更新的数值写在语句中,如:
UPDATE emp
SET job = ’MANAGER’, sal = sal + 1000, deptno = 20
WHERE ename = ’JONES’;
UPDATE accounts@boston
SET balance = balance + 500
WHERE acc_no = 5001;
限制:
l 不能在并行语句中使用或在远程进行更新;
l 不能在更新子句中对LONG类型进行更新。
§6.2.2 用查询更新操作
在Oracle中,可以根据查询结果来更新表的数据。比如,在申请基金项目时,不允许一个人在大学申请项目又在大学的研究所中申请项目,则可使用下面例1的语句来实现检查。
例1:
SQL>update colle_subjectsset app_flag='0'
Where per_id in (select per_id from univ_subjects );
例2:
SQL>updatecomfort set ( noon, midnigt )=
(selecthumiddity,temperature from weather
where city='MANCHESTER')
where City='WALPOLE'and
Sampledate=to_date('22-dec-2000','DD-MON-YYYY');
例3:
UPDATE sales PARTITION(feb96) s
SET s.account_name =UPPER(s.account_name);
§6.2.3 用相关子查询更新操作
同样可以在更新中使用相关的查询操作,如:
例1.复杂UPDATE语句:
UPDATE emp a
SET deptno =
(SELECT deptno
FROM dept
WHERE loc = ’BOSTON’),
(sal, comm) =
(SELECT 1.1*AVG(sal),1.5*AVG(comm)
FROM emp b
WHERE a.deptno = b.deptno)
WHERE deptno IN
(SELECT deptno
FROM dept
WHERE loc = ’DALLAS’
OR loc = ’DETROIT’);
例2:相互更新的例子:
UPDATE TABLE(SELECT projs
FROM dept d WHERE d.dno =123) p
SETp.budgets = p.budgets + 1;
§6.3 DETELE操作
在应用中,可以使用delete 语句实现将不需要的记录进行删除。值得注意的是,经常使用DELETE 语句对表的记录进行有条件的删除。而无条件的全表删除需要一定的技巧。请看下面的例子。
§6.3.1 用delete删除全部记录的操作
1. DELETE 语法
DELETE [ FROM ][user.]table [@db_link][Alias] [WHERE condition];
这里的condition 可以复杂的表达式或子查询。
2.例子:
SQL> delete fromhouse.emp_house_fund;
SQL>delete fromcolle_subjects
Where per_id in ( select per_id from univ_subjects );
§6.3.2 用delete有条件删除部分记录
删除语句用的最多应该是有条件的删除记录。如果我们不是有条件的删除的话,我们应该采用直接建立一个新表、DROP 旧表的方法来达到我们的目的。
例1:
DELETE FROM temp_assign;
例2:
DELETE FROM emp
WHERE JOB = ’SALESMAN’
AND COMM < 100;
例3:
DELETE FROM (select * from emp)
WHERE JOB = ’SALESMAN’
AND COMM < 100;
限制:
l 不能使用并行或带远程对象
l 不能用LONG类型
§6.3.3 用delete分段删除大量记录
有时我们需要删除大块的数据,在环境比较差的情况下,往往由于回滚段不够大而出现删除失败。这样我们可以采用以下两种方法来达到删除大量数据的目的。
例1:用rownum来限制每次删除的记录数目:
DELETE from emp where deptno=9999 and rownum < 1000;
例2:编写一个简单的存储过程,如:
SQL>get del_rec
DECLARE TMP VARCHAR2(1);
BEGIN
LOOP
BEGIN
/* 下面语句确定是否还有可删的记录,如果没有就直接到EXCEPTION */
SELECT ‘X’ INTO TMP FROM SAMPLE
WHERE ROWNUM = 1;
/* 如果SAMPLE还有数据就执行删除一组操作,即使不够20条也不会出错 */
DELETE FROM SAMPLE
WHEREROWNUM<20;
EXCEPTION
WHEN NO_DATA_FOUNDTHEN
EXIT;
END;
END LOOP;
END;
§6.4 insert、delete及update 的提交和撤消
与桌面数据库最大的区别是,所有的关系数据库在事务处理操作中,可以对每个事务进行提交或撤消。当我们在进行insert、update、delete时,都可以根据当前的处理情况来确定目前的操作是提交或撤消。
§6.4.1 自动提交的设置
为了保证在SQL>方式下进行INSERT、DELETE和UPDATE操作达到安全的目的,一般在SQL>下进行INSERT、DELETE和UPDATE操作前,建议将环境设置成为非自动提交的方式。
我们可以在SQL>下用 show autocommit 命令查看当前该参数的状态。如果状态是ON,则建议用下面命令将其设置为 OFF状态,这样做主要目的是:即使误删除某些记录也可以用ROLLBACK命令进行恢复。
l 查看当前的提交状态:
SQL>show autocommit
autocommit OFF
l 设置是否自动提交的命令:
SET AUTO[commit]{ON|OFF|IMM{[EDIATE]|n}(oracle8
SET AUTO[commit]{ON|OFF|IMM{[EDIATE]}(oracle7
当设为imm时,每作一个修改操作均自动提交。
当设n(n为一个整数),当作过n个修改时自动提交。
l 设置为非自动提交:
SQL>setautocommit off
l 设置为自动提交:
SQL>setautocommit on
l Commit
Commit Complete
(这样的命令表示以前的操作到目前为止都进行提交,不是仅提交当前的操作)
l rollback
rollback complete
(只回滚那些未被提交的且最近一次的那些事务)
l 隐含提交
虽然有的操作并不发出commit,但我们用exit 退出后系统会自动提交,目前exit等价于quit,以前oracle v5如果用quit退出,则所作修改不提交。
l 自动回滚
oracle在遇到异外情况下,如断电,系统故障时,自动采取回滚。
例。
SQL>show user
User为“SCOTT”
SQL>show autocommit
Autocommit off
SQL>
SQL> update emp set sal = sal*1.2 wheredeptno=10;
已更新2行。
SQL> rollback;
重算已完成。
§6.4.2 保留点和撤消
我们可以使用 ROLLBACK 命令撤消最近一次所做的操作,要想实现对前面的所有各个操作也要撤消的话,就必须在每个操作命令前设置相应的保留点。当对每个操作都设置了相应的保留点,我们就可以根据需要用ROLLBACK TO xx 来实现撤消到某一步。
l SAVEPOINT命令
SAVEPOINTsavepoint_work;
其中:savepoint_work 是要设置的保留点标识。
l ROLLBACK命令
ROLLBACK [WORK ] [TO[SAVEPOINT] savepoint_work]
其中:savepoint_work 是要设置的保留点标识。
例子:
-- 看当前用户的名字
SQL> show user
USER 为"ZHAO"
-- 先查看emp表的记录数
SQL> select * from emp;
ENAME SAL DEPTNO TEL
-------------------- ------------ ------------ -----------------
赵元杰 99999.12 10 1360 136 5681
张x 8888.88 10 12345
-- 设置第1个保留点a
SQL> savepoint a;
保存点已创建。
--插入第1条记录
SQL> insert into empvalues('John',123.45,20,'56789');
已创建 1 行。
-- 设置第2个保留点b
SQL> savepoint b;
保存点已创建。
--插入第2条记录
SQL> insert into empvalues('Scott',456.78,20,'13601361234');
已创建 1 行。
-- 设置第3个保留点c
SQL> savepoint c;
保存点已创建。
-- 插入第3条记录
SQL> insert into empvalues('赵x',3210.1,20,'62348901');
已创建 1 行。
-- 查询到目前为止的emp表记录数
SQL> select * from emp;
ENAME SAL DEPTNO TEL
-------------------- ---------- ---------- --------------------
赵元杰 99999.12 10 1360 136 5681
张x 8898.88 10 12345
John 123.45 20 56789
Scott 456.78 20 13601361234
赵x 3210.1 20 62348901
-- 要求撤消到第2步,即撤消到保留点b
SQL> rollback to b;
重算已完成。
-- 再查询撤消后的emp 表记录情况
SQL> select * from emp;
ENAME SAL DEPTNO TEL
-------------------- ---------------- ----------------------------
赵元杰 99999.12 10 1360 136 5681
张x 8898.88 10 12345
John 123.45 20 56789
SQL>
注意:撤消的处理必须是在没有发出COMMIT命令的前提下才能有效。
第七章 复杂查询语句的使用
在关系数据库中,select 语句是使用最频繁的语句。它的处理复杂程度可依据业务的要求的不同而不同。它是程序员和管理员必需的语句。前面已经进行过介绍,但下面从较为复杂的程度来介绍它的使用情况。
§7.1 复杂查询语句的使用
使用SELECT语句和子查询(SUBQUERY)可以从一个或多个表、视图、实体视图中返回数据。
§7.1.1 相关子查询
可以将子查询(前面提到的 as subquery )或In 或exists当成 where 的一个条件的一部分,这样的查询称作子查询。
l where中可以包含一个select语句子查询;
l where中可以包含IN, EXISTS 语句;
l 最多可嵌套16层;
l 层数过多会影响性能。
例:比如一个查询是否有专家既以研究所的名义申请基金项目又以大学系为单位申请项目(按规定只能以一个单位来申请):
SQL>select name,per_id,dept_namefrom univ_subjects
Where per_id in ( select per_id from colle_subjects );
§7.1.2 外连接
招生中,如果所有学生的信息放在 students 表中,而部分有特长的学生在另一个表student_skill 中同样有该学生信息。现在要全部列出所有学生,如果某个学生在表student_skill 中有其特长信息,就显示特长内容,如果某个学生没有特长(在表student_skill中无其特长信息)就显示特长为空:
SQL>selecta.st_id, name, age,skill from students a, student_skill b
Wherea.st_id=b.st_id(+)
Order by a.name;
Students 结构为:
St_id varchar(20),
Name varchar2(10),
Age number(2),
Tot_score number(3),
... ...
Student_skill 结构为:
St_id varchar(20),
Skill varchar2(20),
... ...
Students 的记录,Student_skill的记录少。上面的“+”跟在记录少的表后面,它表示当没有与a.st_id 匹配时就为 b.st_id增加一空行。
St_id name tot_score skill
---------- ---------- ------------- ------------------
1111 aaaa 600 足球
2222 bbbb 590 篮球
3333 cccc 620
4444 dddd 610 跳高
... ...
§7.1.3 自我连接
自我连接是在同一个表或视图内进行条件连接。下面语句返回的是每个雇员的名字及该雇员的经理的名字:
SELECT e1.ename||’ works for ’||e2.ename
"Employees and their Managers"
FROM emp e1, emp e2 WHERE e1.mgr = e2.empno;
Employees and their Managers
-------------------------------
BLAKE works for KING
CLARK works for KING
JONES works for KING
FORD works for JONES
SMITH works for FORD
ALLEN works for BLAKE
WARD works for BLAKE
MARTIN works for BLAKE
SCOTT works for JONES
TURNER works for BLAKE
ADAMS works for SCOTT
JAMES works for BLAKE
MILLERworks for CLARK
连接条件是 e1.mgr =e2.empno
§7.1.4 UNION,INTERSECT及MINUS
有时需要从多个表中组合具有一种相似类型的信息。Union 可以完成将两个以上的表的相类似的查询结果合并在一起,并且相同的只取其一;如果unionall 则表示返回所有行(不管是否重复)。Intersect返回在两个表中都有相同内容的信息。Minus 则返回只在一个表中出现的信息。
1. 语法:
select ...
union[all]
select ...
select ...
intersect
select ...
select ...
minus
select ...
例:
sql>
select sum(balance) into lf_return from
(
selectsum(nvl(tran_val,0)*decode(db_cr_flag,'1',1,'0',-1,0) ) balance
from per_fix_det
where acc_no=as_acc_no
union all
selectsum(nvl(tran_val,0)*decode(db_cr_flag,'1',1,'0',-1,0) ) balance
from per_detail
where acc_no=as_acc_no
);
列出有特长的考生(在表students,student_skill同时出现):
sql>select namefrom sutdents
intersect
select name from student_skill;
列出没有特长的考生(仅在表students出现):
sql>select namefrom sutdents
minus
select name from student_skill;
§7.2 创建复杂的视图
许多应用系统都可能有统计等功能,没有经验的开发者可能在开发工具端来写这些语句。比如在POWER BUILDER 中加上复杂的统计与排序等处理。其实,这样很不好。建议把这些复杂语句写成视图更好。下面就介绍几个常用的视图。
§7.2.1 分组视图
例1:
CREATE OR REPLACEVIEW dept_tot as
select a.dname dept,sum(b.sal)total_sal from dept a,emp b
wherea.deptno=b.deptno group by a.dname
例2:
CRATE OR REPLACEITEMTOT AS
SELECTPERSION,SUM(AMOUNT) ITEMTOT
FROM LEDGER
WHERE ACTIONDATEBETWEEN
TO_DATE(‘01-MAR-1901’,’DD-MON-YYYY’) and
TO_DATE(‘31-MAR-1901’,’DD-MON-YYYY’)
And ACTION IN ( ‘BOUGHT’,’RAID’)
GROUP BY PERSION;
§7.2.2 合计视图
例:
CREATE VIEW empvi AS
SELECT DEPTNO,SUM(SAL),SUM(COMM)
FROM EMP GROUP BY DEPTNO;
§7.2.3 组合视图
例:
CREATE OR REPLACEVIEW BYITEM AS
SELECTL.persion persion.item,
Amount,
100*amount/itemtotalbypersion, 100*amount/total bytotal
from ledgerl L,iteamtotal I, total
whereL.persion=I.persion
and actiondatebetween
to_date(‘01-MAR-1901’,’DD-MON-YYYY’) and
TO_date(‘31-MAR-1901’,’DD-MON-YYYY’)
And ACTION IN(‘BOUGHT’,’PAID’);
§7.3 家族树
ORACLE 提供了一个有趣的功能 connect by 子句,它可以对具有家族树结构的分枝进行排序。它的用途有 机构或公司的各层结构,财务的科目代码等。
要使用查询遍历,需要在将数据在基表中按照层次结构进行存储。比如一个组织机构就是这样的典型例子。
实现语句:
SELECT column
FROM table_name
START WITH column=value
CONNECT BY PRIOR 父主键=子外键
§7.3.1 排除单一体和分枝
例1:在ORACLE的EMP 表中,每一条记录都有一个唯一标识当前雇员的empno和标识这个雇员的经理的mgr列。如果mgr 为空,则该雇员是该机构的最顶级。现在要列出每个雇员的层次结构(从顶到底):
select lpad(' ',4*(level-1))||ename name ,empno,mgr fromemp
start with mgr isnull
connect by prior empno=mgr;
NAME EMPNO MGR
-------------------- --------- ---------
JONES 7566 7839
SCOTT 7788 7566
ADAMS 7876 7788
FORD 7902 7566
SMITH 7369 7902
BLAKE 7698 7839
ALLEN 7499 7698
WARD 7521 7698
MARTIN 7654 7698
TURNER 7844 7698
JAMES 7900 7698
CLARK 7782 7839
MILLER 7934 7782
14 rows selected.
SQL>
从查询结果中可以看出,由于JONES、BLAKE、CLARK的上司是KING,所以JONES等MGR(经理编号)=KING的empno号,即KING的直接下级是JONES、BLAKE、CLARK,因为他们的MGR与KING的EMPNO一样。
§7.3.2 遍历至根
例2:现在要从某个雇员往他的上级列出该雇员的层次结构(从顶到底):
SQL> col enamefor a30
SQL> l
select lpad('',4*(level-1))||ename ename,mgr,empno from emp
start withmgr=7788
connect by priormgr= empno
SQL> /
ENAME MGR EMPNO
------------------------------ ----- ---------
ADAMS 7788 7876
SCOTT 7566 7788
JONES 7839 7566
KING 7839
例3:现在要列出所有雇员的层次结构(从顶到底):
select lpad('',4*(level-1))||ename name ,empno,mgr from emp
start with mgr isnot null
connect by empno=prior mgr
NAME EMPNO MGR
-------------------- --------- -----
SMITH 7369 7902
FORD 7902 7566
JONES 7566 7839
KING 7839
ALLEN 7499 7698
BLAKE 7698 7839
KING 7839
WARD 7521 7698
BLAKE 7698 7839
KING 7839
JONES 7566 7839
KING 7839
MARTIN 7654 7698
BLAKE 7698 7839
KING 7839
BLAKE 7698 7839
KING 7839
CLARK 7782 7839
KING 7839
SCOTT 7788 7566
JONES 7566 7839
KING 7839
TURNER 7844 7698
BLAKE 7698 7839
KING 7839
ADAMS 7876 7788
SCOTT 7788 7566
JONES 7566 7839
KING 7839
JAMES 7900 7698
BLAKE 7698 7839
KING 7839
FORD 7902 7566
JONES 7566 7839
KING 7839
MILLER 7934 7782
CLARK 7782 7839
KING 7839
38 rows selected.
§7.4 在from 中使用视图
在SELECT 语句中,当对一个表或视图查询时,语句要求视图是必须存在的。在ORACLE7.2版以后。当该视图不存在时,我们可以在 FROM子句后写上该视图即可。
详细例子如下:
1.假设有下面视图:
CREATE OR REPLACEVIEW TOTAL AS
Select SUM(amount)TOTAL
From ledger
Where actiondatebetween
To_date(‘01-MAR-1901’,’DD-MON-YYYY’) and
To_date(‘31-MAR-1901’,’DD-MON-YYYY’)
And action in(‘BOUGHT’,’PAID’);
2.我们可以像下面来使用视图( FROM 之后的TOTAL是视图):
SELECTPERSON,AMOUNT,100*AMOUNT/TOTAL
From LEDGER,TOTAL
Where actiondatebetween
To_date(‘01-MAR-1901’,’DD-MON-YYYY’) and
To_date(‘31-MAR-1901’,’DD-MON-YYYY’)
And action in(‘BOUGHT’,’PAID’);
3.如果不建立 TOTAL视图,也可以使用下面语句来完成同样的工作:
SELECTPERSON,AMOUNT,100*AMOUNT/TOTAL
From LEDGER,
( select SUM(Amount) TOTAL
from Ledger
Where actiondatebetween
To_date(‘01-MAR-1901’,’DD-MON-YYYY’) and
To_date(‘31-MAR-1901’,’DD-MON-YYYY’)
And action in(‘BOUGHT’,’PAID’)
)
Where actiondatebetween
To_date(‘01-MAR-1901’,’DD-MON-YYYY’) and
To_date(‘31-MAR-1901’,’DD-MON-YYYY’)
And action in(‘BOUGHT’,’PAID’);
第八章 一些高级的用法
这里给出一点不作为一般要求的内容,它虽然不是必需的,但对于应用开发来说很重要。希望Oracle应用设计者把它应用到系统的设计中。
§8.1 关于DECODE
DECODE是Oracle公司独家提供的功能,它是一个功能很强的函数。它虽然不是SQL的标准,但对于性能非常有用。到目前,其他的数据库供应商还不能提供类似DECODE的功能,甚至有的数据库的供应商批评Oracle的SQL不标准。实际上,这种批评有些片面或不够水平。就象有些马车制造商抱怨亨利。福特的“马车”不标准一样。
§8.1.1 DECODE 中的if-then-else逻辑
在逻辑编程中,经常用到If – Then –Else 进行逻辑判断。在DECODE的语法中,实际上就是这样的逻辑处理过程。它的语法如下:
DECODE(value, if1, then1, if2,then2, if3,then3, . . . else )
Value 代表某个表的任何类型的任意列或一个通过计算所得的任何结果。当每个value值被测试,如果value的值为if1,Decode函数的结果是then1;如果value等于if2,Decode函数结果是then2;等等。事实上,可以给出多个if/then 配对。如果value结果不等于给出的任何配对时,Decode 结果就返回else。
需要注意的是,这里的if、then及else都可以是函数或计算表达式。
§8.1.2 DECODE 的简单例子
Oracle系统中就有许多数据字典是使用decode 思想设计的,比如记录会话信息的V$SESSION数据字典视图就是这样。我们从《Oracle8i Reference》资料中了解到,当用户登录成功后在V$SESSION中就有该用户的相应记录,但用户所进行的命令操作在该视图中只记录命令的代码(0—没有任何操作,2—Insert…),而不是具体的命令关键字。因此,我们需要了解当前各个用户的名字及他们所进行的操作时,要用下面命令才能得到详细的结果:
selectsid,serial#,username,
DECODE(command,
0,’None’,
2,’Insert’,
3,’Select’,
6,’Update’,
7,’Delete’,
8,’Drop’,
‘Other’) cmmand
from v$session whereusername is not null;
§8.1.3 DECODE实现表的转置
数据库中的表是由列和行构成的一个二维表。一般列在任何数据库中都是有限的数量,而行的变化较大,如果表很大,行的数量可能大上千万行。同一列的不同行可能有不同的值,而且不是预先定义的。
除上面描述表具有的一些特点外,有一些表可以看成是不变的或者是较稳定的,比如住房公积金系统是各个单位按照职工的工资数的比例交到本地的经办行中,它的处理流程如下:
例1.住房公积金报表置换实例:
1.各个单位在本地经办行进行开户,开户就是将单位的基本信息和职工信息的进行登记;
2.每月各个单位的会计到经办行交缴本单位的所有职工的住房公积金,系统记录有每个职工的交缴明细并在每条记录上记录有经办行的代码;
3.每月、季、半年及年终都要求将经办行 变为“列”给出个月的明细报表:
经办行:城西区 城东区 。 。 。
月份:
2001.01 xxxx1.xx xxxxx2.xx
2001.02 xxxx3.xx xxxxx4.xx
。 。 。 。 。 。
原来的数据顺序是:
城西区2001.01 xxxxx1.xx
城东区2001.01 xxxxx2.xx
城西区2001.02 xxxxx3.xx
城东区2001.02 xxxxx4.xx
住房公积金系统记录职工的每月交缴名细的pay_lst表结构是:
bank_code varchar2(6)NOT NULL, -- 经办行代码
acc_no varchar2(15) not null, -- 单位代码(单位帐号)
emp_acc_no varchar2(20) not null, -- 职工帐号
tran_date date not null, -- 交缴日期
tran_val Number(7,2) not null, -- 交缴额
sys_date date default sysdate, --系统日期
oper_id varchar2(10) --操作员代码
这样的表结构,一般按照将经办行作为行进行统计是很容易的,但是如果希望将经办行变为列这样的格式来输出就有困难。如果用DECODE函数来处理则变得很简单:
我们创建一个视图来对目前的pay_lst表进行查询。将经办行代码变为一些具体的经办行名称即可:
CREATE OR REPLACEVIEW bank_date_lst AS
Select to_char(tran_date,’yyyy.mm’),
SUM( DECODE (bank_code,’001’, tran_val,0 )) 城西区,
SUM( DECODE (bank_code,’002’, tran_val,0 )) 城南区,
SUM( DECODE (bank_code,’003’, tran_val,0 )) 城东区
FROM pay_lst
GROUP BYto_char(tran_date,’yyyy.mm’);
例2.希望将下面的列结果按照列的方式来显示JOB内容:
SQL> selectempno,ename,job,sal,deptno from emp
2 order by deptno,job;
EMPNO ENAME JOB SAL DEPTNO
-------------------- --------- ---------- ----------
7934 MILLER CLERK 1300 10
7782 CLARK MANAGER 2450 10
7839 KING PRESIDENT 5000 10
7788 SCOTT ANALYST 3000 20
7369 SMITH CLERK 800 20
7876 ADAMS CLERK 1100 20
7566 JONES MANAGER 2975 20
7938 赵元杰 软件 12345 20
7698 BLAKE MANAGER 2850 30
7499 ALLEN SALESMAN 1600 30
7654 MARTIN SALESMAN 1250 30
7844 TURNER SALESMAN 1500 30
7521 WARD SALESMAN 1250 30
18 rows selected.
再看下面的查询结果:
SQL> selectdeptno,job,sum(sal) from emp group by deptno,job;
DEPTNO JOB SUM(SAL)
---------- -------------------
10 CLERK 1300
10 MANAGER 2450
10 PRESIDENT 5000
20 ANALYST 3000
20 CLERK 1900
20 MANAGER 2975
20 软件 74070
30 MANAGER 2850
30 SALESMAN 5600
9 rows selected.
从上面的结果看,如果希望将JOB置换成列的方式,则只要用DECODE将JOB列进行描述即可。创建的视图如下:
create or replaceview empv as
select deptno,
sum( decode(job,’ANALYST’, sal,0)) ANALYST,
sum( decode(job,’CLERK’, sal,0)) CLERK,
sum( decode(job,’MANAGER’, sal,0)) MANAGER,
sum( decode(job,’PRESIDENT’, sal,0)) PRESIDENT,
sum( decode(job,’SALESMAN’, sal,0)) SALESMAN,
sum( decode(job,’软件’, sal,0)) 软件
from emp group by deptno;
具体运行的显示样本如下:
SQL> create orreplace view empv as
2 select deptno,
3 sum(decode(job,'ANALYST', sal,0)) ANALYST,
4 sum(decode(job,'CLERK', sal,0)) CLERK,
5 sum(decode(job,'MANAGER', sal,0)) MANAGER,
6 sum(decode(job,'PRESIDENT', sal,0)) PRESIDENT,
7 sum(decode(job,'SALESMAN', sal,0)) SALESMAN,
8 sum(decode(job,'软件', sal,0)) 软件
9 fromemp group by deptno;
View created.
SQL> select *from empv;
DEPTNO ANALYST CLERK MANAGER PRESIDENT SALESMAN 软件
-------------------- ---------- ---------- ---------- ---------- ----------
10 0 1300 2450 5000 0 0
20 3000 1900 2975 0 0 74070
30 0 0 2850 0 5600 0
§8.2 关于访问远程数据库
在许多环境中,都可能需要访问远程数据库。现在的Oracle8i/9I的NET都支持远程访问技术。只要环境网络具备和在参数文件中进行相应的配置就能在SQL语句中进行访问。关于环境的安装和配置另见DBA资料。
§8.2.1 数据库链接
Oracle本地要与远程进行连接,要通过数据库链接。
1.使用数据库链接进行查询和更新:
SELECT * fromworker;
这样的语句表示在本地进行查询。而对于远程,则需要在语句后加相应的数据库链接。如:
SELECT * FROM worker@remote_connect;
如果想省去@号后面的字串,可以采用建立一个本地的同义词来实现。如:
CREATE SYNONYMWORKER_SYN for WORKER@remote_connect;
如果希望进行远程更新的话,类似可以在UPDATE语句中加上远程连接符。如:
UPDATE worker@remote_connect
SET lodging=’CRANMER’ where lodging=’ROSEHILL’;
2.创建数据库链接的语法:
创建数据库链接的语法如下:
CREATE [PUBLIC]DATABASE LINK remote_connect
CONNECT TO usernameidentified by password using ‘connect string’;
一般PUBLIC有DBA来创建。个人用户可以不加PUBLIC就是私有的数据库链接。
Oracle系统可以创建链接,但是连接的数量有限制。缺省的并发数是4(由init.ora文件中的OP_LINKS来限制)。
为了建立数据库链接,需要有CREATEDATABASE LINKS 系统权限;要与远程的帐号进行连接还需要有CREATE SESSION普通权限。
CREATE database linkzhao connect to zhaoyuanjie identified by zhao_yuan_jie
Using ‘sun450’;
这样创建后,可以用下面语句来使用:
SELECT * from tst@sun450;
需要注意的是,在创建数据库链接时是根据Oracle的Tnsnames.ora参数文件中的连接字符串来填写连接字串。比如上面的连接字串是sun450 。则在tnsmaes.ora中的要有下面的数据项:
SUN450 =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST =dbsvr)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = s450)
)
)
§8.2.2 使用同义词获得本地透明
在应用系统的生命周期内,为了方便和维护简便,经常采用建立同义词来实现透明的访问。
例1. 没有建立本地透明时的访问:
SELECT * FROMnorth.worker;
例2.建立一个同义词,然后进行访问:
CREATE SYNONYMWORKER FOR NORTH.WORKER;
SELECT * FROMWORKER;
在实际应用中,为了达到隐藏表的所有权,还可以通过使用数据库链接和同义词来隐藏数据的物理位置。通过使用对应远程端的本地表同义词,可以把另一个逻辑层从应用转移到数据库中。
例3.建立一个本地同义词的远程表:
CREATE SYNONYMWORKERSKILL
For WORKERSKILL@remote_connect;
§8.2.3 在视图中使用user伪列
Oracle提供一个伪列user, 它可以在创建视图中使用,从而实现对结果的返回的限制。如:
CREATE OR REPLACEVIEW emp_lst
AS select * from emp
Where ename=user;
这样的视图表示,只有使用者的用户名与ename中的名字一样,才能返回相关的记录。看下面的结果就会明白:
SQL> CREATE ORREPLACE VIEW emp_lst
2 ASselect * from emp
3 Where ename=user;
View created.
SQL> select *from emp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTN
---------- ---------- --------- ---------- ------------------- ---------- ---------
7369 SMITH CLERK 7902 17-DEC-80 800 2
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 3
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 3
7566 JONES MANAGER 7839 02-APR-81 2975 2
7654 MARTIN SALESMAN 769828-SEP-81 1250 1400 3
7698 BLAKE MANAGER 7839 01-MAY-81 2850 3
7782 CLARK MANAGER 7839 09-JUN-81 2450 1
7788 SCOTT ANALYST 7566 19-APR-87 3000 2
7839 KING PRESIDENT 17-NOV-81 5000 1
7844TURNER SALESMAN 7698 08-SEP-81 1500 0 3
7876 ADAMS CLERK 7788 23-MAY-87 1100 2
7934MILLER CLERK 7782 23-JAN-82 1300 1
7938 赵元杰 软件 23-SEP-01 12345 54321 2
7939 赵元杰 软件 23-SEP-01 12345 54321 2
14 rows selected.
SQL> select * from emp_lst;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTN
---------- ---------- --------- ---------- --------- -------------------- ---------
7788 SCOTT ANALYST 7566 19-APR-87 3000 2
SQL>
上面结果显示,由于本用户的名字是scott,所以只显示ename=scott的记录。
§8.2.4 使用COPY功能
如果我们用 CREATETABLE xxx AS select * from tablename@remote_connect; 不能完成带Long等列的操作时,请你使用Oracle提供的COPY命令。COPY可以实现在CREATE TABLE复制表时所不能实现的功能。
COPY命令的语法如下:
COPY { FROMusername[/password]@database | TO
username[/password]@database |
FROM username[/password]@databaseTO
username[/password]@database }
{ APPEND|CREATE|INSERT|REPLACE }
destination_table[(col1,col2,...) ]USING query
参数说明:
APPEND 如果目标表已存在,将查询的记录插入该表中,
如果目标表不存在,则创建再插入所查询的记录。
CREATE 先创建目标表再插入所查询的记录,
如果目标表不存在,则提示错误。
INSERT 将查询(必须用 using query 查)的记录插入到目标表中,
如果目标表不存在,则提示错误。
REPLACE 将查询的记录替换到目标表中的内容,
如果目标表存在,则先删旧表内容再用拷贝表替换,
如果目标表不存在,则创建表。
使用copy 命令还需设置以下变量:
SQL>set long n; /* 数据长度 */
SQL>setcopycommit m;/* m行提交一次 */
SQL>setarraysize n; /* 批操作的大小 */
例:
COPY from zhao/zhaoyuan@sun450
CREATE WORKER usingselect * from WORKER;
§8.2.5 管理Oracle名称服务器
要保证与远程进程连接,DBA必须确保名称服务器配置的正确。另见DBA资料的“NET8 名称服务器”章节。
§8.3 关于上下文的使用
在Oracle系统中,可以支持对文本进行处理。要想对文本进行搜索,则需要对数据库环境进行设置。
§8.3.1 设置上下文选项
为了在数据库中使用上下文功能,先要设置上下文选项。用以启动数据库上下文处理进程。可以用CTXCTL实用程序来启动上下文服务器:
$CTXCTL
… …
>help
The follwing commands are available:
Help[command] -- 命令信息
Status -- 正在运行的服务器情况
Start n [line |query | ddl | dml ] … --启动n服务进程
Stop pid… | all --停止ctxctl
Quit -- 结束ctxctl
Exit --结束ctxctl
为了支持文本搜索,用户至少启动一个query上下文服务器。到底启动多少个服务器,要根据用户的要求决定。比如:
start 1 ddl dml query
start 1 query
服务器的当前情况可以从CTS_ALL_SERVERS数据字典来查询,如:
SQL>col ser_name for a30
Select ser_name,ser_status,set_started_at fromCTX-ALL_SERVERS;
从结果可看出已经启动的服务器,DBA可以在SQL>下用下面命令来停止服务的运行:
SQL>exec CTX_ADM.SHUTDOWN;
1.在init.ora参数文件中设置启动参数:
要使上下文有效,必须在Init.ora文件中设置下面参数:
text_enable= TRUE
也可以在SQL>下进行设置:
ALTER SESSION set text_enable = TRUE;
2.必要的角色:
在Oracle数据库中,上下文的数据字典属于CTXSYS用户所有。所以CTXSYS是唯一具有CTXADMIN角色的用户。此外,还有CTXAPP(应用程序所有)和CTXUSER(应用程序用户)角色。一般需要将CTXAPP授予开发上下文的用户。如:
grant CTXAPP toscott;
§8.3.2 为上下文查询设置表
为了实现上下文的查询,需要在基表的某列建立主关键字和为表中的文本列设置相应的查询策略。比如有下面的求职表:
1.基本的设置:
1)创建基本表:
CREATE TABLE prospect( name varchar2(20),
Address varchar2(40),
Resume long )tablespace user_data
storage(initial 5mnext 1m pctincrease 0);
2)为prospect表建立一个主关键字:
ALTER table prospect add constraint prospect_pk primary key ( name );
3)为prospect表建立定义策略:
为了在prospect 表上进行上下文的查询,需要用 Oracle提供的CREATE_POLICY过程来创建一个策略。CREATE_POLICY过程需要两个参数:策略名称 和 被索引列的名称。如:
exec CTX_DDL.CREATE_POLICY(‘prospect’,’ prospect.resome’);
2.显示和修改选项:
可以从CTX_PREFERENCE_USAGE数据字典视图中查出有关当前已经建立的策略信息。
1)显示策略信息:
例1.查询数据字典视图中的PROSPECT策略的信息:
SELECT PRE_NAME from CTX_PREFERENCE_USAGE
Where pol_name=’ PROSPECT’;
2)修改策略信息:
可以通过执行CTX_DDL_UPDATE_POLICY过程来对策略进行修改,如希望加SOUNDEX来对prospect进行查询:
EXEC CTX_DDL_UPDATE_POLICY( policy_name=>’ PROSPECT’,
Worldist_pref=>’CTXSYS.SOUNDEX’);
3)删除策略信息:
可以通过执行CTX_DDL_DROP_POLICY过程来实现删除某个策略。如:
EXEC CTX_DDL_DROP_POLICY( ’ PROSPECT’ );
3.建立文本索引:
要实现对文本的查询,除了设置参数外,还要用CTX_DDL包中CREATE_INDEX过程为表建立文本索引。要建立文本索引,要求具有CTXAPP角色。
1)建立文本索引:
EXEC CTX_DDL.CREATE_INDEX(‘prospect’);
如果出现错误,可能还没有启动DDL服务器,则要用CTXCTL来启动上下文服务器,在运行上面的建立索引命令。
2)生成数据库对象:
当创建文本索引完成后,系统就数据库中建立一些表,这些表存放支持策略的信息。可以从CTX_USER_POLICY数据字典中查到各个表及其策略。要查询策略信息,还需要与user_tables和user_indexes数据字典进行相关查询。
3)删除文本索引:
当不能修改文本策略索引或不再需要时,可以通过CTX_DDL.DROP_INDEX过程来删除文本索引。
例。删除一个索引:
EXEC CTX_DDL.DROP_INDEX(‘prospect’);
再查看数据字典的信息:
select pol_status from CTX_USER_POLICIES
where pol_name=’PROSPECT’;
§8.3.3 优化文本索引
在CTX_DDL包中提供了一个OPTIMIZE_INDEX过程来重建所有的文本索引。OPTIMIZE_INDEX过程可以对压缩索引中的碎片,并且去掉或删除文本数据项中的引用和其他相关的数据项。OPTIMIZE_INDEX过程的用法如下:
EXEC CTX_DDL .OPTIMIZE_INDEX(‘prospect’);
§8.4 关于维数(DIMENSION)
Oracle8i提供一种新功能叫维(DIMENSION),用于在一对列之间定义一个“父-子”关系,这些列的全部列必须来自同一个表。但是,对于一个列(或层)可以来自不同的表。优化器可以在实体化视图中利用这种“父-子”关系来进行查询改写。
要建立维,你必须有CREATE DIMENSION 系统权限。如果你要在其他的模式下建立维,则需要具有CREATE ANY DIMENSION 系统权限。
注意,在建立完成维后,Oracle系统会自动有效这些维,如果希望对建立的维有效,则需要指定hierarchy_clause和join_clause, 你必须用DBMS_OLAP.validate_dimension存储过程。
§8.4.1 CREATE DIMENSION语法
创建维的语法如下:
CREATE DIMENSION [schema.]dimension
LEVEL level IS (level_table.level_column )
{
HIERARCHY hierarchy ( child_level CHILD OF parent_level
JOIN KEY (child_key_column)
REFERENCES parent_level
) |
ATTRIBUTE level DEFERENCES ( dependent_column )
}
schema
模式名字,如果没有指定模式名,则Oracle将你的用户名作为模式名。
dimension
维的名字,在模式下的维名字必须唯一。
level_clause
level_clause定义维的级,一个级定义一个层和属性
level 层的名字
level_table.level_column 级的列名,最多32级。有下面限制:
l 在级中的所有列必须来自同一个表;
l 不同级的列可以来自不同的表,但是必须指定join_clause;
l 所指定的一组列在该级中必须是唯一的;
l 所指定的列不能在另外的地方指定;
l 每个level_column必须是非空的(不是原来意义上的NOT NULL)。
hierarchy_clause
层在维数中定义一个线性的层,每个层概括一个父-子关系的链。在维中的层与其他层有关系,它们可以有一些列。
hierarchy 指定相同的级,这些级在维中必须是唯一的。
child_level 指定级中的名字。
join_clause
join_clause 可以为一个包括多个表的维指定一个内部的等价连接。
attribute_clause
attribute_clause可以指定唯一确定的列。在level中的列必须来自同一个dependent_columns所指的表。在level_clause 中,dependent_columns不需指定。
例如,层次级为:city、state和 country,则city是可以作为市长(mayor),而state可以确定为 州长(governor),而country确定为总统(president)。
§8.4.2 创建维的例子
下面例子在time_tab表中创建一个维 time 和在CITY、STATE、COUNTRY表中创建维geog
CREATEDIMENSION time
LEVELcurDate IS time_tab.curDate
LEVELmonth IS time_tab.month
LEVELqtr IS time_tab.qtr
LEVELyear IS time_tab.year
LEVELfiscal_week IS time_tab.fiscal_week
LEVELfiscal_qtr IS time_tab.fiscal_qtr
LEVELfiscal_year IS time_tab.fiscal_year
HIERARCHYmonth_rollup (
curDateCHILD OF
monthCHILD OF
qtrCHILD OF
year)
HIERARCHYfiscal_year_rollup (
curDateCHILD OF
fiscal_weekCHILD OF
fiscal_qtrCHILD OF
fiscal_year)
ATTRIBUTEcurDate DETERMINES (holiday, dayOfWeek)
ATTRIBUTEmonth DETERMINES (yr_ago_month, qtr_ago_month)
ATTRIBUTEfiscal_qtr DETERMINES yr_ago_qtr
ATTRIBUTEyear DETERMINES yr_ago ;
CREATEDIMENSION geog
LEVELcityID IS (city.city, city.state)
LEVELstateID IS state.state
LEVELcountryID IS country.country
HIERARCHYpolitical_rollup (
cityIDCHILD OF
stateIDCHILD OF
countryID
JOINKEY city.state REFERENCES stateID
JOIN KEY state.country REFERENCES countryID);
第九章 安全管理
在建立应用系统的各种对象(包括表、视图、索引等)前,就得先确定各个对象与用户的关系。也就是说,哪些用户需要建立,哪些用户都充当什么样的角色,他们应该有多大权限等。下面介绍基本的安全管理方面的内容,更详细的内容在DBA资料里介绍。
§9.1 CREATEUSER 命令
CREATE USER usernameIDENTIFIED BY password
Or IDENTIFIEDEXETERNALLY
Or IDENTIFIEDGLOBALLY AS ‘CN=user’
[DEAFULT TABLESPACEtablespace ]
[TEMPORARYTABLESPACE tablespace]
[QUOTA [integerK[M]][UNLIMITED] ON tablespace
[,QUOTA [integer K[M]][UNLIMITED] ONtablespace
[PROFILESprofile_name]
[PASSWORD EXPIRE]
[ACCOUNT LOCK orACCOUNT UNLOCK]
CREATE USER username用户名
IDENTIFIED BYpassword 用户口令
IDENTIFIED BYEXETERNALLY 用户名在操作系统下验证,这个用户名必须与操作
系统中所 定义的用户相同。
IDENTIFIED GLOBALLYAS ‘CN=user’ 用户名是ORACLE 安全域中心服务器来验证
,CN 名字标识用户的外部名。
[DEAFULT TABLESPACEtablespace ] 缺省的表空间
[TEMPORARYTABLESPACE tablespace] 缺省的临时表空间
[QUOTA [integerK[M]][UNLIMITED] ON tablespace 允许使用k[m]字节
[,QUOTA [integer K[M]][UNLIMITED] ONtablespace
[PROFILESprofile_name] 资源文件的名字
[PASSWORDEXPIRE] 立即将口令设成过期状态,用户在登录进入前必须
修改口令。
[ACCOUNT LOCK orACCOUNT UNLOCK] 用户不被加锁。
§9.2 建立用户
建立用户虽然不是经常的工作,但却是比不可少的工作。如果你的环境是一个较复杂的应用系统,你还是应该重视用户的建立和管理的事。下面是建立不同类型用户的方法。
§9.2.1 外部验证(Authenticated )用户
外部识别的Oracle用户可以被客户端的操作系统验证,即在Oracle外放置了用于口令管理和用户验证的控制。此类登录不再需要Oracle 口令。操作系统验证的实现需要进行下面步骤:
1. 在INITsid.ORA文件中设置OS_AUTHENT_PREFIX 参数,在Oracle6里必须设置用户的前缀为OPS$。Oracle8可以不需要前缀OPS$。如在INITsid.ORA文件上加:
. . .
OS_AUTHENT_PREFIX=”OPS$”
. . .
2. 用CREATEUSER 建立外部用户:
CREATE USER ops$zhao IDENTIFIED BY EXTERNALLY;
如果在INITsid.ORA文件的OS_AUTHENT_PREFIX=””(即没有设置为OPS$),则:
CREATE USER zhao IDENTIFIED BY EXTERNALLY;
CREATE USER ops$zhaoyj IDENTIFIED BY EXTERNALLY
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
QUOTS UNLIMITED ON users
QUOTS UNLIMITED ON temp;
§9.2.2 全局(Globally)验证用户-企业验证
在Oracle数据库里,可以将用户配置成不需要验证口令的方式,用以替代来自X.509企业目录服务的口令检查。这种类型的用户一般都是在大的企业里的登录使用。一般企业验证启用Oracle安全服务(OSS)来实现单独的注册。建立全局验证的用户需要带 GLOABLLY AS <directory_name>。如:
CREATE USER SCOTT IDENTIFIED GLOABLLY AS ‘CN=scott,
OU=division1, O=sybex, C=US’;
§9.3 ALTERUSER 命令
ALTER USER命令用于修改用户的资源限制和口令等。ALTER USER 命令语法如下:
ALTER USER usernameIDENTIFIED BY password
Or IDENTIFIED EXETERNALLY
Or IDENTIFIED GLOBALLY AS ‘CN=user’
[DEAFULT TABLESPACEtablespace ]
[TEMPORARYTABLESPACE tablespace]
[QUOTA [integerK[M]][UNLIMITED] ON tablespace
[,QUOTA [integer K[M]][UNLIMITED] ONtablespace
[PROFILESprofile_name]
[PASSWORD EXPIRE]
[ACCOUNT LOCK orACCOUNT UNLOCK]
[DEFAULT ROLE role[,role]
or [DEFAULT ROLE ALL [EXEPT role[,role]]]or[DEFAULT ROLE NOTE]
例:比如用户使用的资源超出限额的话,就如下提示:
ORA-01536:SPACE QUOTA EXCEEDED FOR TABLESPACE ‘USERS’
这时需要对该用户增加资源限额:
SQLPLUSSYSTEM/MANAGER
SQL>ALTER USERsideny QUOTA 10M ON SYSTEM;
§9.4 DROP USER 命令
对于不再需要的用户,可以用DROPUSER 来将不要的用户从数据库系统中删除。以释放出磁盘空间。DROP USER语句的语法如下:
DROP USER user [CASCADE]
如果加CASCADE 则连同用户的 对象一起删除。
SQL>drop user zhao cascade;
提示:不要轻易使用 DROP USER 命令。只有在确认某个用户没有保留时才使用该命令。
§9.5 GRANT命令与REVOKE 命令
Oracle提供两个命令(grant和revoke)用于给用户(或角色)进行授权和从用户(或角色)中收回某些权限。GRANT可以给用户(或角色)授予对象的权限和系统权限。
§9.5.1 GRANT 命令
可以使用GRANT语句将权限或角色授予某个用户或角色。GRANT的语法如下:
GRANT system_privilege |role TO user | role | PUBLIC
[WITH ADMIN OPTION]
GRANT object_privilege |ALL column ON schema.object
FROM user | role | PUBLICWITH GRANT OPTION
system_privilege: 系统权限或角色
role: 角色
user: 被授予的用户或角色
object_privilege:对象的权限,可以是:
l ALTER
l DELETE
l EXECUTE
l INDEX
l INSERT
l REFERENCES
l SELECT
l UPDATE
例子:
SQL
GRANT CREATE TABLE TO gavaskar;
GRANT team_leader TO crystal;
GRANT INSERT, UPDATE ON sales TO larry WITH GRANT OPTION;
GRANT ALL TO PUBLIC;.
§9.5.2 REVOKE 命令
可以使用REVOKE语句从用户或角色中撤消某些权限。REVOKE的语法如下:
REVOKE system_privilege |role FROM user | role | PUBLIC
REVOKE system_privilege |role FROM user | role | PUBLIC
REVOKE object_privilege |ALL ON schema.object FROM user
| role | PUBLIC CASCADECONSTRAINTS
system_privilege: 系统权限或角色
object_privilege:对象的权限,可以是:
l ALTER
l DELETE
l EXECUTE
l INDEX
l INSERT
l REFERENCES
l SELECT
l UPDATE
例:
REVOKE ALTER TABLESPACEFROM john;
REVOKE GRANT ANY ROLE FROMtodd;
REVOKE manager FROM imran;
REVOKE INSERT ON sales FROMjaved;
REVOKE ALL ON marketingFROM terry;.
§9.6 权限和角色
为了管理复杂系统的不同用户,Oracle系统提供了角色和权限。权限可以使用户能访问对象或执行程序。而角色是一组权限的集合,同样,角色被授予用户后,用户也具有某些权限。Oracle有三种类型的权限:
l 对象权限(Object)
l 系统权限(System)
l 角色(Role)
§9.6.1 建立角色
角色是一组权限的集合。角色包含一个或多个权限;角色可以再包含角色。关于角色的详细描述参考另外的《Oracle8i初级数据库管理》--赵元杰著。
1.建立角色的命令语法如下:
CREATE ROLE role_name
[ NOT INDENTIFIED |
[ INDENTIFIED [BYpassword | EXTERNALLY|GLOABALLY] ]
];
role 指定角色名;
NOT IDENTIFIED 不需口令有数据库验证;
IDENTIFIED 需要口令,分别为三种情况:
l BY password 指定本地用户口令,如果在建立角色时用口令,则用户在使用时也要给出口令。
l EXTERNALLY 指定为外部用户,该用户用外部服务进行验证。
CREATEROLE role
NOTIDENTIFIED
IDENTIFIED
BYpassword
EXTERNALLY
GLOBALLY;
例:
CREATE ROLE vendor IDENTIFIED GLOBALLY;
2.三种标准角色:
自从Oracle6版本以后,Oracle系统一直有三种缺省的角色,即:
l CONNECT ( 连接角色)
具有CONNECT角色用户,可以进行 SELECT ,INSERT,UPDATE和DELETE操作。
l RESOURCE (资源角色)
具有RESOURCE角色的用户可以进行CONNECT所做的工作,此外,还可以进行CREATE TABLE、CREATESEQUENCE、CREATE PROCEDURE、CREATETRIGGER、CREATE INDEX及CREATECLUSTER等。
l DBA(数据库管理员角色)
具有DBA角色的用户可以进行除 RESOURCE外,还可以进行数据库的管理操作。
§9.6.2 给角色授权
一旦创建完角色,用户就可以给角色授权。给角色授权的GRANT语句在前面介绍。通过GRANT语句可以对角色授各种权限,如用户对象的访问权,系统权限等。如果用户具有DBA权限的话,则用户有GRANT ANYPRIVILEGE系统权限。可以对角色授予各种权限。如:
GRANT CREATE SESSION TO clerk;
GRANT CREATE SESSION,CREATE Database link tomamager;
§9.6.3 授权角色给用户
如果角色创建完毕并且已经给角色授了相应的权限,用户就可以将角色授权给用户了。这样的操作完成后,被授角色的用户就有了相应的权限。要完成这样的操作,只要操作者具有GRANT ANY PRIVILEGE系统权限就可通过GRANT语句对用户授各种权限。如:
GRANT clerk TO ZHAO;
§9.7 有关的数据字典
无论对数据库管理员或是一般的用户,对Oracle有关数据字典的了解程度是衡量是否真正掌握Oracle核心的关键。如果你了解基本的Oracle数据字典,对于各种系统的信息查询将大有好处。下面给出与安全管理有关的数据字典简单介绍。
§9.7.1 与用户、角色与权限有关的数据字典
DBA_USERS 实例中有效的用户及相应信息。
V$SESSION 实例中会话的信息。
DBA_ROLES 实例中已经创建的角色的信息。
ROLE_TAB_PRIVS 授予角色的对象权限。
ROLE_ROLE_PRIVS 授予另一角色的角色。
ROLE_SYS_PRIVS 授予角色的系统权限。
DBA_ROLE_PRIVS 授予用户和角色的角色。
SESSION_ROLES 用户可用的角色的信息。
§9.7.2 查询数据字典信息
对于一般的软件人员来说,应该掌握对数据字典的基本查询,如:
例1。查看当前已经创建了多少用户和用户缺省的表空间:
SQL> set line 120
SQL> col usernamefor a26
SQL> coldefault_tablespace for a20
SQL> selectusername,DEFAULT_TABLESPACE,created from dba_users;
USERNAME DEFAULT_TABLESPACE CREATED
---------------------------------------------- ----------
SYS SYSTEM 05-12月-01
SYSTEM TOOLS 05-12月-01
OUTLN SYSTEM 05-12月-01
DBSNMP SYSTEM 05-12月-01
AURORA$JIS$UTILITY$ SYSTEM 05-12月-01
OSE$HTTP$ADMIN SYSTEM 05-12月-01
AURORA$ORB$UNAUTHENTICATEDSYSTEM 05-12月-01
ORDSYS SYSTEM 05-12月-01
ORDPLUGINS SYSTEM 05-12月-01
MDSYS SYSTEM 05-12月-01
ZHAO USERS 07-12月-01
SCOTT USERS 08-2月-02
已选择12行。
例2。查看当前已经创建了多少角色:
SQL> select *from dba_roles;
ROLE PASSWORD
--------------------------------------
CONNECT NO
RESOURCE NO
DBA NO
SELECT_CATALOG_ROLE NO
EXECUTE_CATALOG_ROLE NO
DELETE_CATALOG_ROLE NO
EXP_FULL_DATABASE NO
IMP_FULL_DATABASE NO
RECOVERY_CATALOG_OWNER NO
AQ_ADMINISTRATOR_ROLE NO
AQ_USER_ROLE NO
ROLE PASSWORD
--------------------------------------
SNMPAGENT NO
OEM_MONITOR NO
HS_ADMIN_ROLE NO
JAVAUSERPRIV NO
JAVAIDPRIV NO
JAVASYSPRIV NO
JAVADEBUGPRIV NO
JAVA_ADMIN NO
JAVA_DEPLOY NO
TIMESERIES_DEVELOPER NO
TIMESERIES_DBA NO
已选择22行。
SQL>
第十章 其它一些常见问题及技巧
下面给出一些对于初学者来是常见问题的一点描述,目的是使那些使用Oracle系统时间不长的人员能尽快适应在Oracle环境下的开发。
§10.1 一些常见问题
下面是一些初学者常遇到的问题的解释。如果你是一位Oracle的老手可以不阅读本章的内容。
§10.1.1 Oracle与2000年问题
l oracle 约定
l oracle在系统中一直用YYYY:MM:DD HH24:MI:SS表示日期和时间
l ORACLE7 server和ORACLE8 server提供一种年格式掩码RR
规则转换如下表:
当前年度(最后两位数) 指定的两位数年
(机器设置为)
RR返回的年
0~49 0~49
50~99 0~49
0~49 50~99
50~99 50~99
当前世纪(19XX)
下个世纪(20XX)
上个世纪(18XX)
当前世纪(19XX)
l 当前年是在50~99后半世纪
1)如果输入00和99之间,oracle将被记为下个世纪,如在1996年输02,则被记为2002年。
2)如果输入50和99之间,oracle将被记为当前世纪,如在1996年输97,则被记为1997年。
l 当前年是在00~49前半世纪
1)如果输入00和49之间,oracle将被记为当前世纪,如在2001年时输02,则被记为2002年。
2)如果输入的两位年在50和99之间,oracle将被记为上个世纪,如在2001年输97,则被记为1997年。
l 例子
Create table abc(datefld date)
Insert into abc Value('01-JAN-11');
Insert into abc Value('01-JAN-90');
转换成1911年和1990年
Update abc
Set Datefld(d=To_date(To_datefld,'DD-MON-YY HH24:MI:SS'),
'DD_MON_RR HH24:MI:SS');
SelectTo_char(Datefld,'DD-MON-YYYY') Datefld from abc;
01-JAN-2011
01-JAN-1990
建议:2000年问题在开发中的建议任何时应采用4位年表示如果用两位一定用YY或RR表示输入界面最好作判断和提示。
§10.1.2 如何正确插入日期数据
许多初学者都可能遇到这样的问题,就是往DATE类型的列插入日期数据时,经常被提示错误。
比如有下面的ABC表结构:
SQL> desc abc
名称 空? 类型
------------------------------------------------- ------------
RQ DATE
NAME VARCHAR2(20)
当用下面命令插入数据到ABC表时,系统提示如下信息:
SQL> insert intoabc values('02-JAN-2002','赵元杰');
insert into abcvalues('02-JAN-2002','赵元杰')
*
ERROR 位于第 1 行:
ORA-01843: 无效的月份
这是由于系统安装时的默认字符集引起的。虽然上面语句看上去没有错误,但是它不符合当前系统的日期格式要求。那么当前系统的日期格式是什么呢?我们可以用下面语句来查询:
SQL> selectsysdate from dual;
SYSDATE
----------
01-2月 -02
既然系统的日期格式是中文的月份,则将上面语句改为中文的月即可:
SQL> insert intoabc values('02-2月-2002','赵元杰');
已创建 1 行。
当然,你可以用altersession命令修改当前系统的日期格式为你喜欢的格式,如:
SQL> altersession set nls_date_format='yyyy/mm/dd';
会话已更改。
SQL> insert intoabc values('2002/02/02','赵元杰');
已创建 1 行。
SQL> select *from abc;
RQ NAME
------------------------------
2002/02/02 赵元杰
2002/02/02 赵元杰
关于日期格式的有关资料,请参见《Oracle8I数据库管理员》--赵元杰著
§10.1.3 在查询中只返回满足条件的部分记录
有时,我们关心的查询结果不是所有的记录,而是关心所查询的内容的存在性。如果我们不加特别的限制,满足条件的所有记录会源源不断地显示在屏幕上。为了只显示少量的内容,oracle提供rownum伪列来限制在查询时返回的记录数。例如:
Select * from dict where rownum < 10
Rownum是要显示的记录数
注意:
l 该用法在PL/SQL和Pro*C中常常不能用 ;
l 在条件中可以用 <= ,但不能用 = 或> 作判断。
§10.1.4 快速大量删除数据Truncate
在数据库管理操作中,经常需要将某个表的所有记录都删除而只保留表结构,这样的要求如果用delete 进行删除的话,Oracle系统会自动为该操作分配回滚段。如果回滚段较小,则可能导致操作失败。即使回滚段足够大,删除操作也需要较长的时间才能完成。为了加快删除操作,Oracle提供了一个特别的命令TRUNCATE,可以快速地完成对某个表的所有记录的删除。TRUNCATE的语法如下:
TRUNCATE [TABLE | CLUSTER]
schema.[table][cluster] [DROP | REUSE STORAGE]
删除表中或簇中的所有行,REUSE STORAGE保留被删除的空间作为该表的新行使用:缺省为DROP storge 即收回被删除的空间给系统。
特点:不可恢复。即不需rollback segment,不在日志文件中记录信息。
§10.1.5 Rowid的使用
l oracle为每个表的每一条记录赋予一个唯一的标识号rowid;
l 它是一个伪列,虽然在定义表结构时并不声明它,但它自动地建立;
l 用desc查看表结构时并不显示该字段;
l ROWID 在进行UPDATE或DELETE操作中速度最快;
l Oracle7和Oracle8的ROWID不同。
SQL>select name,sex,sal,rowid from emp;
SQL>delete from emp where rowid='...XXXX.XX.XXXX';
l Oracle7的Rowid 由三个部分组成,结构为:
block.row.file
格式为:
BBBBBBBB.RRRR.FFFF
BBBBBBBB 是文件的块号;RRRR 块中的行号;FFFF是文件的绝对号。
例0000000F.0000.00002
第15数据块.第1行.第二个数据文件
l Oracle8和Oracle8i/9i的Rowid由四个部分组成,结构为:
OOOOOOFFFBBBBBBRRR
格式为:
l OOOOOO代表数据对象号,它表示数据库段的编号;
l FFF 代表在表空间中的相对文件号;
l BBBBBB代表在一个文件中的块号,块号与数据文件有关,与表空间无关;
l RRR代表块中行的位置号。
例1:查询记录中的ROWID列的值:
SQL> select *from emp;
ENAME SAL DEPTNO TEL
------------------------------ ---------- -------------
赵元杰 9999.12 10 1360 136 5681
赵元杰 9999.12 10 1360 136 5681
例2:使用ROWID删除重复的记录。由于记录每个列的值完全一样,where 条件无法辨别,但是两条记录的ROWID号是不一样的。所以可以用ROWID作为条件。详细操作如下:
1)先查出记录:
SQL> selectename,sal,deptno,tel,rowid from emp;
ENAME SAL DEPTNO TEL ROWID
---------------------- ---------- -------------------- ------------------
赵元杰 9999.12 10 1360 136 5681 AAAFyUAADAAAAADAAA
赵元杰 9999.12 10 1360 136 5681 AAAFyUAADAAAAADAAB
2)看到两条记录的rowid不一样,可以使用chartorowid函数和rowid值完成删除操作:
SQL> delete fromemp where rowid = chartorowid('AAAFyUAADAAAAADAAB');
已删除 1 行。
3)删除操作完成,再查看emp表的数据:
SQL> selectename,sal,deptno,tel,rowid from emp;
ENAME SAL DEPTNO TEL ROWID
---------------------- ---------- -------------------- ------------------
赵元杰 9999.12 10 1360 136 5681 AAAFyUAADAAAAADAAA
从例子中可以看出,rowid的优势,建议在PL/SQL中的循环操作使用ROWID来提高处理速度。
§10.1.6 在查询中不让记录被更新
要保证在统计(查询)执行过程中,记录不被其他用户更新,则可以使用For update子句进行加锁。这样在这个锁释放前其他用户不能对这些记录作update、delete和加锁。
SQL>Select daptno from dept
Where deptno=25 For update;
如果你使用了FORUPDATE来对表进行加锁,组必须用commit来释放加锁的记录。
§10.1.7 EXCEPTIONS(违反完整性)问题
前面介绍的主键的创建方法,如果我们对表创建了主键后,那么在查入记录时,Oracle会自动对插入数据进行唯一性检查,当出现有数据违反唯一性限定的情况时,一般用户可能只看到ORA-0001 Dup_val_on_index ( 试图破坏一个唯一性限制 ) 类似的提示,而没有看是哪条记录违反唯一性限定。为了使用户有针对性地修改违反唯一性限定的记录,Oracle提供了一种方法可以容许用户捕获那些导致限定产生失败的行的信息。方法是:
l 用户生成一个列为EXCEPTION的表,创建该表的脚本是UTLEXCPT.SQL,你可以从oracle.../rdbms/admin目录中找。如:
SQL> startc:\oracle\ora81\rdbms\admin\utlexcpt
表已创建。
l 在建表时声明EXCEPTIONS。
l 激活EXCEPTIONS,如:
Alter table newspaper enable primary key
Exceptions into EXCEPTIONS;
EXCEPTIONS 表有四个列:
Row_ID 违反限定的各行
Ower 违反限定的拥有者
Table_name 违反限定所在的表
Constraint 行所在的限定
l 查看违反完整性的记录:
Select * form Newspaper
Where Rowid in (select Row_id from EXCEPTIONS);
注:可能原版资料上将例外的表拼写成为 except_table ,其实脚本并不是这样。下面是原版资料的例子:
ALTER TABLE emp
ENABLE VALIDATE CONSTRAINT fk_deptno
EXCEPTIONS INTO except_table;
SELECT emp.*
FROM emp e, except_table ex
WHERE e.row_id = ex.row_id
AND ex.table_name = ’EMP’
AND ex.constraint = ’FK_DEPTNO’;
§10.1.8 Not in和Not Exists
一般来说,在处理存在性检查中,用户都会看到Not in 和Not Exists 两个判断语句,但是它们在处理速度上有些不同。
Not in 速度慢
Not Exists 速度较快。
SQL>select name,depart,zip
From export
Where name not in (select name from xxx not exists
Item_application where …) and rownum<6
Order by score;
§10.1.9 关于 COPY命令
可以弥补 createtable ... as select ... from ...的和imp,exp 的不足,主要功能有:
1) 从一个本地数据库将一个或多个表拷贝到一个远程数据库;
2) 将一个表的一些记录拷贝到远程或本地库的其它表中;
3) 将包含 long 类型的表的一些列拷贝到其他表中;
4) 从一个oracle 数据库向一个非oracle数据库的拷贝表。
语法如下:
COPY { FROMusername[/password]@database | TO
username[/password]@database |
FROM username[/password]@databaseTO
username[/password]@database}
{ APPEND|CREATE|INSERT|REPLACE }
destination_table[(col1,col2,...) ]USING query
参数说明:
APPEND 如果目标表已存在,将查询的记录插入该表中,
如果目标表不存在,则创建再插入所查询的记录。
CREATE 先创建目标表再插入所查询的记录,
如果目标表不存在,则提示错误。
INSERT 将查询(必须用 using query 查)的记录插入到目标表中,
如果目标表不存在,则提示错误。
REPLACE 将查询的记录替换到目标表中的内容,
如果目标表存在,则先删旧表内容再用拷贝表替换,
如果目标表不存在,则创建表。
使用copy 命令还需设置以下变量:
SQL>set long n; /* 数据长度 */
SQL>setcopycommit m;/* m行提交一次 */
SQL>setarraysize n; /* 批操作的大小 */
§10.1.10 列值为NULL情形的处理
对于编程人员来说,如果对列的空值(NULL)不注意而使处理结果不正确的情况还比较多。这主要是由于数学上的问题引起的,数学的数字运算是:null + 数字 = null 。这样的法则使我们在处理表中的列的值为空时会出现不正确的结果。请看下面操作:
例子:假设EMP表有下面的记录:
SQL> select *from emp;
ENAME SAL DEPTNO TEL COMM
---------------------- ---------- -------------------- ----------
赵元杰 9000 10 1360 136 5681 1000
张三 8898 10 123456 1200
李四 7000 10 654321
从查询结果来看,李四的comm(奖金)没有值。当我们将sal+comm(工资+奖金)来列出结果时,则看到下面的结果:
SQL> selectename,sal,comm,sal+comm from emp;
ENAME SAL COMM SAL+COMM
---------------------- ---------- ----------
赵元杰 9000 1000 10000
张三 8888 1200 10088
李四 7000
由于李四没有奖金(comm 列值为空),所以“工资”加“奖金”也变为“空”了。
对于这样的情形,请你务必记住使用 NVL 函数来解决。NVL函数的格式为:
NVL ( value,substitute)
即value为空时以substitute代替,可以将上面语句改为下面语句就正确了:
SQL> selectename,sal,comm,nvl(sal,0)+nvl(comm,0) from emp;
ENAME SAL COMM NVL(SAL,0)+NVL(COMM,0)
---------------------- ---------- ----------------------
赵元杰 9000 1000 10000
张三 8888 1200 10088
李四 7000 7000
§10.1.11 使用 product_user_file来限制用户使用产品
缺省情况下,该表在oraclerdbms 中是不建立的,需要时可以system帐户下运行...\dbs\pupbld.sql即可建立该表,product_user_file表的结构如下:
product 产品名,sql*plus
userid 被禁止的用户(大写)
attribute 被禁止的属性(具体命令)
scope 用于非oracle 产品,用null替换
numeric_value 用于非oracle 产品, 用null替换
char_value 必需含'DISABLED'
date_value 用于非oracle 产品,用null替换
long_value 用于非oracle 产品,用null替换
例1:禁止scott用户使用sql*plus ,则
insert intoproduct_user_profile
(product,userid,attribute,scope,numeric_value,
char_value,date_value,long_value)
values
('SQL*PLUS','SCOTT','HOST',NULL,NULL,'DISABLE',NULL,NULL);
例2:重新让scott用户使用sql*plus ,则
SQL>delete fromproduct_user_profile
Where userid='SCOTT'and attribute='HOST';
Attribute 可能的命令如下:
COPY
EDIT
EXECUTE
EXIT
GET
HOST
QUIT
PASSWORD
RUN
SAVE
SET
SPOOL
START
ALTER
ANALYZE
AUDIT
CONNECT
CREATE
DELETE
DROP
GRANT
INSERT
LOCK
NOAUDIT
RENAME
REVOKE
SELECT
SET ROLE
SET TRANSACTION
TRUNCATE
BEGIN
DECLARE
§10.2 常用技巧
下面给出开发人员在工作中经常遇到的问题的解决方法。这里的所有例子都经过作者的精心调试通过。希望能对那些新手的读者有点帮助。
§10.2.1 long 类型的查询
由于在SQLPLUS 下的缓冲区有限,并且LONG的缺省长度为80 个字符。所以要查询LONG类型的字段时,需要设置 LONG的值为一个合适的值。如:
SQL>set long 3000
SQL>col text for a90
SQL>select text from user_source;
§10.2.2 如何确定执行时间
在程序调试过程中,经常需要了解某个语句的执行时间。以选择较好的语句。用下面的设置可以显示每个语句所用去的时间(毫秒,毫秒/1000=秒)。
SQL> settiming on
SQL> selectcount(*) from emp;
COUNT(*)
--------------
14
real: 190
SQL>
实际需要 190/1000=0.19秒
§10.2.3 如何终止用户会话
有时由于系统资源紧张或需要进行系统的整理时,都希望将与系统处于连接的用户会话进行终止。可以用下面命令分两步实现:
1)先查询出所有用户的信息:
SQL> selectsid,serial#,username,terminal from v$session;
SID SERIAL# USERNAME TERMINAL
--------- --------------------------------------- ----------------
1 1 ZHAOYUANJIE
2 1 ZHAOYUANJIE
3 1 ZHAOYUANJIE
4 1 ZHAOYUANJIE
5 1 ZHAOYUANJIE
6 1 ZHAOYUANJIE
7 505
8 505
11 370 SYS ZHAOYUANJIE
9 rows selected.
real: 190
SQL>
2)用ALTER system kill session ‘sid,serial#’ ; 来终止:
SQL>Alter system kill session ’11,370’;
如果希望一次终止多个会话,可以采用变量的方式更为快捷,如:
SQL> selectsid,serial#,username,terminal from v$session;
SID SERIAL# USERNAME TERMINAL
--------- --------------------------------------- ----------------
1 1 ZHAOYUANJIE
2 1 ZHAOYUANJIE
3 1 ZHAOYUANJIE
4 1 ZHAOYUANJIE
5 1 ZHAOYUANJIE
6 1 ZHAOYUANJIE
7 833
8 833
11 370 SYS ZHAOYUANJIE
13 370 SCOTT ZHAOYUANJIE
10 rows selected.
SQL> altersystem kill session '&sid,&ser';
Enter value forsid: 13
Enter value forser: 370
old 1: alter system kill session'&sid,&ser'
new 1: alter system kill session '13,370'
System altered.
real: 100
§10.2.4 用TRANSLATE对数据加密和解密
TRANSLATE函数是一个变换函数,用它可以实现对存入表中数据进行加密和解密处理。函数语法如下:
REPLACE (‘string’ [,’string_in’,’string_out’])
String:希望被替换的字符串或变量。
String_in: 被替换字符串。
String_out: 要替换字符串。
例1:
PL/SQL
Var1:= REPLACE (‘Oracle’, ‘Or’, ‘Mir’,);
SQL
SELECT REPLACE (‘Oracle’, ‘Or’, ‘Mir’) “Example “ FROM DUAL;
Example
-------
Miracle
下面给出较复杂的例子,例1是一个 加密的存储过程,例2是用该过程进行加密的过程;例3是密的过程,例4是使用解密的过程:
例1:加密过程可以写成如下:
CREATE OR REPLACE FUNCTION ENCRYPT(INPASS IN VARCHAR2)
RETURN VARCHAR2 AS
STRING_IN VARCHAR(78);
STRING_OUT VARCHAR2(39);
OFFSET NUMBER(2);
OUTPASS VARCHAR2(30);
BEGIN
OFFSET := TO_NUMBER(TO_CHAR(SYSDATE,’SS’))MOD 39;
STRING_IN := ‘YN8K1JOZVURB3MDETS5GPL27AXWIHQ94C6F0#$_’;
STRING_OUT :=’_$#ABCDEFGHIJKMNOPQRSTUVWXYZ0123456789’;
OUTPASS := SUBSTR(STRING_IN,OFFSET,1);
STRING_IN := STRING_IN||STRING_IN;
STRING_IN := SUBSTR(STRING_IN,OFFSET,39);
OUTPASS :=OUTPASS||TRANSATE(UPPER(INPASS),STRING_IN,STRING_OUT);
RETURN OUTPASS;
END;
例2:编写下面过程以调用ENCRYPT来加密:
DECLARE
X VARCHAR2(40);
BEGIN
X := ENCRYPT(‘HELLO’);
DBMS_OUTPUT.PUT_LINE(‘Encrypted:’|| X );
END;
SQL>set serveroutput on
SQL>/
Encrypted:#2PVV9
例3:编写一个针对例1的加密函数程序:
CREATE OR REPLACE FUNCTION DECRYPT(OUTPASS IN VARCHAR2)
RETURN VARCHAR2 AS
STRING-IN VARCHAR2(780;
STRING_OUT VARCHAR2(39);
OFFSET NUMBER(2);
INPASS VARCHAR2(30);
BEGIN
STRING_IN :=‘YN8K1JOZVURB3MDETS5GPL27AXWIHQ94C6F0#$_’;
STRING_OUT :=’_$#ABCDEFGHIJKMNOPQRSTUVWXYZ0123456789’;
OFFSET := INSTR(STRING_IN,SUBSTR(OUTPASS,1,1));
STRING_IN := STRING_IN||STRING_IN;
STRING-IN := SUBSTR(STRING_IN,OFFSET,39);
INPASS :=TRANSLATE(UPPER(SUBSTR(OUTPASS,2)),STRING_OUT,STRING_IN);
RETURN INPASS;
END;
例4:编写一个调用解密的简单过程:
DECLARE
X VARCHAR2(30);
BEGIN
X := DECRYPT(‘#2PVV9’);
DBMS_OUTPUT.PUT_LINE(X);
END;
§10.2.5 如何用查询来修改数据
有时需要用另外表的数据或本表的数据来修改某些记录。这样的应用在实际中经常使用。以下例子具有代表性:
例1:更新一个字段的情况:
UPDATE DEPT SETDEPT_NAME=(SELECT DEPART_NAME FROM DEPART
WHERE DEPART.DEPTNO = DEPT.DEPTNO );
例2:更新多个字段的情况:
UPDATE CHP8A
SET (DEPT_NAME,MGR_ID)=(SELECT
DEPART_NAME,MANAGER_ID FROM CHP8B
WHERE CHP8B.DEPART_NUMBER=CHP8A.DEPT_NO
AND ROWNUM=1)
WHERE EXIST(SELECT ‘X’ FROM CHP8B whereCHP8B.depart_number=CHP8a.dept_no);
§10.2.6 如何产生创建用户的脚本
有时,我们需要在新的环境创建用户,这些用户来自一个已经存在的应用环境中。比如希望重新创建用户的命令如何来产生。下面的例子从数据字典中查询数据自动完成创建用户、创建角色、授权角色给用户操作。
accept uname prompt'输入希望的用户名: '
spool c:\gen_user
SELECT username,'CREATE USER '||username||' '||
DECODE(password, 'EXTERNAL', 'IDENTIFIEDEXTERNALLY',
'IDENTIFIED BY '''||password||'''') lne,
'DEFAULT TABLESPACE'||default_tablespace lne,
'TEMPORARY TABLESPACE'||temporary_tablespace||';' lne
FROM DBA_USERS
WHERE USERNAME LIKE UPPER('%&&uname%')
OR UPPER('&&uname') IS NULL
ORDER BY USERNAME;
SELECT username,'ALTER USER '||username||' QUOTA '||
DECODE(MAX_BYTES, -1, 'UNLIMITED',TO_CHAR(ROUND(MAX_BYTES/1024))||'K')
||' ON TABLESPACE'||tablespace_name||';' lne
FROM DBA_TS_QUOTAS
WHERE USERNAME LIKE UPPER('%&&uname%')
OR UPPER('&&uname') IS NULL
ORDER BY USERNAME;
spool off
§10.2.7 如何产生创建表结构的脚本
可以通过查询Oracle的数据字典USER_TABLES和USER_TAB_COLUMNS来产生创建表的脚本。这样的要求,其实主要是decode 函数的使用。下面给出两个例子。
例1.从USER_TAB_COLUMNS来产生创建表的脚本:
/************************************************************/
/* 功能:自动产生创建表的脚本 */
/* 文件名:gen_cre_tab1.sql */
/* 作者:赵元杰 */
/* 日期:2001.12.10 */
/************************************************************/
set linesize 500
set pagesize 1000
set arraysize 8
set feedback off
set heading off
selectdecode(t1.column_id,1,'CREATE TABLE '||t1.table_name|| ' (', ' ') a,
t1.column_name b,
t1.data_type||decode(t1.data_type,'DATE',
decode(t1.NULLABLE,'N',' not null'),
'VARCHAR2','('||to_char(t1.data_length)||')'||
decode(t1.NULLABLE,'N','not null'),
'NUMBER',decode(t1.data_precision,null,‘ ‘ ,
'('||to_char(t1.data_precision)||
','||to_char(t1.data_scale)||')' )
||decode(t1.NULLABLE,'N','not null'),
'CHAR','('||to_char(t1.data_length)||')'||
decode(t1.NULLABLE,'N','not null'))||
decode(t1.column_id,max(t2.column_id),');',',') c
FROMuser_tab_columns t1,user_tab_columns t2
WHERE t1.table_name = t2.table_name and
t1.table_name in (select distinctobject_name from user_objects
where object_type='TABLE')
group byt1.column_id,t1.table_name,t1.data_type,t1.nullable,
t1.data_length,t1.data_scale,t1.column_name,
t1.data_precision
order byt1.table_name,t1.column_id;
例2.从USER_TABLES和USER_TAB_COLUMNS来产生创建表的脚本:
/************************************************************/
/* 功能:自动产生创建表的脚本和相应的存储参数 */
/* 文件名:gen_cre_tab2.sql */
/* 作者:赵元杰 */
/* 日期:2001.12.10 */
/************************************************************/
set linesize 500
set pagesize 1000
set arraysize 8
set feedback off
set heading off
selectdecode(t1.column_id,1,'CREATE TABLE '||t1.table_name|| ' (', ' ') a,
t1.column_name b,
t1.data_type||decode(t1.data_type,'DATE',
decode(t1.NULLABLE,'N',' not null'),
'VARCHAR2','('||to_char(t1.data_length)||')'||
decode(t1.NULLABLE,'N','not null'),
'NUMBER',decode(t1.data_precision,null,‘ ‘ ,
'('||to_char(t1.data_precision)||
','||to_char(t1.data_scale)||')' )
||decode(t1.NULLABLE,'N','not null'),
'CHAR','('||to_char(t1.data_length)||')'||
decode(t1.NULLABLE,'N',' not null'))||
decode(t1.column_id,max(t2.column_id),
')'||chr(10)|| 'TABLESPACE '||t3.tablespace_name||chr(10)||
-- ‘PCTFREE ‘||TO_CHAR(t3.pct_free)|| chr(10)||
-- ‘PCTUSED ‘||TO_CHAR(t3.pct_used)|| chr(10)||
‘STORAGE( INITIAL ‘||TO_CHAR(t3.initial_extent)|| chr(10)||
‘NEXT ‘||TO_CHAR(t3.next_extent) ||’ );’ ||chr(10) ,
',') c
FROMuser_tab_columns t1,user_tab_columns t2 ,user_tables t3
WHERE t1.table_name = t2.table_name andt2.table_name=t3.table_name and
t1.table_name in (select distinctobject_name from user_objects
where object_type='TABLE')
group byt1.column_id,t1.table_name,t1.data_type,t1.nullable,
t1.data_length,t1.data_scale,t1.column_name,
t1.data_precision, t3.tablespace_name ,
-- t3.pct_free , t3.pct_used,
t3.initial_extent ,t3.next_extent
order byt1.table_name,t1.column_id;
§10.2.8 如何产生创建视图的脚本
对于视图来说,所有的创建视图的数据来源都在USER_VIEWS数据字典。但是USER_VIEWS的view_name是视图的名称;而视图的查询文本存放在text 列上。而且text列的类型是long 类型。所以在查询前先要设置long 类型为一个较大的值(因为long 的缺省值为80个字符),这样就可以查询完整的视图内容。
set linesize 150
set pagesize 1000
set arraysize 8
set feedback off
set heading off
set long 5000
col view_name fora20
col text for a80
select 'CREATE ORREPLACE VIEW '||VIEW_NAME||' AS ',TEXT
FROM USER_VIEWS order BY VIEW_NAME;
§10.2.9 如何产生创建序号的脚本
对于序号来说,所有的创建序号的数据都存放在USER_SEQUENCES数据字典。具体操作如下:
select 'CREATESEQUENCE '|| SEQUENCE_NAME||
' START WITH'||to_char(min_value)||
' INCREMENT BY'||to_char(INCREMENT_BY)||':'
FROMUSER_SEQUENCES order BY SEQUENCE_NAME;
§10.2.10 如何为用户创建公共同义词
同义词是Oracle用于管理用户对象的一种方法,它可以隐藏实际对象,但是不影响其他经过授权的使用。下面是创建用户的表、视图、序列的简单办法。
1.假设下面脚本的文件名为 cre_syn.txt:
accept uname prompt '输入希望的用户名: '
set echo off
set heading off
set feedback off
spool c:\cre_syn.sql
select 'drop public synonym ' || table_name || ';' from all_tableswhere owner = upper('&&username') ;
select 'create public synonym ' || table_name || ' for ' ||upper('&&username') || '.' || table_name || ';' from all_tables
where owner = upper('&&username') ;
select 'drop public synonym ' || view_name || ';' from all_viewswhere owner = upper('&&username') ;
select 'create public synonym ' || view_name || ' for ' ||upper('&&username') || '.' || view_name || ';' from all_views
where owner = upper('&&username') ;
select 'drop public synonym ' || SEQUENCE_NAME || ';' fromALL_SEQUENCES
where SEQUENCE_OWNER = upper('&&username') ;
select 'create public synonym ' || SEQUENCE_NAME || ' for ' ||upper('&&username') || '.' || SEQUENCE_NAME || ';'
from ALL_SEQUENCES where SEQUENCE_OWNER =upper('&&username') ;
spool off
2. 运行时,只要输入用户名字即可,如:
SQL> startc:\syn.txt
输入希望的用户名: zhao
原值 1: select 'droppublic synonym ' || table_name || ';' from all_tables where owner
新值 1: select 'droppublic synonym ' || table_name || ';' from all_tables where owner
drop public synonym ABC;
. . . . . .
3. 编辑产生创建脚本的cre_syn.sql文件,保留drop synonyms 和create public synonym两种语句,删除“原值 1: select”和“新值 1: select”.
第二部分 Oracle PL/SQL基础
第十一章 PL/SQL 程序设计简介
在这部分用简短的篇幅描述一下PL/SQL程序设计的基本知识。由于本教材是针对初学者,因而内容只涉及PL/SQL的基础部分。希望深入了解的读者请参考目前书店销售的《Oracle8I PL/SQL高级程序设计》和《PL/SQL User’sGuide and reference》。
§11.1 概述
早些的Oracle5以前是没有PL/SQL这个产品的。由于当时数据库与编程的接口基本上都使用高级语言(简称3GL),如Fortran、COBOL和C/C++等。所以一般编程都是以高级语言为主,将数据库作为辅助的存储数据的工具来使用数据库系统。后来由于数据库的发展和软件技术的发展。出现了第4 代开发工具,称为4GL。4GL虽然在编程方面功能强大,但在与数据库接口上仍然采用SQL语句(现在4GL与数据库的通信仍以SQL语句来实现)。4GL的功能是在处理界面上有优势,但在数据库的数据处理上仍不是它们的特长。鉴于数据库在SQL方面的优势,Oracle公司在Oracle6版本以后开发了PL/SQL产品。起初它仅是一个辅助的产品。用它也只是弥补SQL语句本身不进行过程编程的不足。但是后来由于数据库技术的发展。有些应用业务处理要求进行大量的数据处理。比如银行的年终结息;商场的日结、月结等。这类处理基本上不要求进行实时的人-机交互。而是只关心最后的结果。由于这类业务的需求增长,许多数据库公司纷纷在自己的数据库引擎上增加了存储过程这样的功能。比如Sybase的相应产品叫存储过程;IBM也叫存储过程,但它不但可以将类似PL/SQL的程序写进数据库系统中,此外也可以将C语言写的程序加进数据库中。处于这样的一种需求和时髦的推动,Oracle便在后来的版本中增强了PL/SQL的功能。包括现在的许多系统的处理几乎是由PL/SQL写成,而不是用C来编写。这样的看法可以从目前的大量PL/SQL存储包和Oracle9i的iAS就可以看出来。
§11.2 SQL与PL/SQL
与其它的开发工具类似,在Oracle的PL/SQL工具中,可以使用标准SQL的部分子集。
§11.2.1 什么是PL/SQL?
PL/SQL是 Procedure Language & Structured Query Language 的缩写。Oracle的SQL是支持ANSI(Americannational Standards Institute)和ISO92(International Standards Organization)标准的产品。PL/SQL是对SQL语言存储过程语言的扩展。从Oracle6以后,Oracle的RDBMS附带了PL/SQL。它现在已经成为一种过程处理语言,简称PL/SQL(发音:pea ell sequel)。目前的PL/SQL包括两部分,一部分是数据库引擎部分;另一部分是可嵌入到许多产品(如C语言,JAVA语言等)工具中的独立引擎。可以将这两部分称为:数据库PL/SQL和工具PL/SQL。两者的编程非常相似。都具有编程结构、语法和逻辑机制。工具PL/SQL另外还增加了用于支持工具(如Oracle Forms)的句法,如:在窗体上设置按钮等。本章主要介绍数据库PL/SQL内容。
§11.2.1 PL/SQL的好处
§11.2.1.1 有利于客户/服务器环境应用的运行
对于客户/服务器环境来说,真正的瓶颈是网络上。无论网络多快,只要客户端与服务器进行大量的数据交换。应用运行的效率自然就回受到影响。如果使用PL/SQL进行编程,将这种具有大量数据处理的应用放在服务器端来执行。自然就省去了数据在网上的传输时间。
§11.2.1.2 适合于客户环境
PL/SQL由于分为数据库PL/SQL部分和工具PL/SQL。对于客户端来说,PL/SQL可以嵌套到相应的工具中,客户端程序可以执行本地包含PL/SQL部分,也可以向服务发SQL命令或激活服务器端的PL/SQL程序运行。
§11.2.1.3 客户及服务器端的好处
l 减少网络传输量
l 开发标准的代码
l 力争低偶联与高内聚性(少用全局变量和建立包来实现)
l 隐藏实现细节
l 统一的和集中的方式处理工程项目,以达到重用
l 更好地设计数据库应用(标准化、降低维护)
l 支持SQL语句
l 支持面向对象编程
l 更好的性能
l 轻便性(portability)
l 更高的编程效率(productivity)
§11.2.2 PL/SQL 可用的SQL语句
PL/SQL是Oracle系统的核心语言,现在Oracle的许多部件都是由PL/SQL写成。在PL/SQL中可以使用的SQL语句有:
l 在PL/SQL中可以用的SQL语句有:
INSERT
UPDATE
DELETE
SELECT INTO
COMMIT
ROLLBACK
SAVEPOINT
提示:在 PL/SQL中只能用SQL语句中的 DML 部分,不能用 DDL 部分,如果要在PL/SQL中使用DDL(如Create table 等)的话,只能以动态的方式来使用。
l Oracle 的 PL/SQL 组件在对 PL/SQL 程序进行解释时,同时对在其所使用的表名、列名及数据类型进行检查。
l PL/SQL 可以在SQL*PLUS 中使用。
l PL/SQL 可以在高级语言中使用。
l PL/SQL可以 在Oracle的 开发工具中使用。
l 其它开发工具也可以调用PL/SQL编写的过程和函数,如Power Builder 等都可以调用服务器端的PL/SQL过程。
§11.3 运行PL/SQL程序
PL/SQL程序的运行是通过Oracle中的一个引擎来进行的。这个引擎可能在Oracle的服务器端,也可能在 Oracle 应用开发的客户端。引擎执行PL/SQL中的过程性语句,然后将SQL语句发送给数据库服务器来执行。再将结果返回给执行端。比如已经用下面的脚本创建了一个存储过程:
create or replaceprocedure fundstat1(start_year_month in varchar2 ,
end_year_month in varchar2 ) is
year_month0 varchar2(7); --年月
stat_date0 date; --统计时间
bank_code0 varchar2(20); --经办行代码
no_id0 number(2); --经办行序号
...
-- 声明光标, 用于 在各经办行信息表(bank_code)中取出每一个经办行名称
cursor get_bank is select bank_code ,bank_name,no_id
from bank_code order by no_id;
begin
set transaction use rollback segmenthdhouse_rs;-- SCO 上的回退段
begin
if (substr(end_year_month,5,2) >= 1
. . . . . .
end;
则在SQL*PLUS下运行可以用下面命令启动运行:
SQL>execute funstat1(‘2001.01’,’2001.02’);
类似地,在Power builder和Developer/2000下可以用下面语句来启动运行:
EXECUTE fundstat1(‘2001.01’,’2001.03’);
或
date1 := ‘2001.01’;
date2 := ‘2001.03’;
EXECUTE fundstat1 ( :date1,:date2 );
§11.4 PL/SQL内置包
除了Oracle引擎提供环境让程序设计者根据应用要求来开发各个存储过程、函数、包及触发器外。Oracle系统本身提供了一套功能强大的内置包。这些系统包都以DBMS_开头进行命名。用这些包可以完成许多Oracle管理功能。关于Oracle内置包的详细说明请参考《Oracle8ISupplied PL/SQL Packages Reference》原版资料。下面是一些常用包的简单说明:
l DBMS_ALERT 数据库报警,允许会话间通信;
l DBMS_JOB 任务调度服务;
l DBMS_LOB 大对象操作用的包;
l DBMS_PIPE 数据库管道用的包;
l DBMS_SQL 动态SQL所用的包;
l UTL_FILE 文本文件I/O所用的包。
第十二章PL/SQL 块结构和组成元素
本章给出编写PL/SQL程序的基本结构和语句。
§12.1 PL/SQL结构
l PL/SQL块中可以包含子块;
l 子块可以位于 PL/SQL中的任何部分;
l 子块也即PL/SQL中的一条命令;
l 已定义的对象有一定的作用范围。
举例:本例是 一个嵌套子块的结构:
Declare
/* 本部分可以定义变量、光标等。 */
v_name varchar2(10);
v_sql number(7,2);
Begin
/* 执行部分*/
. . . . . .
select ename, sal intov_name, v_sal from scott.emp where empno=’7777’;
begin
/* 子块开始,也是执行语句 */
. . . . . .
EXCEPTION
/* 执行异常处理部分 */
. . . . . .
end;
EXCEPTION
/* 执行异常部分 */
. . . . . .
end; /* 本PL/SQL 程序结束*/
§12.2 PL/SQL块
PL/SQL程序由三个块组成,即 声明部分、执行部分、异常处理部分。
PL/SQL块的结构如下:
Declare
/* 声明部分: 在此 声明PL/SQL用到的变量,类型及光标 */
begin
/* 执行部分: 过程及SQL 语句 , 即程序的主要部分 */
Exception
/* 执行异常部分:错误处理 */
End;
其中 执行部分是必须的。
块可以分为四类:
无名块:动态构造,只能执行一次。
命名块:加了标号的块。
子程序:存储在数据库中的存储过程、函数及包等。当在数据库上建立好后可以在其它程序中调用它们。
触发子:当数据库发生操作时,回触发一些事件,从而自动执行相应的程序。
§12.3 标识符
PL/SQL程序设计中的标识符定义与SQL 的标识符定义的要求相同。要求和限制有:
l 标识符名不能超过30字符;
l 第一个字符必须为字母;
l 不分大小写;
l 不能用’-‘(减号);
l 不能是SQL保留字。
举例1: 合法的标识符:
declare
v_name varchar2(20); /* 存放name列的值 */
v_sal number(9,2); /* 存放sal 列的值 */
. . . . . .
举例2: 不合法的标识符:
declare
v-name varchar2(20); /* 存放name列的值 */
2001_sal number(9,2); /* 存放sal 列的值 */
mine&yours number; -- 非法的标识符
debit-amount number(10,4) ; -- 非法的标识符
on/off char(1); -- 非法的标识符
user id varchar2(20); -- 非法的标识符(不能用空格)
提示: 一般不要把变量名声明与表中字段名完全一样,如果这样可能得到不正确的
结果.
变量命名在PL/SQL中有特别的究竟,建议在系统的设计阶段就要求所有编程人员共同遵守一定的要求。使得整个系统的文档在规范上达到要求。下面是建议的命名方法:
变量名 | 意义 |
V_variablename | 程序变量 |
E_exceptionName | 自定义的异常标识 |
T_TypeName | 自定义的类型 |
P_parameterName | 存储过程、函数的参数变量 |
C_ContantName | 用CONTANT限制的变量 |
§12.4 PL/SQL 变量类型
在前面的介绍中,有系统的数据类型,也可以自定义数据类型。下表是Oracle类型和PL/SQL中的变量类型的合法使用列表:
§12.4.1 变量类型
在Oracle8i中可以使用的变量类型有:
类型 | 子类 | 说 明 | 范 围 | ORACLE限制 |
Char | Character String Rowid Nchar | 定长字符串
接受nls 数据 | 0à32767 可选,缺省=1 | 255 |
Varchar2 | Varchar String Nvarchar2 | 可变字符串
| 0à32767 4000 | 2000 |
Binary_integer |
| 带符号整数,为整数计算优化性能 |
|
|
Number(p,s) | Dec
Double precision Integer Int Numeric Real Small int | 小数, Number 的子类型 高精度实数 整数, Number 的子类型 整数, Number 的子类型 与Number等价 与Number等价 整数, 比 integer 小 |
|
|
Long |
| 变长字符串 | 0->2147483647 | 32,767字节 |
Date |
| 日期型 | 公元前4712年1月1日至公元后4712年12月31日 |
|
Boolean |
| 布尔型 | TRUE, FALSE,NULL | 不使用 |
ROWID |
| 存放数据库行号 |
|
|
例1.
declare
order_no number(3);
cust_name varchar2(20);
order_date date;
emp_no integer := 25; -- 缺省为 25
pi constant number := 3.14159;
begin
null;
end;
§12.4.2 复合类型(记录和表)
ORACLE 在 PL/SQL 中除了提供象前面介绍的各种类型外,还提供一种称为复合类型的类型---记录和表。
1. 记录类型
定义记录类型语法如下:
TYPE record_type IS RECORD(
Field1 type1 [NOT NULL] [:= exp1 ],
Field2 type2 [NOT NULL] [:= exp2 ],
. . . . . .
Fieldn typen [NOT NULL] [:= expn ] ) ;
例 :
-- 节选自在线代码 assign.sql
DECLARE
TYPE t_rec1 type is record (
Field1 number,
Field2 varchar2(5) );
TYPE t_rec2 IS RECORD(
Field1 number,
Field2 varchar2(5));
V_Rec1 t_rec1 type;
V_recc1 t_rec2 type;
Begin
/* 赋值 (要求类型一致) */
v_rec1 := v_rec2;
v_rec1.field1 := v_rec2.field1;
v_rec2.field2 := v_rec2.field2;
end;
可以用 select 语句对记录变量进行赋值,只要保证 记录字段与 查询结果列表中的字段相配即可。
例:
--节选自在线代码 select.sql
DECLARE
-- 用%TYPE 类型定义与表相配的字段
TYPE t_studentrecord IS RECORD(
Firstname sutdents.first_name%TYPE,
Lastname sutdents.last_name%TYPE,
Major sutdents.major%TYPE );
-- 声明接收数据的变量
v_stduent t_StudentRecord;
Begin
Select first_name,last_name,major
Into v_student
From students
Where id=10000;
End;
§12.4.3 使用%ROWTYPE
PL/SQL 可以声明与数据库行有相同类型的记录。
例:
--节选自在线tabrec.sql
DECLARE
TYPE t_studenttable is TABLE OF students%ROWTYPE
INDEX BYBINARY_INTEGER;
/* v_students 的每一元素是一个记录 */
v_students t_studentstable;
Begin
/* 将结果存到数组变量中 */
select * into v_students(10001) from students
where id=10001;
end;
§12.4.4 LOB类型
Oracle提供了LOB (Large OBject)类型,用于存储大的数据对象的类型。Oracle目前主要支持BFILE, BLOB, CLOB 及 NCLOB 类型。
BFILE
存放大的二进制数据对象,这些数据文件不放在数据库里,而是放在操作系统的某个目录里,数据库的表里只存放文件的目录。
BLOB
存储大的二进制数据类型。每个变量存储大的二进制对象的位置。大二进制的大小<=4GB。
CLOB
存储大的字符数据类型。每个变量存储大字符对象的位置,该位置指到大字符数据块。大字符的大小<=4GB。
NCLOB
存储大的NCHAR字符数据类型。每个变量存储大字符对象的位置,该位置指到大字符数据块。大字符的大小<=4GB。
§12.4.5 用户定义的子类型
在Oracle7.2后的版本中,可以定义一种称为子类型的类型,子类型可以使真正类型的名字变为另外的名字(注意仅仅是一种真正类型的另外叫法)。子类型的声明使用SUBTYPE命令说明。如:
SUBTYPE CHARACTER IS CHAR;
SUBTYPE INTEGER IS NUMBER(38,0); -- allows only whole numbers
这里子类型CHARACTER
与真正类型 CHAR
一样
, 所以 CHARACTER
是一个不受约束的
子类型。
但是 INTEGER
是基本类型的一个子类型 NUMBER
,所以INTEGER
是受约束的子类型。
定义子类型
语法如下:
SUBTYPE subtype_name ISbase_type[(constraint)] [NOT NULL];
例:
DECLARE
SUBTYPE BirthDate IS DATE NOT NULL; -- 基于 DATE 类型
SUBTYPE Counter IS NATURAL; -- 基于 NATURAL 子类型
TYPE NameList IS TABLE OF VARCHAR2(10);
SUBTYPE DutyRoster IS NameList; -- 基于 TABLE 类型
TYPE TimeRec IS RECORD (minutes INTEGER, hours INTEGER);
SUBTYPE FinishTime IS TimeRec; -- 基于 RECORD 类型
SUBTYPE ID_Num IS emp.empno%TYPE; -- 基于 column 类型
使用子类型
一旦定义子类型,就可以在声明的地方使用子类型。
DECLARE
SUBTYPE Counter IS NATURAL;
rows Counter;
DECLARE
SUBTYPE Accumulator IS NUMBER;
total Accumulator(7,2);
例。
DECLARE
SUBTYPE Numeral IS NUMBER(1,0);
x_axis Numeral; -- 大小范围: -9 .. 9
y_axis Numeral;
BEGIN
x_axis := 10; -- 触发 VALUE_ERROR
...
END;
类型的兼容性
一个无约束的子类型可以与基本类型进行交换,如:
DECLARE
SUBTYPE Accumulator IS NUMBER;
amount NUMBER(7,2);
total Accumulator;
BEGIN
...
total := amount;
...
END;
不同的子类型也可以互换,如:
DECLARE
SUBTYPE Sentinel IS BOOLEAN;
SUBTYPE Switch IS BOOLEAN;
finished Sentinel;
debugging Switch;
BEGIN
...
debugging := finished;
...
END;
不同的子类型也可以互换,如:
DECLARE
SUBTYPE Word IS CHAR(15);
SUBTYPE Text IS VARCHAR2(1500);
verb Word;
sentence Text(150);
BEGIN
...
sentence := verb;
...
END;
§12.4.6 数据类型的转换
可以进行转换的隐式类型转换有:
| BIN_INT | CHAR | DATE | LONG | NUMBER | PLS_INT | RAW | UROWID | VARCHAR2 |
BIN_INT |
| X |
| X | X | X |
|
| X |
CHAR | X |
| X | X | X | X | X | X | X |
DATE |
| X |
| X |
|
|
|
| X |
LONG |
| X |
|
|
|
| X |
| X |
NUMBER | X | X |
| X |
| X |
|
| X |
PLS_INT | X | X |
| X | X |
|
|
| X |
RAW |
| X |
| X |
|
|
|
| X |
UROWID |
| X |
|
|
|
|
|
| X |
VARCHAR2 | X | X | X | X | X | X | X | X |
|
§12.5 运算符和表达式(数据定义)
与其他语言一样,为了完成所要求的各种处理,PL/SQL需要下面各个运算符和表达式。
§12.5.1 关系运算符
关系运算符有:
运算符 | 意义 |
| 等于 |
| 不等于 |
| 小于 |
| 大于 |
| 小于或等于 |
| 大于或等于 |
§12.5.2 一般运算符
一般运算符有:
运算符 | 意义 |
| 加号 |
| 减号 |
| 乘号 |
| 除号 |
:= | 赋值号 |
=> | 关系号 |
.. | 范围运算符 |
|| | 字符连接符 |
§12.5.3 逻辑运算符
逻辑运算符有:
运算符 | 意义 |
is null | 是空值 |
Between | 介于两者之间 |
In | 在一列值中间 |
And | 逻辑与 |
Or | 逻辑或 |
Not | 取返,如 is not null, not in, |
§12.6 变量赋值
在PL/SQL编程中,变量赋值是一个值得注意的地方,它的语法如下:
variable := expression ;
variable 是一个PL/SQL变量,expression 是一个PL/SQL 表达式.
§12.6.1 字符及数字运算特点
l 空值加数字仍是空值:
null + < 数字>= null
l 空值加(连接)字符,结果为字符:
null || <字符串>= < 字符串>
§12.6.2 Boolean 赋值
布尔值只有TRUE, FALSE及 NULL三个值。如:
DECLARE
done BOOLEAN;
the following statements are legal:
BEGIN
done := FALSE;
WHILE NOT done LOOP
...
END LOOP;
§12.6.3 数据库赋值
数据库赋值是通过select 语句来完成的。每执行select 语句一次只能赋值一次。一般要求被赋值的变量与select中的列名要一一对应。如:
DECLARE
emp_idemp.empno%TYPE;
emp_nameemp.ename%TYPE;
wagesNUMBER(7,2);
BEGIN
...
SELECTename, sal + comm
INTOemp_name, wages FROM emp
WHEREempno = emp_id;
...
END;
提示:不能将select语句中的列赋值给布尔变量。
Declare
V_string1 varchar2(10);
V_string2 varchar2(15);
V_numeric number;
Begin
V_string1 :=’Hello’;
V_string2 := v_string1;
V_numeric := -12.4;
End;
§12.6.4 可转换的类型赋值
l CHAR 转换为 NUMBERT
使用 TO_NUMBER 函数来完成字符到数字的转换,如:
v_total := to_number(‘100.0’) + sal;
l NUMBERT 转换为CHAR
使用 TO_CHAR函数可以实现 数字到字符的转换,如:
v_comm := to_char(‘123.45’) || ’元’ ;
l 字符 转换为 日期
使用 TO_DATE函数可以实现 字符到日期的转换,如:
v_date := to_date('2001.07.03','yyyy.mm.dd');
l 日期 转换为 字符
使用 TO_CHAR函数可以实现 日期到字符的转换,如:
v_to_day := to_char(sysdate,'yyyy.mm.dd hh24:mi:ss') ;
可以在SQL>下输入下面命令来验证以上的语句:
selectto_char(sysdate,'yyyy')||'年'||to_char(sysdate,'mm')||'月'
||to_char(sysdate,'dd')||'日' from dual;
TO_CHAR(SYSDAT
--------------
2001年07月02日
SQL> altersession set nls_date_format ='yyyy"年"mm"月"dd"日"';
Session altered.
SQL> selectsysdate from dual;
SYSDATE
--------------
2001年04月24日
SQL> selectto_number('133')+200 from dual;
TO_NUMBER('133')+200
--------------------
333
SQL> selectto_char('321')||'元' from dual;
TO_CH
-----
321元
SQL> selectto_date('2001.07.03','yyyy.mm.dd') from dual;
TO_DATE('2
----------
03-7月-01
§12.7 变量作用范围以可见性
在PL/SQL编程中,如果在变量的定义上没有做到统一的话,可能会隐藏一些危险的错误,这样的原因主要是变量的作用范围所致。与其它高级语言类似,PL/SQL的变量作用范围特点是:
l 变量的作用范围是在你所引用的程序单元(块、子程序、包)内。即从声明变量开始到该块的结束。
l 一个变量(标识)只能在你所引用的名字内是可见的。
l 当一个变量超出了作用范围,PL/SQL引擎就释放用来存放该变量的空间(因为它可能不用了)。
l 在子块中重新定义该变量后,它的作用仅在该块内。
§12.8 注释
在PL/SQL里,可以使用两种符号来写注释,即:
l 使用双 ‘-‘ ( 减号) 加注释
PL/SQL允许用双 - - (双减号)来写注释,它的作用范围是只能在1行有效。如:
V_Sal number(12,2); -- 工资变量。
l 使用/* */ 来加一行或多行注释
使用/* . . . */ 在PL/SQL程序中加,可以对多行进行注释。如:
如:
/***********************************************/
/* 文件名:stattistcs_sal.sql */
/* 功能:统计整个部门工资 */
/* 作者: 赵元杰 */
/* 修改日期 :2001.07.03 */
/***********************************************/
或
/***********************************************
文件名:stattistcs_sal.sql
功能:统计整个部门工资
作者: 赵元杰
修改日期 :2001.07.03
***********************************************/
提示:被解释存放在数据库中的PL/SQL 程序,一般系统自动将程序头部的注释去掉。只有在Procedure 之后的注释才被保留;另外程序中的空行也自动被去掉。
建议:为了标准起见,最好使用 /* . .. */ 这样的注释语句。因为这样的注释在许多语言中是相同的。
§12.9 简单例子
在我们给出编写PL/SQL程序所需的各个语句之前,先给出一些简单的例子,目的是让读者对PL/SQL程序先有个感性认识。
§12.9.1 简单数据插入例子
/* 本例子仅是一个简单的插入,不是实际应用。 */
declare
v_ename varchar2(20) := ‘赵元杰’;
v_sal number(7,2) :=1234.56;
v_deptno number(2) := 10;
v_empno number(4) := 8888;
begin
insert into emp ( empno, ename, JOB, sal, deptno, hiredate )
values ( v_empno, v_ename, ‘自由职业’,v_sal, v_deptno, to_date(’1954.06.09’,’yyyy.mm.dd’) );
commit;
end;
/
show error
§12.9.2 简单数据删除例子
/* 本例子仅是一个简单的删除例子,不是实际应用。 */
declare
v_ename varchar2(20) := ‘赵元杰’;
v_empno number(4) := 8888;
begin
delete from emp where empno=v_empno and ename=v_ename;
commit;
end;
/
show error
第十三章PL/SQL 处理流程
在PL/SQL程序中,要使程序能按照逻辑进行处理,除了有些语句是SQL语句外,还必须有能进行逻辑控制的语句。下面就介绍进行处理流程的语句结构。
§13.1 条件语句
IF <布尔表达式>THEN
PL/SQL 和 SQL语句
END IF;
IF <布尔表达式>THEN
PL/SQL 和 SQL语句
ELSE
其它语句
END IF;
IF <布尔表达式>THEN
PL/SQL 和 SQL语句
ELSIF < 其它布尔表达式> THEN
其它语句
END IF;
提示: ELSIF 不能写成 ELSEIF
例:
--节选自在线代码 if1.sql
DECLARE
V_numberseats rooms.number_seats%TYPE;
V_comment varchar2(35);
Degin
Select number_seats into v_numberseats
From rooms where room_id=99999;
IF v_numberseats<50 then
V_comment := ‘Fairly small’;
Elsif v_numberseats < 100 then
V_comment := ‘A little bigger’;
Else
V_comment := ‘Lots of room’;
End;
§13.2 循环
1. 简单循环
Loop
要执行的语句;
end loop;
--此循环将执行到遇到一条 exit 语句为止.
例1.
declare
x number;
begin
x:= 0;
loop
x:=x+1;
dbms_output.put_line(to_char(x));
exit when x=10;
end loop;
end;
例 2.
--节选自在线代码 simple.sql
DECLARE
V_counter BINARY_INTEGER := 1;
Begin
LOOP
Inert into temp_table
Values( v_counter, ‘loop index’ );
V_counter := v_counter + 1;
If v_counter > 50 then
Exit;
End if ;
End loop;
End;
例 3.
--节选自在线代码 exitwhen.sql
DECLARE
V_counter binary_index := 1;
Begin
Loop
Insert into temp_table
Values ( v_counter,’ loop index ‘ );
Exit when v_counter > 50 ;
End loop;
End;
2. WHILE 循环
While 循环
While <布尔表达式> loop
要执行的语句;
end loop;
例1.
declare
x number;
begin
x:= 1;
while x<10 loop
dbms_output.put_line(to_char(x)||’还小于10’);
x:= x+1;
end loop;
end;
例 2.
--节选自在线代码 while1.sql
DECLARE
V_counter binary_integer := 1;
Begin
While v_counter <= 50 loop
Inert into temp_table
Values( v_counter, ‘loop index ‘) ;
V_counter := v_counter + 1;
End loop;
End;
3. 数字式循环
For 循环
For 循环计数器 in 下限.. 上限
loop
要执行的语句;
end loop;
FOR loop_counter IN [ REVERSE ] low_bound . . high_bound LOOP
Sequence_of_statements;
END LOOP;
例1.
begin
for I in 1 .. 10 loop
dbms_output.put_line(‘in=’||to_char(I));
end loop;
end;
例 2.
--节选自在线代码 forscope.sql
DECLARE
V_counter number := 7;
Begin
Inert into temp_table (num_col)
Values ( v_counter );
For v_counter IN 20 .. 30 loop
Insert into temp_table (num_col )
Values ( v_counter );
End loop;
Inert into temp_table (num_col )
Values( v_counter );
End ;
注:如果在for中用 INVERSE 关键字,则循环索引将从最大向最小进行迭代.
§13.3 标号和GOTO
PL/SQL中GOTO语句是无条件跳转到指定的标号去的意思。语法如下:
GOTO label;
. . . . . .
<<label>>
例:
--节选自在线代码 goto.sql
DECLARE
V_counter BINARY_INTEGER := 1;
Begin
Loop
Inert into temp_table
Values( v_counter,’loop count’ );
V_counter :=v_counter + 1;
If v_counter > 50then
Goto l_endofloop;
End if;
End loop;
<<l_endofloop>>
insert into temp_table ( char_col )
values(‘Done !’);
End ;
§13.4 NULL 语句
在 PL/SQL 程序中,null语可以用 null 语句来说明“不用做什么”的意思。如:
declare
. . .
begin
…
if( v_num is null then
goto print1;
end if;
…
<<print1>>
NULL; -- 不需要处理任何数据。
End;
第十四章 光标的使用
与在SQL*PLUS命令方式下不同,在 PL/SQL 程序中,对于处理多行记录的事务经常使用光标来实现。
§14.1 光标概念
为了处理SQL 语句,oracle 必须分配一片叫上下文( contextarea )的区域来处理所必需的信息,其中包括要处理的行的数目,一个指向语句被分析以后的表示形式的指针以及查询的活动集(active set).
光标是一个指向上下文的句柄( handle)或指针。通过光标, PL/SQL 可以控制上下文区和处理语句时上下文区会发生些什么事情。
§14.1.1 处理显式光标
在PL/SQL程序中定义的光标称作显式光标。下面是显式光标的使用介绍。
1. 显式光标处理
显式光标处理需四个 PL/SQL步骤:
cursor 光标名称 is 查询语句;
open 光标名称;
Fetch 光标名称 into 变量列表;
Close 光标名称;
例1.
declare
cursor c1 is
select ename, sal from emp where rownum<11;
v_ename varchar2(10);
v_sal number(7,2);
begin
open c1;
fetch c1 into v_ename, v_sal;
while c1%found
loop
dbms_output.put_line(v_ename||to_char(v_sal) );
fetch c1 into v_ename, v_sal;
end loop;
close c1;
end;
2.光标属性
%found 布尔型属性,当最近一次读记录时成功返回,则值为true
%nofound 布尔型属性,与%found相反.
%isopen 布尔型属性, 当光标已打开时返回 true.
%rowcount 数字型属性, 返回已从光标中读取得记录数.
3.参数化光标
在声明光标时,将未确定的参数说明成变量,在使用光标时给出光标变量的具体值(相当于实参)使得语句可以按照给出的条件进行查询。
例2:
DECLARE
Cursor c1 (view_pattern varchar2) IS
Select view_name from all_views
Where view_name like view_pattern’||% AND rownum <= 10
Order by view_name ;
Vname varchar2(40);
BEGIN
For I1 in c1 ( ‘DBA’) loop
DBMS_OUTPUT.PUT_LINE( I1.view_name ) ;
END loop;
END;
§14.1.2 处理隐式光标
所有的SQL 语句在上下文区内部都是可执行的,因此都有一个光标指向上下文区,此光标就是所谓的SQL光标(SQL cursor),与显式光标不同,SQL 光标不被程序打开和关闭。
例3:
--节自在线代码nomat1.sql
BEGIN
UPDATE rooms SET number_seats = 100
WHERE room_id = 99980;
-- 如果更新没有匹配则插入一新行
IF SQL%NOTFOUND THEN
INSERT INTO rooms ( room_id, number_seats)
VALUES ( 99980, 100 ) ;
END IF;
END;
例4:
--节自在线代码nomat2.sql
BEGIN
UPDATE rooms SET number_seats = 100
WHERE room_id = 99980;
-- 如果更新没有匹配则插入一新行
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO rooms ( room_id,number_seats )
VALUES ( 99980, 100 ) ;
END IF;
END;
例5:
--节自在线代码nodata.sql
DECLARE
V_roomdata rooms%ROWTYPE;
BEGIN
Select * into v_roomdata from rooms
Where room_id = -1 ;
IF SQL%NOFOUND THEN
INSERT INTOtemp_table ( char_col )
VALUES ( ‘Not Found! ‘) ;
END IF;
EXCEPTION
WHENNO_DATA_FOUND THEN
INSERT INTOtemp_table ( char_col )
VALUES ( ‘Not Found! exception handle. ‘) ;
END;
§14.2 光标循环
在高级语言编程和 PL/SQL 编程中,经常使用“提取循环(fetch loop)” 来实现逐行提取所需数据的方法。
§14.2.1 简单循环
可以使用LOOP 和END LOOP来实现简单循环。但这样的循环需要在循环体内说明跳出循环的语句,否则就会出现无限循环现象。
--节自在线代码simple.sql
DECLARE
V_studentID students.id%TYPE;
V_firstName students.first_name%TYPE;
V_LastName students.last_name%TYPE;
CURSOR c_HistoryStudents IS
SELECT id,first_name, last_name from students
WHERE major = ‘History’;
BEGIN
Open c_HistoryStudents ;
LOOP
FETCH c_HistoryStudents INTO v_studentID, v_FirstName,
V_LastName ;
EXIT WHEN c_HistoryStudents%NOFOUND ;
INSERT INTO registered_students(student_id,department,course )
VALUES (v_StudentsID,’HIS’,301 );
INSERT INTO temp_table ( num_col, char_col )
VALUES (v_StudentID, v_FirstName||’ ‘|| v_lastName ) ;
END LOOP;
CLOSE c_HistoryStudents;
COMMIT;
END;
提示:EXIT WHEN 是紧跟在FETCH 后面。
§14.2.2WHILE循环
与简单循环不一样的是,while 循环是先判断后执行。只有满足条件才能执行其循环体内的语句;而简单循环至少执行一次。看下面例子:
DECLARE
CURSORc_HistoryStudents IS
SELECT id,first_name, last_name from students
WHERE major = ‘History’;
BEGIN
Open c_HistoryStudents ;
LOOP
FETCH c_HistoryStudents INTO v_studentData;
WHILE c_HistoryStudents%FOUND LOOP
INSERT INTO registered_students(student_id,department,course )
VALUES (v_StudentsID,’HIS’,301 );
INSERT INTO temp_table ( num_col, char_col )
VALUES (v_StudentID, v_FirstName||’ ‘|| v_lastName ) ;
-- 返回下一行,%FOUND循环前检查
FETCH c_HistoryStudents INTO v_studentData;
END LOOP;
CLOSE c_HistoryStudents;
COMMIT;
END;
§14.2.3 光标 FOR 循环
除上面的循环外,一种可以控制次数的循环就是For循环。
--节选自在线代码 forloop.sql
DECLARE
CURSOR c_HistoryStudents IS
SELECT id,first_name,last_name
FROM students
WHERE major = ‘History’;
BEGIN
FOR v_StudentData IN c_HistoryStudents LOOP
INSERT INTO registered_students(student_id,department,course )
VALUES (v_StudentsID,’HIS’,301 );
INSERT INTO temp_table ( num_col, char_col )
VALUES (v_StudentID, v_FirstName||’ ‘|| v_lastName ) ;
-- 循环前隐含检查 %NOFOUND
END LOOP;
-- 自动关闭光标
COMMIT;
END;
§14.2.4 关于NO_DATA_FOUND和%NOTFOUND
一般初学者对Oracle的NO_DATA_FOUND和%NOTFOUND两个保留字的用法不够了解,主要是它们从意义上是有些类似。其实它们的用法是有区别的,小结如下:
l SELECT . . . INTO 语句触发 NO_DATA_FOUND;
l 当一个显示光标的 where 子句未找到时 触发%NOTFOUND;
l 当UPDATE或DELETE 语句的where子句未找到时 触发 SQL%NOTFOUND;
l 在光标的提取(Fetch)循环中要用 %NOTFOUND 或%FOUND来确定循环的退出条件,不要用 NO_DATA_FOUND。
§14.2.5SELECT FOR UPDATE 光标
为了对正在处理(查询) 的行不被另外的用户改动,oracle 提供一个 FOR UPDATE 子句来对所选择的行进行锁住。语法如下:
SELECT . . . FROM FOR UPDATE [OF column_reference] [NOWAIT]
如果另一个会话已对活动集中的行加了锁,那么 SELECT FOR UPDATE操作一直等待到其它的会话释放这些锁后才继续自己的操作,对于这种情况,如果加 NOWAIT 子句,如果这些行真的被另一个会话锁定,则OPEN立即返回并给出:
ORA-0054 :resourcebusy and acquire with NOWAIT specified.
如果 使用 FOR UPDATE 声明光标,则可在delete, update 语句中使用WHERE CURRENT OF 子句。
例6:
--节选自在线代码 forupdate.sql
DECLARE
V_NumCreadits classes.num_creadits%TYPE;
CURSOR c_RegisteredStudents IS
SELECT * FROM students
WHERE is IN ( SELECT student_id fromregistered_students
WHERE department = ‘HIS’ AND course =101 )
FOR UPDATE OF current_credits;
BEGIN
FOR v_Studentinfo INc_RegisteredStudents LOOP
SELECT num_credits INTOv_numcredits FROM classes
WHERE department =’HIS’ AND course=101;
UPDATE students
SET current_credits = current_credits + v_numcredits
WHERE CURRENT OF c_RegusteredStudents;
END LOOP;
COMMIT;
END;
§14.3 光标变量
前面所给出光标都是显式的光标,而且是静态的光标。Oracle从PL/SQLRelease2.2版以后,声明的光标可以不是静态的,光标变量就是允许开发人员编写的程序在运行时与不同的语句相关联。光标变量在运行可以取不同的值。以达到灵活的目的。
§14.3.1 声明光标变量
光标变量语法如下:
TYPE type_name IS REF CURSOR RETURN return_type;
§14.3.2 为光标变量分配存储空间
由于光标变量是一种引用类型,在使用前需要为其分配一片内存区,用下面语句完成分配:
EXEC SQL ALLCATE :variable
§14.3.3 打开光标变量
如果要将一个光标变量与一个特定的select语句关联,则需 Open 将其打开,语法如下:
OPEN cursor_variable FOR select_statements;
§14.3.4 关闭光标变量
关闭光标的语法如下:
CLOSE cursor_variable;
§14.3.5 光标变量例子
例 1.光标变量在PRO*C中使用:
-- 节选自在线代码 cursor1.pc
REM cursor1.pc
REM 这是一个Pro*C 程序。
/* 包括 C 和 SQL头文件. */
#include<stdio.h>
EXEC SQL INCLUDESQLCA;
/* SQL 声明部分:光标、变量都要这里声明 */
EXEC SQL BEGINDECLARE SECTION;
/* SQL 光标变量 */
SQL_CURSOR v_CursorVar;
/* 整数变量,用于查询表数据. */
int v_Table;
/* 用于room的输出变量. */
int v_RoomID;
VARCHAR v_Description[2001];
/* 用于classes的输出变量. */
VARCHAR v_Department[4];
int v_Course;
EXEC SQL END DECLARESECTION;
/* 错误处理程序,用于打印错误和退出. */
void handle_error(){
printf("SQL Error occurred!\n");
printf("%.*s\n",sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
EXEC SQL ROLLBACK WORK RELEASE;
exit(1);
}
int main() {
/* 字符串输入变量. */
char v_Choice[20];
/* 设置错误处理,当SQL出现错误时,可调用handle_error() */
EXEC SQL WHENEVER SQLERROR DO handle_error();
/* 连接到数据库. */
EXEC SQL CONNECT :v_Username;
printf("Connected to Oracle.\n");
/* 分配光标变量内存区. */
EXEC SQL ALLOCATE:v_CursorVar;
/* 显示提示信息。*/
printf("Choose from (C)lasses or(R)omms. Enter c or r: ");
gets(v_Choice);
/* 确定连接表. */
if (v_Choice[0] == 'c')
v_Table = 1;
else
v_Table = 2;
/* 使用嵌入PL/SQL来打开光标变量 */
EXEC SQL EXECUTE
BEGIN
IF :v_Table = 1THEN
/* 为Class表打开变量 */
OPEN:v_CursorVar FOR
SELECTdepartment, course
FROM classes;
ELSE
/* 为room 表打开变量 */
OPEN:v_CursorVar FOR
SELECTroom_id, description
FROMrooms;
END IF;
END;
END-EXEC;
/* 当取数完毕退出 */
EXEC SQL WHENEVER NOT FOUND DO BREAK;
/* 开始循环 */
for (;;) {
if (v_Table == 1) {
/* 取class 信息 */
EXEC SQL FETCH :v_CursorVar
INTO :v_Department, :v_Course;
/* 显示相应信息 */
printf("%.*s %d\n",v_Department.len, v_Department.arr,
v_Course);
}
else {
/* 取room 信息 */
EXEC SQL FETCH :v_CursorVar
INTO :v_RoomID, v_Description;
/* 显示相应信息 */
printf("%d %.*s\n", v_RoomID,v_Description.len,
v_Description.arr);
}
}
/* 关闭光标变量 */
EXEC SQL CLOSE :v_CursorVar;
/* 断开数据库连接. */
EXEC SQL COMMIT WORK RELEASE;
}
§14.3.6 光标变量
例 2.光标变量在PL/SQL中使用:
REM 选自在线代码cursor2.sql
REM 作者: Scott Urman.
REM 中文注释:赵元杰
CREATE OR REPLACEPROCEDURE ShowCursorVariable
/* 变量声明。*/
(p_Table IN VARCHAR2) AS
/* 定义光标变量类型 */
TYPE t_ClassesRooms IS REF CURSOR;
/* 光标变量引用 */
v_CursorVar t_ClassesRooms;
/* 处理输出变量. */
v_Department classes.department%TYPE;
v_Course classes.course%TYPE;
v_RoomID rooms.room_id%TYPE;
v_Description rooms.description%TYPE;
BEGIN
/* 根据输入参数来打开光标变量 */
IF p_Table ='classes' THEN
OPEN v_CursorVarFOR
SELECTdepartment, course
FROM classes;
ELSIF p_table ='rooms' THEN
OPEN v_CursorVarFOR
SELECT room_id,description
FROM rooms;
ELSE
/* 输入错误值则触发错误 */
RAISE_APPLICATION_ERROR(-20000,
'Input must be ''classes'' or''rooms''');
END IF;
/* 处理循环,当处理完退出 */
LOOP
IF p_Table = 'classes' THEN
FETCH v_CursorVar INTO
v_Department, v_Course;
EXIT WHEN v_CursorVar%NOTFOUND;
INSERT INTO temp_table (num_col,char_col)
VALUES (v_Course, v_Department);
ELSE
FETCH v_CursorVar INTO
v_RoomID, v_Description;
EXIT WHEN v_CursorVAR%NOTFOUND;
INSERT INTO temp_table (num_col,char_col)
VALUES (v_RoomID, SUBSTR(v_Description,1, 60));
END IF;
END LOOP;
/* 关闭光标变量. */
CLOSE v_CursorVar;
COMMIT;
ENDShowCursorVariable;
第十五章 错误处理
一个优秀的程序都应该能够正确处理各种出错情况,并尽可能从错误中恢复。Oracle 提供异常情况(EXCEPTION)和异常处理(EXCEPTION HANDLER)来实现错误处理。
§15.1 异常处理概念
异常情况处理(EXCEPTION)是用来处理正常执行过程中未预料的事件,程序块的异常处理预定义的错误和自定义错误,由于PL/SQL程序块一旦产生异常而没有指出如何处理时,程序就会自动终止整个程序运行.
异常处理部分一般放在 PL/SQL 程序体的后半部,结构为:
EXCEPTION
When first_exception then <code to handlefirst exception>
When second_exception then <code to handlesecond exception>
END;
异常处理可以按任意次序排列,但 Others 必须放在最后。
§15.1.1 预定义的异常处理
有两种类型的异常情态:用户定义(user_define)和预定义 ( predefined )
预定义说明的oracle 异常
ORACLE 错误 异常信息 说明
ORA-0001 Dup_val_on_index 试图破坏一个唯一性限制
ORA-0051 Timeout-on-resource 在等待资源时发生超时
ORA-0061 Transaction-backed-out 由于发生死锁事务被撤消.
ORA-1001 Invalid-cursor 试图使用一个无效的光标
ORA-1012 Not-logged-on 没有连接到ORACLE
ORA-1017 Login-denied 无效的用户名/口令
ORA-1403 Not-data-found 没有找到数据
ORA-1422 Too-many-rows select into 返回多行
ORA-1476 Zero-divide 试图被零除
ORA-1722 Invalid-number 转换一个数字失败
ORA-6500 Storage-error 内存不够引发的内部错误
ORA-6501 Program-error 内部错误
ORA-6502 Value-error 转换或截断错误
ORA-6504 Rowtype-mismatch 缩主光标变量与 PL/SQL变量
有不兼容行类型
ORA-6511 Cursor-already-open 试图打开一个已存在的光标
ORA-6530 Access-into-null 试图为null 对象的属性赋值
ORA-6531 Collection-is-null 试图将Exists 以外的集合(collection)
方法应用于一个 null pl/sql 表上或varray上
ORA-6532 Subscript-outside-limit 对嵌套或varray索引的引用超出声明范围
以外
ORA-6533 Subscript-beyond-count 对嵌套或varray索引得引用大于集合中
元素的个数.
§15.1.2 触发异常情态
当与一个异常情态相关的错误出现时,就会隐含引发该异常情态。用户定义的异常情态是通过显式使用 RAISE 语句来引发。
--节选自在线代码 handle.sql
REM 选自在线代码handle.sql
REM 作者: Scott Urman.
REM 中文注释:赵元杰
DECLARE
e_TooManyStudents EXCEPTION; -- 类型为Exception,用于指示错误条件
v_CurrentStudents NUMBER(3); -- HIS-101学生注册当前号
v_MaxStudents NUMBER(3); -- HIS-101学生注册允许的最大号
BEGIN
/* 找出注册学生当前号和允许的最大号 */
SELECT current_students, max_students
INTO v_CurrentStudents, v_MaxStudents
FROM classes
WHERE department = 'HIS' AND course = 101;
/* 检查学生的号 */
IF v_CurrentStudents > v_MaxStudents THEN
/* 太多的学生注册,则触发例外处理 */
RAISE e_TooManyStudents;
END IF;
EXCEPTION
WHEN e_TooManyStudents THEN
/* 当太多的学生注册,就插入信息解释发生过错误 */
INSERT INTO log_table (info) VALUES('History 101 has ' || v_CurrentStudents ||
'students: max allowed is ' ||v_MaxStudents);
END;
/
§15.1.3 处理异常情态
当引发一个异常情态时,控制就转向到EXCEPTION 块异常情态部分,执行错误处理代码。语法如下:
EXCEPTION
WHEN exception_name THEN
Sequence_of_statements1;
WHEN exception_name THEN
Sequence_of_statements2;
WHEN OTHERS THEN
Sequence_of_statements3;
END;
例:对于不确定的错误使用 sqlerrm 将其显示出。
--节选自在线代码 sqlerrm.sql
REM 作者: Scott Urman.
REM 中文注释:赵元杰
DECLARE
e_TooManyStudents EXCEPTION; -- 类型为Exception,用于指示错误条件
v_CurrentStudents NUMBER(3); -- HIS-101学生注册当前号
v_MaxStudents NUMBER(3); -- HIS-101学生注册允许的最大号
v_ErrorCode NUMBER; -- 错误处理代码变量
v_ErrorText VARCHAR2(200); -- 错误处理信息变量
BEGIN
/* 找出注册学生当前号和允许的最大号 */
SELECT current_students, max_students
INTO v_CurrentStudents, v_MaxStudents
FROM classes
WHERE department = 'HIS' AND course = 101;
/* 检查学生的号 */
IF v_CurrentStudents > v_MaxStudents THEN
/* 太多的学生注册,则触发例外 */
RAISE e_TooManyStudents;
END IF;
EXCEPTION
WHEN e_TooManyStudents THEN
/* 如果在HIS-101中有太多的学生注册,则插入日志信息解释已经发生的情况 */
for HIS-101. We will insert a logmessage explaining what has happened. */
INSERT INTO log_table (info) VALUES ('History101 has ' || v_CurrentStudents ||
'students: max allowed is ' ||v_MaxStudents);
WHEN OTHERS THEN
/* 所有其它错误的处理 */
v_ErrorCode := SQLCODE;
v_ErrorText := SUBSTR(SQLERRM, 1,200); -- Note the use of SUBSTR here.
INSERT INTO log_table (code, message, info)VALUES
(v_ErrorCode, v_ErrorText, 'Oracle erroroccurred');
END;
/
§15.1.4 用户定义的异常处理
可以使用RAISE_APPLICATION_ERROR创建自己的错误处理。其语法如下:
RAISE_APPLICATION_ERROR(error_number,error_message,[keep_errors]) ;
这里的error_number 是从 –20,000 到 –20,999 之间的参数,
error_message 是相应的提示信息(< 512 字节),
keep_errors 为可选,如果keep_errors =TRUE ,则新错误将被添加到已经引发的错误列表中。如果keep_errors=FALSE(缺省),则新错误将替换当前的错误列表。
--节选自在线代码register.sql
REM 作者: Scott Urman.
REM 中文注释:赵元杰
CREATE OR REPLACEPROCEDURE Register (
p_StudentID IN students.id%TYPE,
p_Department IN classes.department%TYPE,
p_Course IN classes.course%TYPE) AS
v_CurrentStudents NUMBER; -- 班上学生的当前号
v_MaxStudents NUMBER; -- 班上学生的最大号
BEGIN
/* 找出学生的当前号和最大号 */
SELECTcurrent_students, max_students
INTO v_CurrentStudents, v_MaxStudents
FROM classes
WHERE course = p_Course
AND department = p_Department;
/* 确认另外的学生是否有足够的教室*/
IF v_CurrentStudents + 1 > v_MaxStudentsTHEN
RAISE_APPLICATION_ERROR(-20000, 'Can''t addmore students to ' ||
p_Department || ' ' || p_Course);
END IF;
/* 加一个学生在本班 */
ClassPackage.AddStudent(p_StudentID,p_Department, p_Course);
EXCEPTION
WHEN NO_DATA_FOUND THEN
/* 教室信息不存在,触发错误处理*/
RAISE_APPLICATION_ERROR(-20001,p_Department || ' ' || p_Course ||
' doesn''t exist!');
END Register;
/
§15.2 异常情态传播
由于异常情态 可以在声明部分和执行部分以及异常情态部分出现,因而在不同部分引发的异常情态也不一样。
§15.2.1 在执行部分引发异常情态
当一个异常情态在执行部分引发时,有下列情况:
1) 如果当前块对该异常情态设置了处理,则执行它并成功完成该块的执行,然后控制转给包含块。
2) 如果没有对当前块异常情态设置定义处理器,则通过在包含块中引发它来传播异常情态。然后对该包含块执行步骤1)。
§15.2.2 在声明部分引发异常情态
如果在声明部分引起异常情况,即在声明部分出现错误,那么该错误就能影响到其它的块。比如在有如下的PL/SQL程序:
DECLARE
Abc number(3)=’abc’;
. . . . . .
begin
. . . . . .
EXCEPTION
WHEN OTHERS THEN
. . . . . .
END;
例子中,由于Abc number(3)=’abc’; 出错,尽管在 Exception 中说明了WHEN OTHERS THEN语句,但WHENOTHERS THEN也不会被执行。 但是如果在该错误语句块的外部有一个异常情态,则该错误就能被抓住,如:
DECLARE
Abc number(3)=’abc’;
. . . . . .
begin
. . . . . .
EXCEPTION
WHEN OTHERS THEN
. . . . . .
END;
EXCEPTION
WHEN OTHERS THEN
. . . . . .
END;
§15.3 异常处理编程
在一般的应用处理中,建议程序人员要用异常处理,因为如果你程序中不声明任何异常处理,则 在程序运行出错时,程序就被终止,并且也不提示任何信息。下面是使用系统提供的异常来编程的例子。
例:这是真实例子的一部分:
proceduredelete_shift_fund(cur_accno varchar2,
cur_procdatevarchar2,rtn out number) is
begin
set transaction use rollback segmenthdhouse_rs;
delete from shift_fund whereout_acc_no=cur_accno;
delete from shift_lst whereacc_no=cur_accno and
to_char(proc_date,'yyyymmdd')=cur_procdate;
commit;
rtn:=1;
Exception
when value_errorthen
rtn:=-1;
rollback;
end;
在例子中,用到了whenvalue_error then 语句。表示可能出现值赋值的错误。
§15.4 在 PL/SQL 中使用 sqlcode,sqlerrm
由于oracle 的错误信息最大长度是512字节, 为了的到完整的错误提示信息,我们可用 sqlerrm 和 substr 函数一起得到错误提示信息。
sqlcode 返回错误代码数字,
sqlerrm 返回错误信息.
如: sqlcode=+100 è sqlerrm=’no data found ‘
sqlcode=0 è sqlerrm=’normal, successfual completion’
例1.
declare
. . .
err_msg varchar2(100);
begin
/* 得到所有ORACLE 错误信息 */
for err_num in 1 .. 9999 loop
err_msg := sqlerrm(err_num);
insert into errors values(err_msg);
end loop;
end;
例2
-- 节选自在线代码 sqlerrm2.sql
--节选自在线代码sqlerrm2.sql
REM 作者: Scott Urman.
REM 中文注释:赵元杰
DECLARE
v_ErrorText log_table.message%TYPE; --Variable to hold error message text
BEGIN
/* SQLERRM(0) */
v_ErrorText := SUBSTR(SQLERRM(0), 1, 200);
INSERT INTO log_table (code, message, info)
VALUES (0, v_ErrorText, 'SQLERRM(0)');
/* SQLERRM(100) */
v_ErrorText := SUBSTR(SQLERRM(100), 1, 200);
INSERT INTO log_table (code, message, info)
VALUES (100, v_ErrorText, 'SQLERRM(100)');
/* SQLERRM(10) */
v_ErrorText := SUBSTR(SQLERRM(10), 1, 200);
INSERT INTO log_table (code, message, info)
VALUES (10, v_ErrorText, 'SQLERRM(10)');
/* SQLERRM with no argument */
v_ErrorText := SUBSTR(SQLERRM, 1, 200);
INSERT INTO log_table (code, message, info)
VALUES (NULL, v_ErrorText, 'SQLERRM with noargument');
/* SQLERRM(-1) */
v_ErrorText := SUBSTR(SQLERRM(-1), 1, 200);
INSERT INTO log_table (code, message, info)
VALUES (-1, v_ErrorText, 'SQLERRM(-1)');
/* SQLERRM(-54) */
v_ErrorText := SUBSTR(SQLERRM(-54), 1, 200);
INSERT INTO log_table (code, message, info)
VALUES (-54, v_ErrorText, 'SQLERRM(-54)');
END;
/
提示:虽然在PL/SQL编程中,例外处理不是必须的,但建议编程人员要养成在PL/SQL编程中指定相应的例外(错误处理)。最好针对明显可能出现的错误加以描述。否则,如果程序在运行时出现错误,程序就会被自动终止。而且许多被终止的PL/SQL程序是不容易被用户发现的。
第十六章 存储过程和函数
ORACLE编写的程序一般分为两类,一种是可以完成一定功能的程序叫存储过程;另一种就是在使用时给出一个或多个值,处理完后返回一个或多个结果的程序叫函数。这两种程序都存放在Oracle数据库字典中。下面分别介绍这两种程序的编写方法。
§16.1 引言
ORACLE 提供可以把PL/SQL 程序存储在数据库中,并可以在任何地方来运行它。这样就叫存储过程或函数。在本节中,主要介绍:
1.创建存储过程和函数
2.正确使用系统级的异常处理和用户定义的异常处理
3.建立和管理存储过程和函数
§16.2 存储过程
与其它的数据库系统一样,Oracle的存储过程是用PL/SQL语言编写的能完成一定处理功能的存储在数据库字典中的程序。
§16.2.1 创建过程
建立内嵌过程
在 oracle server 上建立内嵌过程,可以被多个应用程序调用,可以向内嵌过程
传递参数,也可以向内嵌过程传回参数.
创建过程语法:
create [or replace]procedure procedure_name
[ (argment [ { in| in out }] type,
argment [ { in | out | in out } ] type
{ is | as }
<类型.变量的说明>
( 注: 不用 declare 语句 )
Begin
<执行部分>
exception
<可选的异常处理说明>
end;
l 这里的IN表示向存储过程传递参数,OUT表示从存储过程返回参数。而IN OUT 表示传递参数和返回参数;
l 在存储过程内的变量类型只能指定变量类型;不能指定长度;
l 在AS或IS 后声明要用到的变量名称和变量类型及长度;
l 在AS或IS 后声明变量不要加declare 语句。
例1.
--节选自在线代码modetest.sql
REM 作者: Scott Urman.
REM 中文注释:赵元杰
CREATE OR REPLACEPROCEDURE ModeTest (
p_InParameter IN NUMBER,
p_OutParameter OUT NUMBER,
p_InOutParameter IN OUT NUMBER) IS
v_LocalVariable NUMBER;
BEGIN
/* 分配 p_InParameter 给 v_LocalVariable. */
v_LocalVariable := p_InParameter; -- Legal
/* 分配 7 给 p_InParameter. 这是非法的,因为声明是IN */
p_InParameter := 7; -- Illegal
/* 分配 7 给 p_InParameter. 这是合法的,因为声明是OUT */
p_OutParameter := 7; -- Legal
/* 分配 p_OutParameter 给 v_LocalVariable.这是非法的,因为声明是IN */
v_LocalVariable := p_outParameter; -- Illegal
/* 分配 p_InOutParameter 给 v_LocalVariable. 这是合法的,因为声明是IN OUT */
v_LocalVariable := p_InOutParameter; -- Legal
/*分配 7 给 p_InOutParameter. 这是合法的,因为声明是IN OUT */
p_InOutParameter := 7; -- Legal
END ModeTest;
/
§16.2.2 使用过程
存储过程建立完成后,只要通过授权,用户就可以在SQLPLUS、Oracle开发工具或第三方开发工具来调用运行。Oracle 使用EXECUTE 语句来实现对存储过程的调用。
EXEC[UTE] procedure_name( parameter1,parameter2…);
例:
CREATEPACKAGE emp_data AS
TYPEEmpRecTyp IS RECORD (
emp_idNUMBER(4),
emp_nameVARCHAR2(10),
job_titleVARCHAR2(9),
dept_nameVARCHAR2(14),
dept_locVARCHAR2(13));
TYPEEmpCurTyp IS REF CURSOR RETURN EmpRecTyp;
PROCEDUREget_staff (
dept_noIN NUMBER,
emp_cvIN OUT EmpCurTyp);
END;
/
CREATEPACKAGE BODY emp_data AS
PROCEDUREget_staff (
dept_noIN NUMBER,
emp_cvIN OUT EmpCurTyp) IS
BEGIN
OPENemp_cv FOR
SELECTempno, ename, job, dname, loc FROM emp, dept
WHEREemp.deptno = dept_no AND emp.deptno = dept.deptno
ORDERBY empno;
END;
END;
/
COLUMNEMPNO HEADING Number
COLUMNENAME HEADING Name
COLUMNJOB HEADING JobTitle
COLUMNDNAME HEADING Department
COLUMNLOC HEADING Location
SETAUTOPRINT ON
VARIABLEcv REFCURSOR
EXECUTEemp_data.get_staff(20, :cv)
§16.2.3 开发存储过程步骤
目前的几大数据库厂商提供的编写存储过程的工具都没有统一,虽然它们的编写风格有些相似,但由于没有标准,所以各家的开发调试过程也不一样。下面编写PL/SQL存储过程、函数、包及触发器的步骤如下:
§16.2.3.1 编辑存储过程源码
使用文字编辑处理软件编辑存储过程源码,要用类似WORD 文字处理软件进行编辑时,要将源码存为文本格式。
§16.2.3.2 对存储过程程序进行解释
在SQLPLUS或用调试工具将 存储过程程序进行解释;
在SQL>下调试,可用start 或get 等Oracle命令来启动解释。如:
SQL>startc:\stat1.sql
如果使用调试工具,可直接编辑和点击相应的按钮即可生成存储过程。
§16.2.3.3 调试源码直到正确
我们不能保证所写的存储过程达到一次就正确。所以这里的调试是每个程序员必须进行的工作之一。在SQLPLUS下来调试主要用的方法是:
1.使用 SHOW ERROR命令来提示源码的错误位置;
2. 使用 user_errors 数据字典来查看各存储过程的错误位置。
§16.2.3.4 授权执行权给相关的用户或角色
如果调试正确的存储过程没有进行授权,那就只有建立者本人才可以运行。所以作为应用系统的一部分的存储过程也必须进行授权才能达到要求。 在SQLPLUS下可以用GRANT命令来进行存储过程的运行授权。
GRANT语法:
GRANTsystem_privilege | role TO user | role | PUBLIC
[WITHADMIN OPTION]
GRANTobject_privilege | ALL column ON schema.object
FROMuser | role | PUBLIC WITH GRANT OPTION
变量:
system_privilege: 系统权限
role: 角色名
user: 被授权的用户名
object_privilege: 所授予的权限名字,可以是
l ALTER
l DELETE
l EXECUTE
l INDEX
l INSERT
l REFERENCES
l SELECT
l UPDATE
Column: 列名
schema: 模式名
object: 对象名
例子:
GRANTteam_leader TO crystal;
GRANTINSERT, UPDATE ON sales TO larry WITH GRANT
OPTION;
GRANTALL TO PUBLIC;.
§16.2.4 与存储过程相关数据字典
user_source 用户的存储过程、函数的源代码字典
all_source 所有用户的存储过程、函数的源代码字典
user_errors 用户的存储过程、函数的源代码存在错误的信息字典
相关的权限:
create any procedure
drop any procedure
如果某个用户没有权限来创建存储过程,则需要DBA将创建过程的权限授予某用户。如:
sql>grant create any procedure to user1;
§16.3 创建函数
Oracle的函数是一个独有的对象,它也是由PL/SQL语句编写而成,但的不同的地方是:函数必须返回某些值,而存储过程可以不返回任何值。与创建存储过程类似,创建函数的语法如下:
1.建立内嵌函数
create [or replace]function function_name
[ (argment [ { in| in out }] TYPE,
argment [ { in | out | in out } ] type]
return return_type { is | as }
begin
function_body
exception
. . . .. .
end;
例1.
create or replacefunction text_len( t varchar2, l number )
return varchar2 as
tmp varchar2(20);
begin
tmp :=substr( t, 1, l );
return to_char(l)||’ ‘||tmp;
end;
例2. 较为复杂的函数:
REM 选自clasinfo.sql
REM 作者: Scott Urman.
REM 中文注释:赵元杰
CREATE OR REPLACEFUNCTION ClassInfo (
/* 如果教室全满,则返回 'Full'
如果教室超过80%,则返回'Some Room'
如果教室超过60%,则返回'More Room'
如果教室小于60%,则返回'Lots of Room'
如果教室没有学生,则返回'Empty'. */
p_Department classes.department%TYPE,
p_Course classes.course%TYPE)
RETURN VARCHAR2 IS
v_CurrentStudents NUMBER;
v_MaxStudents NUMBER;
v_PercentFull NUMBER;
BEGIN
-- 得到学生的当前和最大值
SELECT current_students, max_students
INTO v_CurrentStudents, v_MaxStudents
FROM classes
WHERE department = p_Department
AND course = p_Course;
-- 计算当前的百分比.
v_PercentFull := v_CurrentStudents /v_MaxStudents * 100;
IF v_PercentFull = 100 THEN
RETURN 'Full';
ELSIF v_PercentFull > 80 THEN
RETURN 'Some Room';
ELSIF v_PercentFull > 60 THEN
RETURN 'More Room';
ELSIF v_PercentFull > 0 THEN
RETURN 'Lots of Room';
ELSE
RETURN 'Empty';
END IF;
END ClassInfo;
/
§16.4 过程和函数中的例外处理
与编写存储过程一样,在编写PL/SQL函数时,也需要对可能出现的各种错误进行描述,以保证函数在运行中出现错误时,程序能按照我们定义的要求进行处理。
§16.4.1 使用系统定义的例外处理
§16.4.1.1 没有例外处理的缺点
如果在编写时没有给出EXCEPTION 的话,一旦出现例外的情况,Oracle就自动终止程序的运行。如果编写的程序没有给出例外处理,则当程序出错时用户无法得到提示,调试者也无法进行修改程序。所以,一般无论多简单的程序最好也要给出例外处理的要求。
§16.4.1.2 使用预定义的例外处理
例。实际例子:
proceduresum_interest_year(cur_procdate in varchar2 ,rtn out number) is
/***********************************************************************/
/* 程序名:sprocess.SQL */
/* 功能: 由PB调用的PL/SQL子过程集,公积金转移金额及利息的计算等.... */
/* 编程语言:PL/SQL for ORACLE 7.3.2,oracle8 v8.x */
/* 运行环境:ORACLE 7.3.X, oracle8i */
/* 修 改 : 赵元杰 */
/* 修改日期:1999 06.25 */
/**********************************************************************/
datestr varchar2(10);
ls_procdate varchar2(10);
ls_year varchar2(10);
begin
/*修改定期主表年终余额、利息、积数、状态位,将年余额
按新的一年加入per_fix_det,status='0';
将per_detail.status='1',per_fix_det.status='1',
per_fix_det and per_detail 的余额转入per_fix_abst,
将年结数据加入年余额表save_bal
修改系统表日期 sssyspar.yearbegin=date_str*/
set transaction use rollback segment hdhouse_rs;
select distinct to_char(procdate,'yyyymmdd') intols_procdate
from vw_tmp_inte;
select to_char(to_number(substr(yearbegin,1,4))+1) intols_year
from sssyspar;
if substr(ls_procdate,1,4)<>ls_year then
rtn:=-1;
return;
elsif substr(ls_procdate,5,4)<>'0630' then
rtn:=-1;
return;
end if;
datestr:=substr(cur_procdate,1,4)||substr(cur_procdate,6,2)||substr(cur_procdate,9,2);
update per_fix_abst set(avail_bal,acc_int,acc_int_fix,interest,
interest_b,crea_int,crea_int_b) =
(selectbal+inteval+inteval_fix,acumbase,acumbase_fix,
inteval_c+inteval_c_fix,
inteval+inteval_fix,0,0
from vw_tmp_inte
whereper_fix_abst.acc_no=vw_tmp_inte.accno
andper_fix_abst.emp_acc_no=vw_tmp_inte.emp_accno
)
where nvl(acc_status,'0')='0';
if sqlcode!=0 then
rtn:=sqlcode;
rollback;
return;
end if;
update per_abst set avail_bal=0,accu_int=0,interest=0
where nvl(acc_status,'0')='0';
if sqlcode!=0 then
rtn:=sqlcode;
rollback;
return;
end if;
update per_detail set status='1' wherenvl(status,'0')='0'
and to_char(crea_date,'yyyy/mm/dd')<=cur_procdate;
if sqlcode!=0 then
rtn:=sqlcode;
rollback;
return;
end if;
update per_fix_det set status='1' wherenvl(status,'0')='0'
andto_char(crea_date,'yyyy/mm/dd')<=cur_procdate;
if sqlcode!=0 then
rtn:=sqlcode;
rollback;
return;
end if;
insert intoper_fix_det(acc_no,emp_acc_no,tran_date,tran_val,
tran_code,balance,db_cr_flag,cash_check,memo,crea_date,
base_int,status,bank_code)
selectacc_no,emp_acc_no,to_date(substr(cur_procdate,1,4)||'/07/01','yyyy/mm/dd'),
avail_bal,'',avail_bal,'1','0','年结
',to_date(substr(cur_procdate,1,4)||'/07/01','yyyy/mm/dd'),
0,'0',bank_code
from per_fix_abst where nvl(acc_status,'0')='0';
if sqlcode!=0 then
rtn:=sqlcode;
rollback;
return;
end if;
insert intosave_bal(acc_no,emp_acc_no,cal_year,balance,interest,
interest_b,accu_int,accu_int_fix,rate_c,rate_c_fix,
rate_b,rate_b_fix)
selectacc_no,emp_acc_no,datestr,avail_bal,interest,interest_b,
acc_int,acc_int_fix,sssyspar.rate_val_c,
sssyspar.rate_fixval_c,sssyspar.rate_val_b,
sssyspar.rate_fixval_b
from per_fix_abst ,sssyspar
where nvl(acc_status,'0')='0';
if sqlcode!=0 then
rtn:=sqlcode;
rollback;
return;
end if;
update sssyspar setyearbegin=substr(cur_procdate,1,4)||'-07-01';
if sqlcode!=0 then
rtn:=sqlcode;
rollback;
return;
end if;
commit;
rtn:=1;
exception
when no_data_found then
rtn:=sqlcode;
rollback;
when others then
rtn:=sqlcode;
rollback;
end;
§16.4.2 使用用户定义的例外处理+
§16.4.2.1 定义的用户例外处理
PL/SQL可以让你定义自己的例外。与预定义不同,用户定义的例外必须声明且必须用RAISE语句来激活(raise)。
声明例外
例外必须在PL/SQL块、子程序或包中进行声明。但不能在一个块里声明两次。但可以在两个块中对同一个例外进行声明。
§16.4.2.2 使用户EXCEPTION_INIT处理
在PL/SQL的程序中,除了所列出的系统错误代码外,实际上还有许多的可能错误。这些内部异常(错误)必须用OTHERS 或 EXCEPTION_INIT 来处理,实际程序(pragma)是一个编译指示器。它是通过一个叫附加说明来传给编译器。Pragma(也叫伪指令)是在编译时内处理,不是在运行时被处理。
DECLARE
deadlock_detected EXCEPTION;
PRAGMAEXCEPTION_INIT(deadlock_detected, -60);
BEGIN
...
EXCEPTION
WHEN deadlock_detected THEN
-- handle the error
END;
§16.4.2.3 使用户raise_application_error处理
例:
CREATEPROCEDURE raise_salary (emp_id NUMBER, amount NUMBER) AS
curr_salNUMBER;
BEGIN
SELECTsal INTO curr_sal FROM emp WHERE empno = emp_id;
IFcurr_sal IS NULL THEN
/* 发布用户定义的错误信息 */
raise_application_error(-20101,’Salary is missing’);
ELSE
UPDATEemp SET sal = curr_sal + amount WHERE empno = emp_id;
ENDIF;
END raise_salary;
第十七章 创建包和使用包
Oracle系统可以把存储过程和函数按照功能的相关性存放在一起,这样的一组对象就叫做包。下面介绍包的建立和使用。
§17.1 引言
把相关的过程和函数归类到一个起并赋予一定的管理功能和使用就叫包。把相关的模块归类成为包可使开发人员利用面向对象的方法进行内嵌过程的开发,从而提高系统性能。
包中包含过程和函数,它们共享公共的变量,公共的局部函数和过程。
一个包由两个分开的部分组成.
包说明(package soecification)
( 定义包所包含的过程,函数,数据类型和变量)
包主体(package body )
( 包中对象的代码 )
包说明和包主体分开编译,并作为两部分分开的对象存放,在数据库字典中,详见
数据字典 user_source,all_source,dba_source.
§17.2 包的定义
要想将相关的一组存储过程和函数存放在一起,必须创建相应的包。
包定义的语法如下:
CREATE [OR REPLACE] PACKAGEpackage_name
[AUTHID {CURRENT_USER |DEFINER}]
{IS | AS}
[PRAGMA SERIALLY_REUSABLE;]
[collection_type_definition...]
[record_type_definition ...]
[subtype_definition ...]
[collection_declaration ...]
[constant_declaration ...]
[exception_declaration ...]
[object_declaration ...]
[record_declaration ...]
[variable_declaration ...]
[cursor_spec ...]
[function_spec ...]
[procedure_spec ...]
[call_spec ...]
[PRAGMA RESTRICT_REFERENCES(assertions) ...]
END [package_name];
[CREATE [OR REPLACE] PACKAGE BODY package_name {IS | AS}
[PRAGMA SERIALLY_REUSABLE;]
[collection_type_definition ...]
[record_type_definition ...]
[subtype_definition ...]
[collection_declaration ...]
[constant_declaration ...]
[exception_declaration ...]
[object_declaration ...]
[record_declaration ...]
[variable_declaration ...]
[cursor_body ...]
[function_spec ...]
[procedure_spec ...]
[call_spec ...]
[BEGIN
sequence_of_statements]
END [package_name];]
§17.3 包的开发步骤
与开发存储过程类似,包的开发需要几个步骤:
1.将每个存储过程调试正确;
2.用文本编辑软件将各个存储过程和函数集成在一起;
3.按照包的定义要求将集成的文本的前面加上包头;
4.按照包的定义要求将集成的文本的前面加上包体;
5.使用SQLPLUS或开发工具进行调试。
§17.4 包的头部说明
包的头部的说明主要包括是将包所要包含的所有的存储过程、函数进行说明。严格来说,就是把形成包的所有存储过程和函数的名字、变量等进行说明,以便在包体部分进行描述。它所用语法是:
CREATE [OR REPLACE] PACKAGE package_name
[AUTHID {CURRENT_USER | DEFINER}]
{IS | AS}
[PRAGMA SERIALLY_REUSABLE;]
[collection_type_definition ...]
[record_type_definition ...]
[subtype_definition ...]
[collection_declaration ...]
[constant_declaration ...]
[exception_declaration ...]
[object_declaration ...]
[record_declaration ...]
[variable_declaration ...]
[cursor_spec ...]
[function_spec ...]
[procedure_spec ...]
[call_spec ...]
[PRAGMA RESTRICT_REFERENCES(assertions) ...]
END [package_name];
§17.5 包体的说明
包体是独立于包头的另外数据库对象,也就是说,在编写整个存储包时,虽然我们将包头和包体写在一个文件(一个程序)并在SQL>下进行解释生成包程序。但是经过Oracle的PL/SQL解释的程序会被分成包的头部、包的体部及存储过程、函数部分。当我们查询数据库字典时,可以看到Oracle数据库是将包头和包体分开的。 包体的创建语法如下:
[CREATE[OR REPLACE] PACKAGE BODY package_name {IS | AS}
[PRAGMASERIALLY_REUSABLE;]
[collection_type_definition...]
[record_type_definition...]
[subtype_definition...]
[collection_declaration...]
[constant_declaration...]
[exception_declaration...]
[object_declaration...]
[record_declaration...]
[variable_declaration...]
[cursor_body...]
[function_spec...]
[procedure_spec...]
[call_spec...]
[BEGIN
sequence_of_statements]
END[package_name];]
例子。创建包的例子:
REM 选自clpack.sql
REM 作者: Scott Urman.
REM 中文注释:赵元杰
CREATE OR REPLACEPACKAGE ClassPackage AS
--加一新的学生到指定的班.
PROCEDURE AddStudent(p_StudentID IN students.id%TYPE,
p_Department INclasses.department%TYPE,
p_Course IN classes.course%TYPE);
-- 指定的班删除某个学生.
PROCEDURE RemoveStudent(p_StudentID IN students.id%TYPE,
p_Department INclasses.department%TYPE,
p_Course IN classes.course%TYPE);
-- 触发删除学生的例外处理.
e_StudentNotRegistered EXCEPTION;
-- 使用表类型来处理学生信息.
TYPE t_StudentIDTable IS TABLE OFstudents.id%TYPE
INDEX BY BINARY_INTEGER;
-- 在指定的班上返回PL/SQL表学生内容
PROCEDURE ClassList(p_Department IN classes.department%TYPE,
p_Course IN classes.course%TYPE,
p_IDs OUT t_StudentIDTable,
p_NumStudents IN OUTBINARY_INTEGER);
END ClassPackage;
/
CREATE OR REPLACEPACKAGE BODY ClassPackage AS
--加一新的学生到指定的班.
PROCEDURE AddStudent(p_StudentID IN students.id%TYPE,
p_Department INclasses.department%TYPE,
p_Course IN classes.course%TYPE) IS
BEGIN
INSERT INTO registered_students(student_id, department, course)
VALUES (p_StudentID, p_Department,p_Course);
COMMIT;
END AddStudent;
-- 从指定班中删除一个学生.
PROCEDURE RemoveStudent(p_StudentID IN students.id%TYPE,
p_Department INclasses.department%TYPE,
p_Course IN classes.course%TYPE) IS
BEGIN
DELETE FROM registered_students
WHERE student_id = p_StudentID
AND department = p_Department
AND course = p_Course;
-- 检查DELETE操作是否成功,如果无匹配的行,则触发错误处理
IF SQL%NOTFOUND THEN
RAISE e_StudentNotRegistered;
END IF;
COMMIT;
END RemoveStudent;
-- 在指定的班上返回PL/SQL表学生内容
PROCEDURE ClassList(p_Department IN classes.department%TYPE,
p_Course IN classes.course%TYPE,
p_IDs OUT t_StudentIDTable,
p_NumStudents IN OUTBINARY_INTEGER) IS
v_StudentID registered_students.student_id%TYPE;
-- 声明取已经注册学生的信息的光标.
CURSOR c_RegisteredStudents IS
SELECT student_id
FROM registered_students
WHERE department = p_Department
AND course = p_Course;
BEGIN
/* p_NumStudents变量为表索引,开始为0;每次循环时增加。
循环结束时,它存放有取到的行数量。并且以p_IDs返回 行数 */
p_NumStudents := 0;
OPEN c_RegisteredStudents;
LOOP
FETCH c_RegisteredStudents INTOv_StudentID;
EXIT WHEN c_RegisteredStudents%NOTFOUND;
p_NumStudents := p_NumStudents + 1;
p_IDs(p_NumStudents) := v_StudentID;
END LOOP;
END ClassList;
END ClassPackage;
/
§17.6 删除过程、函数和包
对那些不再需要的存储过程、函数或包,只要具有DROP ANY PROCEDURE权限,就可以删除它们。
1.删除过程
我们可以 DROP PROCEDURE命令对不需要的过程进行删除,语法如下:
DROP PROCEDURE[user.]procudure_name;
与DROP PROCEDURE 有关的内容请参见AlterProcedure和Create Procedure。
2.删除函数
我们可以 DROP Function 命令对不需要的函数进行删除,语法如下:
DROP FUNCTION[user.]function_name;
与DROP FUNCTION 有关的内容请参见Alter Function和CreateFunction。
3.删除包
我们可以 DROP PACKAGE 命令对不需要的包进行删除,语法如下:
DROP PACKAGE [BOAY] [user.]package_name;
与DROP PACKAGE 有关的内容请参见Alter package和Createpackage。
§17.7 包的管理
当开发已经将包创建在数据库中之后,就开始面临对包的管理的问题。由于包的源代码是存放Oracle的数据字典里,不象在文件系统下直接可以浏览和拷贝等那样方便,所以包的管理对DBA来说更具挑战性。下面是本人在日常工作对包的管理的一点小结。
§17.7.1 包有关的数据字典
与Oracle系统的包有关的数据字典有:
DBA_SOURCE
DBA_ERRORS
1.DBA_SOURCE数据字典:
DBA_SOURCE数据字典存放有整个Oracle系统的所有包、存储过程、函数的源代码。它的列及说明如下:
列名 | 数据类型 | 是否空 | 说明 |
Owner | Varchar2(30) | Not null | 对象的主人 |
Name | Varchar2(30) | Not null | 对象名称 |
Type | Varchar2(12) |
| 对象类型,可以是PROCEDURE,FUNCTION, PACKAGE,TYPE,TYPE BODY ,PACKAGE BODY |
Line | Number | Not null | 行号 |
text | Varchar2(4000) |
| 源代码 |
2.DBA_ERRORS数据字典:
DBA_ERRORS存放所有对象的错误列表。编程人员和DBA可以从中查看错误的对象名及错误内容。它的列说明如下:
列名 | 数据类型 | 是否空 | 说明 |
Owner | Varchar2(30) | Not null | 对象的主人 |
Name | Varchar2(30) | Not null | 对象名称 |
Type | Varchar2(12) |
| 对象类型,可以是PROCEDURE,FUNCTION, PACKAGE,TYPE,TYPE BODY ,PACKAGE BODY |
sequence | number | Not null | 顺序号 |
Line | Number | Not null | 行号 |
position | number | Not null | 错误在行中的位置(列) |
text | Varchar2(4000) |
| 错误代码 |
§17.7.2 包中无效对象的查询和编译
使用过Oracle的PL/SQL编程的人员都有这样的体会,已经存放在数据字典中的存储过程或函数有时会变为无效而不能运行。有经验的DBA会经常去浏览数据库系统内是否存在无效的对象,从而对其进行编译。
1.查询无效的对象例子
例1. 自动产生无效对象的编译命令:
/* 功能:自动产生无效对象的编译命令脚本 */
/* 作者:赵元杰 2001.10 */
/* 使用版本:Oracle 7, oarcle8I */
/* 使用 SPOOL 将显示的信息(即脚本)写到文件 comp_objs.sql中 */
SPOOLc:\comp_objs.sql
SELECT 'SET FEEDBACKON' FROM DUAL;
-- 产生无效对象的提示信息 和 产生Alter 编译命令.
-- 这里的CHR(10)是换行命令
SELECT 'PROMPTCompile PROCEDURE ' || OBJECT_NAME || ' ...' || CHR(10) ||
'ALTER PROCEDURE ' || OBJECT_NAME || 'COMPILE;' || CHR(10)
|| 'SHOW ERRORS;' || CHR(10) ||'PROMPT;'
FROM USER_OBJECTS
WHERE STATUS <> 'VALID'
AND OBJECT_TYPE = 'PROCEDURE'
ORDER BY OBJECT_NAME;
SELECT 'PROMPTCompile FUNCTION ' || OBJECT_NAME || ' ...' || CHR(10) ||
'ALTER FUNCTION ' || OBJECT_NAME || 'COMPILE;' || CHR(10)
|| 'SHOW ERRORS;' || CHR(10) ||'PROMPT;'
FROM USER_OBJECTS
WHERE STATUS <> 'VALID'
AND OBJECT_TYPE = 'FUNCTION'
ORDER BY OBJECT_NAME;
SELECT 'PROMPTCompile PACKAGE ' || OBJECT_NAME || ' ...' || CHR(10) ||
'ALTER PACKAGE ' || OBJECT_NAME || 'COMPILE;' || CHR(10)
|| 'SHOW ERRORS;' || CHR(10) ||'PROMPT;'
FROM USER_OBJECTS
WHERE STATUS <> 'VALID'
AND OBJECT_TYPE = 'PACKAGE'
ORDER BY OBJECT_NAME;
SELECT 'PROMPTCompile PACKAGE BODY ' || OBJECT_NAME || ' ...' || CHR(10) ||
'ALTER PACKAGE ' || OBJECT_NAME || 'COMPILE BODY;'
|| CHR(10) || 'SHOW ERRORS;' || CHR(10)|| 'PROMPT;'
FROM USER_OBJECTS
WHERE STATUS <> 'VALID'
AND OBJECT_TYPE = 'PACKAGE BODY'
ORDER BY OBJECT_NAME;
2.执行编译无效对象的脚本:
用下面命令启动由SPOOL产生的脚本就可以实现自动编译无效包对象。
SQL>startc:\coomp_objs.sql
§17.7.3 包源代码的导出
当我们在进行应用系统的移植、分析、升级等都需要将包的源代码导出。一般情况下,Oracle不提供这样的操作。这里给出一个简单的导出方法供PL/SQL程序人员参考。
例1. 自动产生重新创建所有PL/SQ的命令:
/* 功能:自动产生创建脚本的源代码。 */
/* 作者: 赵元杰 2001.10 */
SELECT DECODE(ROWNUM, 1, 'CREATE OR REPLACE '||
RTRIM(RTRIM(us.text,CHR(10) )),
RTRIM(RTRIM(us.text,CHR(10) ))) text
FROM user_source us
ORDER BY us.name , us.type , us.line;
例2. 存储过程源代码的提取:
/***************************************************************/
/* 文件名:exp_plsql.sql */
/* 功能:导出存储过程的源代码 */
/* 使用方法:在SQL>用start 运行exp_plsql.sql文件,如: */
/* SQL>start c:\exp_plsq.sql */
/* 在提示输入: */
/* 在“导出对象名字:”中输入PL/SQL过程的名字 */
/* 在“导出文件名”中输入路径和导出的文件名 */
/***************************************************************/
accept proc_name char prompt '导出对象名字:'
accept file_name char prompt '导出文件名:'
set pagesize 10000
set verify off
set termout off
set feedback off
col dummy_col new_value max_len noprint
select max(length(text)) dummy_col fromdba_source
where upper(name) = upper('&proc_name');
set linesize &max_len
spool & file_name
SELECT name||' ('||type||')' "Infosabout" from dba_source
WHERE upper(name) = upper('&proc_name')and rownum < 2;
SELECT text "SQL-Code" fromdba_source
WHERE upper(name) = upper('&proc_name');
spool off
set verify on
set termout on
set feedback on
SQL> start c:\exp_plsql.txt
导出对象名字:f81_index_object
导出文件名:c:\exp2
SQL>
第十八章 触发器
触发器是许多关系数据库系统都提供的一项技术。在oracle系统里,触发器类似过程和函数,都有声明,执行和异常处理过程的PL/SQL块。
§18.1 触发器类型
触发器在数据库里以独立的对象存储,它与存储过程不同的是,存储过程通过其它程序来启动运行或直接启动运行,而触发器是由一个事件来启动运行。即触发器是当某个事件发生时自动地隐式运行。并且,触发器不能接收参数。所以运行触发器就叫触发或点火(firing)。在Oracle里,触发器事件指的是对数据库的表进行的INSERT、UPDATE及DELETE操作或对视图进行类似的操作。Oracle目前的版本将触发器的功能扩展到了触发Oracle系统事件,如数据库的启动与关闭等。
§18.1.1DML触发器
Oracle可以在DML语句进行触发,可以在DML操作前或操作后进行触发,并且可以对每个行或语句操作上进行触发。
§18.1.2 替代触发器
由于在Oracle里,不能直接对由两个以上的表建立的视图进行操作。所以给出了替代触发器。它就是Oracle8专门为进行视图操作的一种处理方法。
§18.1.3 系统触发器
Oracle8i 提供了第三种类型的触发器叫系统触发器。它可以在Oracle数据库系统的事件中进行触发,如Oracle系统的启动与关闭等。
§18.2 创建触发器
创建触发器的一般语法是:
CREATE [ ORREPLACE]TRIGGER trigger_name
[ BEFORE|AFTER ]trigger_event ON table_reference
[ FOR EACH ROW [WHEN trigger_condition] ]
trigger_body;
当一个基表被修改(insert,update,delete)时要执行的内嵌过程。执行时根据其所依附的 基表改动而自动触发,因此与应用程序无关,用数据库触发器可以保证数据的一致性和完整性.
每张表最多可建立 12 个触发器,它们是:
before insert
before insert foreach row
after insert
after insert foreach row
before update
before update foreach row
after update
after update foreach row
before delete
before delete foreach row
after delete
after delete for each row
§18.2.1 创建DML触发器
触发器名与过程名和包的名字不一样,它是单独的名字空间,因而触发器名可以和 表 或过程 有相同的名字,但在一个模式中触发器名不能相同。
触发器的限制
触发器有下面一些限制:
。触发器中不能使用控制语句 COMMIT,ROLLBACK, SVAEPOINT 语句;
。由触发器所调用的过程或函数也不能使用控制语句;
。触发器中不能使用LONG,LONG RAW 类型;
。触发器所访问的表受到远表的约束限制,即后面的“变化表”。
问题:当触发器被触发时,要使用被插入,更新或删除的记录中的列值,有时要使用操作前,
后列的值.
实现: :new 修饰符访问操作完成后列的值
:old 修饰符访问操作完成前列的值
例1: 建立一个触发器,当职工表 emp 表被删除一条记录时,把被删除记录写到职工表删除日志表中去.
/**********************************************************************/
/* 功 能 : 当员工表数据被删除时,记录被删除的记录。 */
/* 文件名 : del_emp.sql */
/* 作 者: 赵元杰 2001.5.31 */
/**********************************************************************/
create or replacetrigger scott.del_emp
before delete on scott.emp for each row
begin
-- 将 修改前数据插入到 日志记录 表 del_emp, 以供监督使用。
insert into emp_his( deptno , empno,ename , job ,mgr , sal , comm , hiredate )
values( :old.deptno, :old.empno,:old.ename , :old.job,
:old.mgr, :old.sal, :old.comm,:old.hiredate );
end;
/
show errors
§18.2.2 创建替代(Instead_of)触发器
Instead_of 用于对视图的DML触发,由于视图有可能是由多个表进行联结(join)而成,因而并非是所有的联结都是可更新的。但可以按照所需的方式执行更新,例如下面情况:
--节选自在线代码 instead.sql
CREATE VIEW room_summary AS
SELECT building,sum(number_seats) total_seats
FROM rooms GROUP BY building;
在此视图中直接删除是非法的:
SQL>DELETE FROMrooms_summary WHERE building=’Building 7’;
DELETE FROMrooms_summary WHERE building=’Building 7’;
*
ERROR at line 1:
ORA-01732:data manipulation operation not legal on this view
但是我们可以创建Instead_of 触发器来为 DELETE 操作执行所需的处理,即删除rooms 表中所有基准行:
--节选自在线代码 instead.sql
CREATE TRIGGERroom_summary_delete
INSTEAD OF DELETE ON room_summary
FOR EACH ROW
BEGIN
-- 删除表 room 中行,这些行构成单个视图行。
DELETE FROM rooms WHERE building = :old.building;
END room_summary_delete;
§18.2.3 创建系统触发器
Oracle8i提供的系统触发器可以在DDL或数据库系统上被触发。DDL指的是数据定义语言,如CREATE,ALTER及DROP 等。而数据库系统事件包括数据库服务器的启动或关闭,用户的登录与退出、数据库服务错误等。创建系统触发器的语法如下:
CREATE OR REPLACETRIGGER [sachema.] trigger_name
{BEFORE|AFTER}
{ddl_event_list|database_event_list}
ON { DATABASE |[schema.] SCHEMA }
[ when_clause]trigger_body;
ddl_event_list: 一个或多个DDL事件,事件间用 OR 分开;
database_event_list: 一个或多个数据库事件,事件间用 OR 分开;
下面给出系统触发器的种类和事件出现的时机(前或后):
事件 | 允许的时机 | 说明 |
启动 | 之后 | 实例启动时激活 |
关闭 | 之前 | 实例正常关闭时激活 |
服务器错误 | 之后 | 只要有错误就激活 |
登录 | 之后 | 成功登录后激活 |
注销 | 之前 | 开始注销时激活 |
创建 | 之前,之后 | 在创建之前或之后激活 |
撤消 | 之前,之后 | 在撤消之前或之后激活 |
变更 | 之前,之后 | 在变更之前或之后激活 |
系统触发器可以在数据库级(database)或模式(schema)级进行定义。数据库级触发器在任何事件都激活触发器,而模式触发器只有在指定的模式的触发事件发生时才触发。
例:建立一个当用户USERA登录时,自动记录一些信息的触发器:
CREATE OR REPLACETRIGGER loguserAconnects
AFTER LOGON ON SCHEMA
BEGIN
INSERT INTOexample.temp_table
VALUES(1,’LogUserAConnectsfired!’);
END loguserAconnects;
例:建立一个当用户USERB登录时,自动记录一些信息的触发器:
CREATE OR REPLACETRIGGER loguserAconnects
AFTER LOGON ON SCHEMA
BEGIN
INSERT INTO example.temp_table
VALUES(2,’LogUserAConnectsfired!’);
END loguserBconnects;
例:建立一个当所有用户登录时,自动记录一些信息的触发器:
CREATE OR REPLACETRIGGER logALLconnects
AFTER LOGON ON SCHEMA
BEGIN
INSERT INTOexample.temp_table
VALUES(3,’LogUserAConnectsfired!’);
END logALLconnects;
SQL>connectusera/usera
Connected.
SQL>connectuserb/userb
Connected.
SQL>connectscott/tiger
Connected.
SQL>select * fromtemp_table;
Num_COL CHAR_COL
----------------------------------------------
3 LogALLConnects fired!
2 LoguserBConnects fired!
3 LogALLConnects fired!
3 LogALLConnects fired!
1 LoguserAConnects fired!
§18.2.4 触发器触发次序
Oracle 对事件的触发共有16种,但是它们的触发是有次序的,基本触发次序如下:
1) 执行 BEFORE语句级触发器;
2) 对与受语句影响的每一行:
a) 执行BEFORE语句行级触发器
b) 执行 DML语句
c) 执行 AFTER行级触发器
3)执行AFTER语句级触发器
§18.2.5 使用触发器谓词
ORACLE 提供三个参数 INSERTING,UPDATEING,DELETING用于判断触发了哪些操作。谓词的行为如下:
谓词 | 行为 |
INSERTING | 如果触发语句是 INSERT 语句,则为TRUE,否则为FALSE |
UPDATING | 如果触发语句是 UPDATE语句,则为TRUE,否则为FALSE |
DELETING | 如果触发语句是 DELETE 语句,则为TRUE,否则为FALSE |
例
--节选自在线代码 Rschange.sql
REM 选自:RSchange.sql
REM 作者: Scott Urman.
REM 中文注释:赵元杰
CREATE OR REPLACETRIGGER LogRSChanges
BEFORE INSERT OR DELETE OR UPDATE ONregistered_students
FOR EACH ROW
DECLARE
v_ChangeType CHAR(1);
BEGIN
/* INSERT 用’I’, DELETE用’D’, UPDATE 用’U’ */
IF INSERTING THEN
v_ChangeType := 'I';
ELSIF UPDATING THEN
v_ChangeType := 'U';
ELSE
v_ChangeType := 'D';
END IF;
/* 在RS_audit 记录所有的改变,使用sysdate 来产生系统时间邮戳,
使用 user 返回当前用户的标识 */
INSERT INTO RS_audit
(change_type, changed_by, timestamp,
old_student_id, old_department,old_course, old_grade,
new_student_id, new_department,new_course, new_grade)
VALUES
(v_ChangeType, USER, SYSDATE,
:old.student_id, :old.department,:old.course, :old.grade,
:new.student_id, :new.department,:new.course, :new.grade);
END LogRSChanges;
/
§18.3 删除和使能触发器
当触发器创建完成后,程序员和DBA管理员要经常关心数据库实例中的触发器的情况。对于不必需的触发器,要进行删除或使触发器无效,从而使系统的性能有所提高。
删除触发器的命令语法如下:
DROP TRIGGER trigger_name;
例:从数据子字典中删除某个触发器:
SQL> selecttrigger_name from user_triggers;
TRIGGER_NAME
------------------------------
SET_NLS
SQL> drop triggerset_nls;
触发器已丢弃
使触发器无效的命令是ALTER TRIGGER,它的语法如下:
ALTER TRIGGER triiger_name [DISABLE | ENABLE ];
如:
SQL> ALTERTRIGGER updatemajorstats DISABLE;
SQL> alter table students disable all triggers;
§18.4 创建触发器的限制
编写触发器程序时有些限制,希望程序人员注意下面的一些情况:
1.代码大小:
一般的触发器的代码大小必须小于32K;如果大于这个限制,可以将其拆成几个部分来写。
2.触发器中有效的语句:
可以包括DMLSQL语句,但不能包括DDL 语句。ROLLBACK, COMMIT, and SAVEPOINT也不能使用。但是,对于“系统触发器(system triggers)”可以使用CREATE/ALTER/DROP TABLE和Alter … COMPILE语句。
3. LONG, LONG RAW和LOB的限制:
l 不能插入数据到LONG或LONG RAW;
l 来自LONG或LONG RAW的数据可以转换成字符型(如CHAR和VARCHAR2),但是只允许32KB;
l 使用LONG或LONG RAW不能声明变量;
l 在LONG或LONG RAW列中不能用:NEW 和 :PARENT;
l LOB中的:NEW变量不能修改,例如:
:NEW.Column := ...
4. 引用包变量的限制:
如果UPDATE或DELETE语句测到与当前的UPADTE冲突,则Oracle执行ROLLBACK到SAVEPOINT上并重新启动更新。这样可以要出现多次才能成功。
§18.5 变异表
关于变异表的详细资料见《Oracle8IApplication developer’s Guide》12 Using triggers
变异表( mutating table ) 是被DML语句正在进行UPDATE, DELETE或INSERT的表。对于触发器而言该表是定义触发器的表。
另一种情况是在定义表时使用 DELETECASCADE 参考完整性限制的也是变异表。
对于变异表来说,Oracle对它的访问有限制,见下表:
原始EMP表 |
图: 变异表( mutating 图)
例如:考虑下面例子:
CREATE ORREPLACE TRIGGER Emp_count
AFTER DELETE ONEmp_tab
FOR EACH ROW
DECLARE
n INTEGER;
BEGIN
SELECT COUNT(*)INTO n FROM Emp_tab;
DBMS_OUTPUT.PUT_LINE(’ There are now ’ || n ||
’ employees.’);
END;
当我们输入下面语句时,会出现错误:
DELETE FROM Emp_tab WHERE Empno = 7499;
ORA-04091: table SCOTT.Emp_tab ismutating, trigger/function may not see it
如:
-- 节选自在线代码 tables.sql
CREATE TABLE registered_students (
Student_id number(5) not null,
Department char(3) not null,
Course number(3) not null,
Grande char(1),
CONSTRAINT rs_grande
Check ( grande IN ( ‘A’,’B’,’C’,’E’ )),
CONSTRAINT rs_student_id
FOREIGN KEY ( student_id ) REFERENCES students ( id ),
CONSTRAINT rs_department_course
FOREIGN KEY ( department,course )
REFERENCES classes ( department, course )
);
此表Registered_students 有两个参考完整性限制,students,classes都是Registered_students的限制表。在对Registered_students 执行DML 语句的时侯,Registered_students本身也是变化的。由于这些限制的影响,students,classes也都需被DML 语句改变或查询。
对于这类表,触发器主体中 SQL 语句不允许进行:
。读取或修改触发语句的任何变异表。
。读取或修改触发表的限制表的主键,唯一值列或外键列。
对于上述的限制,可以用行级触发器来进行触发,如果在INSERT语句中只影响一行的话,则触发器不把它当变异表对待。但是对于象Insert into xxx 。。。 select xxx这样的语句。无论其返回是否是多行都当变异表对待。
总之,变异表是那些具有主系关系的表,对于这样的表,在Oracle触发器里进行DML操作会受到限制。
§18.5.1 变异表典型例子
下面给出两个例子来说明触发器对变异表的影响。
例1:可以被执行的触发器,下面触发器 cascadeRSinserts虽对stdents表和classes表进行修改。但是由于该触发器不修改关键字列,所以可以正常被执行:
CREATE OR REPLACETRIGGER cascadeRSinserts
BEFORE INSERT Onregistered_students FOR EACH ROW
DECLARE
V_creditsclasses.num_credits%TYPE;
BEGIN
SELECT num_credits FROMclasses
WHERE department=:new.department
AND course=:new.course;
-- 可以修改stduents表
UPDATE students SETcurrent_credits=current_credits+v_credits
WHERE ID=:new.student_id;
-- 可以修改classes 表,将学生总数加1
UPDATE classes SETcurrent-stduents = current_students + 1
WHERE deptment =:new.department AND course =:new.course;
END cascadeRSinserts;
例2:下面的触发器不能被执行
CREATE OR REPLACETRIGGER limitMajors
BEFORE INSERT OR UPDATEOF major On students FOR EACH ROW
DECLARE
V_maxstudents CONSTANTNUMBER := 5;
V_CurrentStudentsNUMBER;
BEGIN
SELECT COUNT(*) INTO v_CurrentStudents FROM students
WHERE major =:new.major;
IF v_CurrentStudents + 1 > V_maxStudents THEN
RAISE_APPLICATION_ERROR(-20000,’Toomant students in major ‘||:new.major );
END IF;
END limitMajors;
当我们对students 表进行UPDATE时,就触发了上面的触发器,但是它的执行出现错误:
SQL>Update studentsset major=’History’ where id=10003;
UPDATE students
*
ERROR at line 1:
ORA-04091:tableEXAMPLE.STUDENTS is mutating,trigger/function
May not see it
由于触发器limitMajors 查询自己的表就是变异表。所以在执行limitMajors触发器时出现ORA-04091错误。
§18.5.2 变异表错误的处理
在上例中,由于用了行级触发(FOR EACH ROW )而使students变成了变异表,从而导致不能对students 表进行查询。但是可以将上面limitMajors触发器中FOR EACH ROW去掉使它变为语句级触发器。再对触发器中用到的:new.major 值进行修改,变为两个触发器,一个是行级触发器,另一个是语句级触发器。另外还要建立一个包来存放 :new.major值。从而可以被执行。
CREATE OR REPLACEPACKAGE Studentdata AS
TYPE t_Majors IS TABLEOF students.major%TYPE
INDEX BYBINARY_INTEGER;
TYPE t_IDS IS TABLE OFstudents.ID%TYPE
INDEX BYBINARY_INTEGER;
V_studentsmajorst_majors;
V_studentsIDs t_Ids;
V_numEntriesBINARY_INTEGER :=0;
END Studentdata;
CREATE OR REPLACETRIGGER RlimitMajors
BEFORE INSERT OR UPDATEOF major ON students FOR EACH ROW
BEGIN
Studentdata.v_numEntries:= studentsdata.v_numEntries + 1;
Studentdata.v-studentMajors(studentdata.v_numEntries):= :new.major;
Studentdata.v_studentIDs(studentdata.v_numEntries) := :new.id;
END RlimitMajors;
CREATE OR REPLACETRIGGER SlimitMajors
BEFORE INSERT OR UPDATEOF major ON students
BEGIN
v_MaxStudents CONSTANTNUMBER := 5;
v_CurrentStudents NUMBER;
v_studentIDstudents.ID%TYPE;
v_Majorstudent.major%TYPE;
BEGIN
FOR v_loopindex IN 1.. studentdata.V_numEntries loop
v_studentID :=studentdata.v_stidentIDs(v_loopindex);
v_major :=studentdata.v_studentmajors(v_loopindex);
SELECT count(*) INTOv_currentstudents FROM students
WHERE major = v_major;
IF v_currentstudents> v_maxstudents THEN
RAISE_APPLICATION_ERROR(-20000,’Toomany students for major ‘||v_major||
‘because of student ‘||v_studentID);
END IF;
END LOOP;
END SlimitMajors;
§18.6 触发器数据字典与管理
当我们根据应用的特殊要求而不得不编写触发器来完成一定的操作时,触发器的源代码就已经存放在实例的数据字典中,我们了解数据字典的目的主要是管理触发器,比如使触发器有效或无效或删除触发器或导出触发器源代码等。
§18.6.1 触发器数据字典
与触发器有关的数据字典有:
user_triggers
all_triggers
dba_triggers
作为DBA, 只要了解DBA_TRIGGERS就可以了。下面是DBA_TRIGGERS的列说明:
列名 | 数据类型 | 是否空 | 说明 |
Owner | Varchar2(30) | Not null | 触发器的主人 |
Trigger_Name | Varchar2(30) | Not null | 触发器名称 |
Trigger_Type | Varchar2(16) |
| 触发器类型,可以是: Before each row, After each row Before statement, After statement |
Triggering_event | Varchar2(26) | Not null | 语句,可以是 INSERT,UPDATE,DELETE |
Table_owner | Varchar2(30) | Not null | 表的主人 |
Table_name | Varchar2(30) | Not null | 表名 |
Referencing_name | Varchar2(87) |
| 引用名,可以是OLD或NEW |
When_clause | Varchar2(4000) |
| When 条件 |
status | Varchar2(8) |
| 如果是DISABLE,则不触发 |
Description | Varchar2(4000) |
| 触发器描述 |
Trigger_body | long |
| 当触发时要进行的动作 |
§18.6.2 无效触发器的编译
触发器与存储过程一样,也存在无效的情况。DBA要及时发现无效触发器并对它们进行编译。下面是对数据库内无效触发器的编译自动产生命令脚本的方法。
1.产生编译触发器的脚本:
SPOOLc:\comp_trig.sql
SELECT 'SET FEEDBACKON' FROM DUAL;
SELECT 'PROMPTCompile TRIGGER ' || OBJECT_NAME || ' ...' || CHR(10) ||
'ALTER TRIGGER ' || OBJECT_NAME || 'COMPILE;' || CHR(10)
|| 'SHOW ERRORS;' || CHR(10) ||'PROMPT;'
FROM USER_OBJECTS
WHERE STATUS <> 'VALID'
AND OBJECT_TYPE = 'TRIGGER'
ORDER BY OBJECT_NAME;
2.运行脚本comp_trig.sql实现对无效触发器的编译:
STARTc:\comp_trig.sql
第十九章 外部存储过程
我们知道,Oracle的PL/SQL只是一种简单的专用语言,它的特长在于与数据库进行数据处理和交换方面。在要求苛刻的应用里,还得要与其它的语言配合起来用才能完成用户的复杂要求。如科学计算,网上主页的设计等都需要专门的语言来完成。为此,Oracle专门在与其它高级语言和开发工具方面进行一些接口工作。到目前为止,除了可以在Oracle的开发工具中嵌入PL/SQL语言和在C/C++和Java语言上嵌入PL/SQL程序块外,可以在PL/SQL中调用由C或JAVA写的程序。
§19.1 什么是外部例程
外部例程(external procedure)是用C语言或Java语言写成的过程,这些程序可以被PL/SQL来调用。在最新的版本里,Oracle8i 提供了一种叫Java虚拟机的部件,它是作为Orcle8I 的一部分的Oracle JServer 允许我们创建Java过程,并将它们直接存储到数据库中。
外部例程有下面一些特点:
l C外部例程必须放在共享库中。
Extproc进程在调用该库的过程时动态地将库进行加载。在NT平台上的外部例程被编译成一个DLL库。UNIX操作,如Solaris操作系统也把外部例程编译成共享库.SO。
l Java 外部例程要用Java工具或CreateOR Replace Java 语句将外部例程加到数据库中。
l 调用C 外部例程的每个会话都是由一个SQL*NET 监听器生成的extproc进程(NT叫thread)。在MTS下,extproc进程是由共享服务器进程生成的,其生存期与使用该共享服务器的所有会话相同。调用Java外部例程不会生成extproc进程。
l 监听器和extproc进程都必须驻留在数据库所在的主机上。
l 外部例程也可以回调数据库来执行其它的SQL语句或PL/SQL块。C语言的回调是通过Oracle8的OCI或PRO*C来实现的。而Java的回调是通过JDBC或SQLJ来实现。
l 一个给定的会话在处理过程中既可以调用C的外部例程,也可以调用java 外部例程.
l 由于JSP是按照模式对象加载的,所以访问Java的存储子程序的授权要通过数据库来实现。
l Oracle8I 可以无须通过包装而直接调用JSP。该包对于编译来说是必不可少的。
§19.2 C外部例程
Oracle系统在很早的版本就已经支持了C语言和PL/SQL语言的联合开发环境。也就是说可以在C/C++中嵌入PL/SQL块。但到Oracle8后,才允许将C或java写成程序,在建立数据库中,允许PL/SQL程序来调用它们。下面简要给出C语言外部例程的编程介绍。
§19.2.1 调用步骤
为了完成外部例程的调用,必须进行下面步骤:
1)用C语言编制外部例程,并将该程序进行编译在共享库中;
2)配置NET8参数文件和启动监听器进程;
3)创建库数据字典对象来代表操作系统库;
4)通过创建一个把PL/SQL参数影射到C参数的PL/SQL包,用来发布外部例程。
1.建立C 过程编码
比如我们要建立的代码如下:
#include<stdio.h>
voidOutputString(path,message)
char* path;
char * message;
{
FILE * file_handle;
File_handle =fopen(path,”w”);
Fprint(file_handle,”%s\n”,message);
Fclose(file_handle);
}
比如我们将文件存为outstr.c 。接下来就是将代码编译成为一个共享库。比如在Solaris操作系统中,可以用下面命令来完成:
cc –G –o/home/utils/stringlib.so outstr.c
2.配置NET8监听器
配置监听器需要对listener.ora和tnsnames.ora文件进行编辑。下面给出二文件的配置文本:
1) listener.ora文件
EXTPROC_LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION = # Local connections.
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY =icache))
)
)
SID_LIST_EXTPROC_LISTENER=
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = s450 )
(ORACLE_HOME =/home1/ias/app/oracle/product/1.0.2)
(SID_NAME = s450 )
)
(SID_DESC =
(ORACLE_HOME =/home1/ias/app/oracle/product/1.0.2)
(SID_NAME =iCache_extproc)
(PROGRAM =extproc)
)
)
2) tnsnames.ora文件
# TNSNAMES.ORANetwork Configuration File:
#/home/ias/app/oracle/product/1.0.2/network/admin/tnsnames.ora
# Generated byOracle configuration tools.
DBSVR-ICACHE =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY =icache))
)
(CONNECT_DATA =
(SID = icache)
)
)
ORA_ICACHE =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY =icache))
)
(CONNECT_DATA =
(SID = icache)
)
)
ORA_ICACHE_ORIGIN =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST =<originHostName>)(PORT = <originPortNumber>))
)
(CONNECT_DATA =
(SERVICE_NAME =<originServiceName>)
)
)
# Internal mame forlocal Oracle8i Cache.
INST1_HTTP =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST =dbsvr)(PORT = 1521))
)
(CONNECT_DATA =
(SERVER = shared)
(SERVICE_NAME = MODOSE)
(PRESENTATION = http://admin)
)
)
# Support formod_ose over normal TCP connections.
# Oracle8i Cache'sorigin database.
# Support forexternal procedures within Oracle8i cache.
# Descriptivename to connect to cache via OEMperformance monitor.
EXTPROC_CONNECTION_DATA=
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = ipc)(KEY =icache))
)
(CONNECT_DATA =
(SID =iCache_extproc)
(SERVER = DEDICATED)
)
)
S450 =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST =192.168.1.3)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = s450)
)
)
3.创建库
库就是存储在操作系统目录下的库,它必须在Oracle数据字典中登记。由于共享库位于数据库的外部,因此PL/SQL需要数据库使用某些符号来代表该库。现在我们用:
CREATE LIBRARYlibrary_name ( IS | AS )
‘operating_system_path’;
命令来创建库。
其中:library_name是要创建的库名字;
operating_system_path 是共享库在文件系统中的完整路径。
例如:
CREATE LIBRARY StringlibAS ‘/home/utils/stringlib.so’ ;
同样,我们可以用下面命令将库从数据字典中删除:
DROP LIBRARYlibrary_name;
4.发布外部例程
为了使PL/SQL能调用外部例程,必须对其进行发布。发布是通过创建包来实现的。建立包有几个作用:1)首先,把PL/SQL参数隐射为C类型;2)是位置标识来确认调用过程相关参数;3)包负责通知PL/SQL外部库的名称。
包是由 子程序、使用的参数及跟在后面的 AS EXTERAL(Oracle8要求)或ASLANGUAGE(Oracle8I要求)组成。包可以是独立的过程或函数。下面是语法:
Oracle8建立包的语法:
CREATE OR REPLACE {PROCEDURE |FUNCTION } procedure_name [parameter_list]
AS EXTERNAL
LIBRARY library_name
[NAME external_name]
[LANGUAGE C]
[CALLING STANDARD { C |PASCAL} ]
[WITH CONTEXT ]
[PARAMETERS(external-parameter_list) ] ;
Oracle8i建立包的语法:
CREATE OR REPLACE {PROCEDURE |FUNCTION } procedure_name [parameter_list]
AS LANGUAGE C
LIBRARY library_name
[NAME external_name]
[CALLING STANDARD { C |PASCAL} ]
[WITH CONTEXT ]
[PARAMETERS (external-parameter_list)] ;
其中:
LIBRARY 必须的子句。用于说明共享库名。
NAME 用于说明C过程的名称。如果没有NAME子句,则缺省为PL/SQL。名称在数据库里都是以大写字母进行存放,如果C用大小写混合命名,则要双引号括起。
LANGUAGE 与 ASEXTERNAL共同使用。用来说明外部例程使用的语言,如果不使用该语句,缺省为C语言。
CALLING STANDARD 调用标准可以是C或Pascal 。缺省为C语言。
WITH CONTEXT在外部例程回调数据库时使用。该子句将给C过程增加一个参数,以便C过程可以得到必要的OCI句柄。
PARAMETERS 参数子句说明了PL/SQL参数映射到外部例程C语言参数的方式。如果没有说明映射方式,则数据库使用缺省的映射方式。
例:
CREATE OR REPLACEPROCEDURE outputstring(
P_path IN VARCHAR2,
P_Message IN VARCHAR2 ) AS EXTERNAL
LIBRARY StringLib
NAME “outputstring”
PARAMETERS ( p_pathSTRING,
P_Message STRING);
5.使用外部例程
当创建完包后,就可以从PL/SQL块中调用OutputString了。下面例子在/tmp目录下建立一个文件叫 output.txt,该文件内包含一行字符:“HelloWorld!”。
选自在线代码:callOS.sql
SQL>BEGIN
OutputString(‘/tmp/output.txt’,’HelloWorld!’ );
End;
PL/SQL proceduresucessfully completed.
SQL>exit
$cat /tmp/output.txt
Hello World!
§19.2.2 参数映射( 转换)
对于两种不同的语言来说,通信的主要问题之一是数据类型的转换。在外部例程调用来说,要解决包中参数的PL/SQL数据类型如何映射到参数C语言的数据类型的问题。此外还需要注意:
l 在PL/SQL中,任何变量可以是空的(即null),而C语言没有空的概念,必须使用indicator的变量来传递参数是否为空的信息。
l PL/SQL包括了C语言中没有的数据类型,同样,C语言也包括了PL/SQL没有的数据类型。如C语言中没有DATE,BOOLEAN类型,同样PL/SQL对2个、4个字节的整数不加区分。
l PL/SQL接纳不同字符集的字符变量,因此,这些字符集要通知C语言。
l C语言要求从确认从PL/SQL传递过来的字符串当前值或最大长度。
创建过程或函数中的PARAMETERS 就是用来解决参数映射问题的特别参数。
PRAMETERS(external_parameter_list);
external_parameter_list是一个参数表,它可以是多个项,而每一项的结构可以是:
{ parameter_name | SELF| RETURN } property
[BY REFERENCE | VALUE ][ external_datatype ]
其中:
parameter_name 是参数名,
external_datatype 是赋予该参数的C语言数据类型,
property 可以是INDICATOR、LENGTH、MAXLEN、CHARSETID、CHARSETFORM之一。
1.PL/SQL与C语言数据类型比较
由于PL/SQL和C语言没有相同的数据类型,因而Oracle8专门为每一种PL/SQL数据类型提供对应的默认数据类型。我们可以在需要时加载它们。比如外部数据类型为CHAR对应C数据类型是char;外部数据类型UNSIGNED CHAR对应C语言数据类型是Unsigned char等。在编写外部例程时,建议程序人员一定要注意它们之间的差别。
使用PARAMETERS就是在建立过程或函数时带PARAMETERS参数说明。比如前面的例子就是简单使用的样例:
C函数定义为:
#include<stdio.h>
voidOutputString(path,message)
char* path;
char * message;
{
. . . . . .
PL/SQL 过程的PARAMETERS说明为:
CREATE OR REPLACEPROCEDURE outputstring(
P_path IN VARCHAR2,
P_Message IN VARCHAR2 ) AS EXTERNAL
LIBRARY StringLib
NAME “outputstring”
PARAMETERS ( p_path STRING,
P_Message STRING);
第二十章 会话间通信
PL/SQL 提供了两个内装包 DBMS_PIPE 和DBMS_ALTER用于连接到同一台机器的数据库的会话间发送消息。
§20.1 DBMS_PIPE
DBMS_PIPE包实现在oracle 数据库内的“数据库管道(database pipe)”。即连接到相同的oracle 实例上的不同会话可以通过管道发送和接收消息。
管道是以异步(asynchronous)的方式进行工作的,一旦一个消息发出去就不能再检索到该信息了。
管道可以有多个读者(reader-接收消息的人)和一个写者(writer-发送消息的人).
管道的处理顺序是:
1) 写者将一系列数据用DBMS_PIPE.PACK_MESSAGE打包;
2) 写者用DBMS_PIPE.SEND_MESSAGE将打包的数据送到消息缓冲区;
3) 读者用DBMS_PIPE.RECEIVE_MESSAGE将消息接收下来;
4) 读者用DBMS_PIPE.UNPACK_MESSAGE将消息解包。
§20.1.1 发送消息
发送消息需两个步骤:1)数据打包;2)经管道发送出去。
1.打包PACK_MESSAGE
可以对不同类型的数据打包,定义如下:
PROCEDURE PACK_MESSAGE ( item IN VARCHAR2);
PROCEDURE PACK_MESSAGE ( item IN NUMBER);
PROCEDURE PACK_MESSAGE ( item IN DATE);
PROCEDURE PACK_MESSAGE_RAW( item IN RAW);
PROCEDURE PACK_MESSAGE_ROWID ( item IN ROWID);
由于缓冲区大小为4096字节,因而被打包的数据不能超过该大小,否则得到ORA-06558 错误提示。
1. SEND_MESSAGE
当调用一次或多次PACK_MESSAGE过程将本地消息缓冲区填充了数据后,就可用SEND_MESSAGE过程将该缓冲区的内容通过管道发送出去:
FUNCTION SEND_MESSAGE ( pipename IN VARCHAR2,
Timeout IN INTEGER DEFAULT MAXWAIT,
Maxpipesize IN INTEGER DEFAULT 8192)
RETURN INTEGER;
§20.1.2 接收消息
DBMS_PIPE 提供三个过程用于接收管道消息,它们是:RECEIVE_MESSAGE,NEXT_ITEM_TYPE,UNPACK_MESSAGE。
1. RECEIVE_MESSAGE
从管道中接收消息并放在本地缓冲区中,然后再用UNPACK_MESSAGE从该缓冲区检索出消息。RECEIVE_MESSAGE定义为:
FUNCTION RECEIVE_MESSAGE ( pipename IN VARCHAR2,
Timeout IN DEFAULT MAXWAIT )
RETURN INTEGER;
Timeout 等待消息的最大时间(秒),缺省为86400000秒(1000天),如果Timeout=0,则RECEIVE_MESSAGE立即返回。
返回值
0 成功
1 超时,即在等待的时间内还没有消息发过来。
2 在管道中的消息太长。
3 内部错误。
2. NEXT_ITEM_TYPE
NEXT_ITEM_TYPE返回缓冲区中下一项数据的类型,用以确定用哪个类型的变量接收数据。NEXT_ITEM_TYPE定义如下:
FUNCTION NEXT_ITEM_TYPE RETURN INTEGER;
返回值
0 没有其它项
6 NUMBER
9 VARCHAR2
11 ROWID
12 DATE
23 RAW
3. UNPACK_MESSAGE
UNPACK_MESSAGE从该缓冲区检索出消息。
§20.1.3 示例
例1. 发送消息的 PL/SQL 程序
REM 选自logRSins.sql
REM 作者: Scott Urman.
REM 中文注释:赵元杰.
CREATE OR REPLACETRIGGER LogRSInserts
BEFORE INSERT ON registered_students
FOR EACH ROW
DECLARE
v_Status INTEGER;
BEGIN
/* 先 将包存放在缓冲区. */
DBMS_PIPE.PACK_MESSAGE('I');
/* 打包当前用户和邮戳 */
DBMS_PIPE.PACK_MESSAGE(user);
DBMS_PIPE.PACK_MESSAGE(sysdate);
/* 打包新的值. */
DBMS_PIPE.PACK_MESSAGE(:new.student_ID);
DBMS_PIPE.PACK_MESSAGE(:new.department);
DBMS_PIPE.PACK_MESSAGE(:new.course);
DBMS_PIPE.PACK_MESSAGE(:new.grade);
/* 送信息到 'RSInserts' 管道. */
v_Status :=DBMS_PIPE.SEND_MESSAGE('RSInserts');
/* 如果发送不成功,则触发错误处理 */
IF v_Status != 0 THEN
RAISE_APPLICATION_ERROR(-20010,'LogRSInserts trigger ' ||
'couldn''t send the message, status = '|| v_Status);
END IF;
END LogRSInserts;
/
--接收消息的pro*c 程序
/* 选自RSinsert.pc
程序接收RSInserts管道的信息。然后将信息记录到文件中。*/
/* C 和 SQL 头文件 */
#include<stdio.h>
EXEC SQL INCLUDEsqlca;
EXEC SQL BEGINDECLARE SECTION;
/* 连接到数据库的用户名和口令 */
char *v_Connect ="example/example";
/* 声明状态变量 DBMS_PIPE */
int v_Status;
VARCHAR v_Code[5];
/* 送信息到管道的变量. */
VARCHAR v_Userid[9];
VARCHAR v_Changedate[10];
int v_StudentID;
VARCHAR v_Department[4];
int v_Course;
VARCHAR v_Grade[2];
short v_Grade_ind;
EXEC SQL END DECLARESECTION;
/* 日志文件的指针 */
FILE *outfile;
void sqlerror();
int main() {
/* 设置错误处理 */
EXEC SQL WHENEVER SQLERROR DO sqlerror();
/* 连接数据库 */
EXEC SQL CONNECT :v_Connect;
/* 打开日志文件光标. */
outfile = fopen("rs.log","w");
/* 主循环,当遇到错误时停止. */
for (;;) {
/* 休眠直到信息被收到为止,如果没有指定超时的时间,则用缺省值 */
EXEC SQL EXECUTE
BEGIN
:v_Status :=DBMS_PIPE.RECEIVE_MESSAGE('RSInserts');
END;
END-EXEC;
if (v_Status == 0) {
/* 成功收到信息,则先得到第1个数据元素。 */
v_Code.len = 5;
EXEC SQL EXECUTE
BEGIN
DBMS_PIPE.UNPACK_MESSAGE(:v_Code);
END;
END-EXEC;
v_Code.arr[v_Code.len] = '\0';
if (!strcmp(v_Code.arr,"STOP")) {
/* 停止接收,退出循环 */
break;
}
/* 收到其余的信息,包括用户标识、日期及新值 */
v_Userid.len = 9;
v_Changedate.len = 10;
v_Department.len = 4;
v_Grade.len = 2;
EXEC SQL EXECUTE
DECLARE
v_ChangeDate DATE;
BEGIN
DBMS_PIPE.UNPACK_MESSAGE(:v_Userid);
DBMS_PIPE.UNPACK_MESSAGE(v_ChangeDate);
:v_Changedate :=TO_CHAR(v_ChangeDate, 'DD-MON-YY');
DBMS_PIPE.UNPACK_MESSAGE(:v_StudentID);
DBMS_PIPE.UNPACK_MESSAGE(:v_Department);
DBMS_PIPE.UNPACK_MESSAGE(:v_Course);
DBMS_PIPE.UNPACK_MESSAGE(:v_Grade:v_Grade_ind);
END;
END-EXEC;
/* 空字符串 */
v_Userid.arr[v_Userid.len] = '\0';
v_Changedate.arr[v_Changedate.len] ='\0';
v_Department.arr[v_Department.len] ='\0';
if (v_Grade_ind == -1)
v_Grade.arr[0] = '\0';
else
v_Grade.arr[v_Grade.len] = '\0';
/* 打印日志文件日期. */
fprintf(outfile, "User: %sTimestamp: %s",
v_Userid.arr, v_Changedate.arr);
fprintf(outfile, " ID: %d Course: %s%d Grade: %s\n",
v_StudentID, v_Department.arr,v_Course, v_Grade.arr);
}
else if (v_Status == 1) {
/* RECEIVE_MESSAGE 调用超时. 回到循环再等待. */
continue;
}
else {
/* RECEIVE_MESSAGE 调用出错误. 打印和退出 */
printf("RECEIVE_MESSAGE Error! Status = %d\n", v_Status);
EXEC SQL ROLLBACK WORK RELEASE;
exit(1);
}
} /* 主循环结束 */
/* 关闭文件 */
fclose(outfile);
/* 断开数据库连接. */
EXEC SQL COMMIT WORK RELEASE;
}
/* 错误处理函数. 在屏幕显示错误信息和退出 */
void sqlerror() {
printf("SQL Error!\n");
printf("%.*s\n",sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL ROLLBACK RELEASE;
}
例2:
/* Available online as debug.pc */
#include <stido.h>
EXEC SQL INCLUDE sqlca;
EXEC SQL BEGIN DECLARE SECTION;
Char *v_connect = “example/example”
Int v_status;
. . . . . .
REM 选自debug.sql
REM 作者: Scott Urman.
REM 中文注释:赵元杰.
CREATE OR REPLACEPACKAGE Debug AS
-- 等待的最大秒数
v_TimeOut NUMBER := 10;
-- 主Debug 过程.
PROCEDURE Debug(p_Description IN VARCHAR2,p_Value IN
VARCHAR2);
-- 设置Debug 环境
PROCEDURE Reset;
-- 引起后台程序退出.
PROCEDURE Exit;
END Debug;
/
CREATE OR REPLACEPACKAGE BODY Debug as
v_CurrentPipeName VARCHAR2(30);
PROCEDURE Debug(p_Description IN VARCHAR2,p_Value IN
VARCHAR2) IS
v_ReturnCode NUMBER;
v_Handshake VARCHAR2(10);
BEGIN
/* 如果管道还没有,则说明它. */
IF v_CurrentPipeName IS NULL THEN
v_CurrentPipeName :=DBMS_PIPE.UNIQUE_SESSION_NAME;
END IF;
/* 发送'DEBUG' 信息和:
- 握手信息名
- 描述
- 值
*/
DBMS_PIPE.PACK_MESSAGE('DEBUG');
DBMS_PIPE.PACK_MESSAGE(v_CurrentPipeName);
DBMS_PIPE.PACK_MESSAGE(p_Description);
DBMS_PIPE.PACK_MESSAGE(p_Value);
v_ReturnCode :=DBMS_PIPE.SEND_MESSAGE('DebugPipe');
IF v_ReturnCode != 0 THEN
RAISE_APPLICATION_ERROR(-20210,
'Debug.Debug: SEND_MESSAGE failed with '|| v_ReturnCode);
END IF;
/* 等待在管道上返回握手信息 */
v_ReturnCode :=DBMS_PIPE.RECEIVE_MESSAGE(v_CurrentPipeName);
IF v_ReturnCode = 1 THEN
-- 超时
RAISE_APPLICATION_ERROR(-20211,
'Debug.Debug: No handshake messagereceived');
ELSIF v_ReturnCode != 0 THEN
-- 其它错误
RAISE_APPLICATION_ERROR(-20212,
'Debug.Debug: RECEIVE_MESSAGE failedwith ' ||
v_ReturnCode);
ELSE
-- 检查握手信息.
DBMS_PIPE.UNPACK_MESSAGE(v_Handshake);
IF v_Handshake = 'Processed' THEN
-- Output processed.
NULL;
ELSE
-- 非握手信息
RAISE_APPLICATION_ERROR(-20213,
'Debug.Debug: Incorrect handshakemessage received');
END IF;
END IF;
END Debug;
PROCEDURE Reset IS
/* 检查后台程序在运行。否则触发错误 */
v_ReturnCode NUMBER;
BEGIN
DBMS_PIPE.PACK_MESSAGE('TEST');
v_ReturnCode :=DBMS_PIPE.SEND_MESSAGE('DebugPipe');
IF v_ReturnCode != 0 THEN
RAISE_APPLICATION_ERROR(-20200,
'Debug.Reset: SEND_MESSAGE failed with' || v_ReturnCode);
END IF;
/* 后台程序在相同管道上回答,即超时。 因此后台程序未准备好并触发错误 */
v_ReturnCode :=
DBMS_PIPE.RECEIVE_MESSAGE('DebugPipe',v_TimeOut);
IF v_ReturnCode = 1 THEN
-- 超时
RAISE_APPLICATION_ERROR(-20201,
'Debug.Reset: Daemon not ready');
ELSIF v_ReturnCode != 0 THEN
-- 其它错误
RAISE_APPLICATION_ERROR(-20202,
'Debug.Reset: RECEIVE_MESSAGE failedwith ' ||
v_ReturnCode);
ELSE
-- 后台程序准备好.
NULL;
END IF;
END Reset;
PROCEDURE Exit IS
v_ReturnCode NUMBER;
BEGIN
-- 发送 'STOP' 信息.
DBMS_PIPE.PACK_MESSAGE('STOP');
v_ReturnCode :=DBMS_PIPE.SEND_MESSAGE('DebugPipe');
IF v_ReturnCode != 0 THEN
RAISE_APPLICATION_ERROR(-20230,
'Debug.Exit: SEND_MESSAGE failed with '|| v_ReturnCode);
ENDIF;
END Exit;
END Debug;
/
例3.校园网计费系统的实例,当用户从WEB上修该其口令时,通过管道传给C 语言程序,C语言程序完成PROXY,E_MAIL服务器的口令修改等。
( 因为 PL/SQL 编写的程序不支持直接写 NETSCAPE 中目录服务器操作)
1)存储过程如下:
procedure cust_chgpasswd(name1 in varchar2, passwd1 in varchar2,
passwd2 invarchar2,iaction in varchar2) is
result number(2) := 0;
ldap_hostip1 varchar2(30); /* D?B<server host IP */
ldap_port1 varchar(6); /* Directory Port */
ldap_pwd1 varchar2(30); /* such as: 1234567890 */
ldap_str1 varchar2(30); /* o=Airius.com,uid=zxj */
mailhost2 varchar2(50); /* Mail server host IP */
begin
begin
select ldap_hostip, ldap_port,ldap_pwd, ldap_str, mailhost
into
ldap_hostip1,ldap_port1,ldap_pwd1,ldap_str1,mailhost2
from acctsys_ldap where rownum=1;
exception when no_data_found then
ldap_hostip1 :='210.42.96.48';
ldap_port1 := '389';
ldap_pwd1 := '1234567890';
ldap_str1 := 'o=Airius.com';
mailhost2 := 'whuci.edu.cn';
end;
if iaction = '确 认' then
if passwd1=passwd2 then
result :=acctsysd.execute_system('pipe_mod_ldap_user '||
name1 ||' '||passwd1 );
if result = 0 then
htp.p(blink(' 用户 : ' || name1 || ' 密码修改成功!') );
else
htp.p(blink(' 用 户 : ' || name1 || ' 密码修改失败, 口令不变 !') );
end if;
else
htp.p(blink(' 用 户 : ' || name1 || ' 密码修改失败 (口令不变)!') );
end if; /* passwd1=passwd2 */
end if;
acctsys.custom;
end;
. . . . . .
2)由存储过程调用的 包 的源代码如下:
/* 文 件 名: ACCTSYSD.SQL */
/* 作者:赵西建 1998.11. */
/* 修改:赵元杰 1999.12.01 */
CREATE OR REPLACEPACKAGE acctsysd AS
FUNCTION execute_sql(command in VARCHAR2,
timeout in NUMBER DEFAULT 6000)
RETURN NUMBER;
FUNCTION execute_system(command in VARCHAR2,
timeout in NUMBERDEFAULT 6000)
RETURN NUMBER;
PROCEDURE stop(timeout in NUMBER DEFAULT6000);
END;
/
CREATE OR REPLACEPACKAGE BODY acctsysd AS
FUNCTION execute_system(command in VARCHAR2,
timeout in NUMBERDEFAULT 6000)
RETURN NUMBER IS
status NUMBER;
result VARCHAR2(20);
command_code NUMBER;
pipe_name varchar(30);
BEGIN
pipe_name :=DBMS_PIPE.UNIQUE_SESSION_NAME;
DBMS_PIPE.PACK_MESSAGE('SYSTEM');
DBMS_PIPE.PACK_MESSAGE(pipe_name);
DBMS_PIPE.PACK_MESSAGE(command);
/*
insert into tst values(' send command='||command,
to_char(sysdate,'yyyy.mm.ddhh24:mi:ss'));
*/
status:= DBMS_PIPE.SEND_MESSAGE('acctsysd', timeout);
IF status <> 0 THEN
RAISE_APPLICATION_ERROR(-20010,
'执行 EXECUTE SYSTEM 出错! ' ||
to_char(status));
END IF;
status :=DBMS_PIPE.RECEIVE_MESSAGE(pipe_name, timeout);
/*
insert into tst values('reveive pipe_name ='||pipe_name ,
to_char(sysdate,'yyyy.mm.ddhh24:mi:ss'));
*/
IF status <> 0 THEN
RAISE_APPLICATION_ERROR(-20011,
'执行 EXECUTE SYSTEM 出错! ' ||
to_char(status));
END IF;
DBMS_PIPE.UNPACK_MESSAGE(result);
IF result <> 'done' THEN
RAISE_APPLICATION_ERROR(-20012,
'Execute_system:done not received.');
END IF;
DBMS_PIPE.UNPACK_MESSAGE(command_code);
RETURN command_code;
END ; /* execute_system */
/************************************************************************/
FUNCTION execute_sql(command in VARCHAR2,timeout in NUMBER DEFAULT 10)
RETURN NUMBER IS
status NUMBER;
result VARCHAR2(20);
command_code NUMBER;
pipe_name VARCHAR2(30);
BEGIN
pipe_name :=DBMS_PIPE.UNIQUE_SESSION_NAME;
DBMS_PIPE.PACK_MESSAGE('SQL');
DBMS_PIPE.PACK_MESSAGE(pipe_name);
DBMS_PIPE.PACK_MESSAGE(command);
status :=DBMS_PIPE.SEND_MESSAGE('acctsysd', timeout);
IF status <> 0 THEN
RAISE_APPLICATION_ERROR(-20020,
'Execute_sql:Error while sending. Status = ' ||
to_char(status));
END IF;
status :=DBMS_PIPE.RECEIVE_MESSAGE(pipe_name, timeout);
IF status <> 0 THEN
RAISE_APPLICATION_ERROR(-20021,
'Execute_sql: Errorwhile reveiving. Status = ' ||
to_char(status));
END IF;
DBMS_PIPE.UNPACK_MESSAGE(result);
IF result <> 'done' THEN
RAISE_APPLICATION_ERROR(-20022,
'Execute_sql: done not received.');
END IF;
DBMS_PIPE.UNPACK_MESSAGE(command_code);
DBMS_OUTPUT.PUT_LINE('SQLcommand execute. result = ' ||
to_char(command_code));
RETURN command_code;
END; /* execute_sql;*/
PROCEDURE stop(timeout in NUMBER DEFAULT 10)IS
status NUMBER;
BEGIN
DBMS_PIPE.PACK_MESSAGE('STOP');
status :=DBMS_PIPE.SEND_MESSAGE('acctsysd', timeout);
IF status <> 0 THEN
RAISE_APPLICATION_ERROR(-20030,
'Stop:Error while sending. Status = ' ||
to_char(status));
END IF;
END ; /* stop;*/
END;
/
show errors;
/
/***************************************************************************/
/* 文 件 名: ACCTSYSD.PC for LDAP */
/* 功 能:建立多种管道,如:增删用户(email & proxy),修改密码 */
/* 创建文件,执行系统(unix)命令等。 */
/* 编程语言:PRO* C */
/* */
/* 运行环境: ORACLE 7.3.X ( for SUNSolaris 2.5 ) */
/* ORACLE 8.0.X ( for SUN Solaris 2.x ) */
/* */
/* 作 者: 赵西建 */
/* 修 改: 赵元杰 */
/* 修改日期: 2000.01.23 增加add_ldap,chk_ldap,del_ldap */
/* */
/***************************************************************************/
#include<stdio.h>
#include<pwd.h>
#include<shadow.h>
#include <ctype.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/file.h>
#include<sys/stat.h>
#include<sys/types.h>
#include"examples.h"
#include"ldap.h"
#include"lber.h"
#include"usermnt.h"
int setupwd();
int chkpwd();
int chkpwd_direct();
int writeacl();
int writePROXY();
int AppendUser();
char *GetnSalt(char*AsciiPwd);
int chk_ldap_user(char *username, char *passwd );
static voiddo_other_work();
unsigned long global_counter = 0;
static voidfree_mods( LDAPMod **mods );
#define NMODS 12
#definePIPE_EXECUTESYS "pipe_executesys"
#definePIPE_CHK_LDAP_USER "pipe_chk_ldap_user"
#definePIPE_GETLDAPUSER "pipe_get_ldap_user"
#definePIPE_ADDLDAPUSER "pipe_add_ldap_user"
#definePIPE_MODLDAPUSER "pipe_mod_ldap_user"
#definePIPE_DELLDAPUSER "pipe_del_ldap_user"
#defineMAXLINESIZE 2048
#define LINEFMT "%s %s %s %s %s"
#define TEMPFMT "%s %s"
#define GMTVARS internet,ip_no,age,mac_no,arap
#define TMPVARS internet,temp
int get_ldap_user();
intget_ldap_group();
int setupwd_sup();
#define DEBUG_FLAG 1
voidDEBUG_PRINT(char *debugmsg)
{
if (DEBUG_FLAG) printf("提示: %s\n", debugmsg);
}
/* 管道命令参数 */
char internet[10], ip_no[15],age[15],mac_no[15],arap[15],temp[1024];
char pwdfile[128], passwd[32], username[32],cuid[32];
char cgid[32], pw_age[32];
char usercomment[126], homedir[128],shell[64];
int cmdflag,xgid, xuid;
char cmdstr[32];
struct passwd userattr;
char ldap_hostip[30], ldap_port[10],ldap_pwd[30];
char ldap_dn[30], ldap_str[30];
char ldap_user[30], ldap_group[30], ldap_pass[30];
char ldap_flag[4], PwdFile[256];
EXEC SQL INCLUDESQLCA;
EXEC SQL BEGINDECLARE SECTION;
char *uid = "coteam/armszxj";
int status , row_cnt, user_cnt;
VARCHAR command[20];
VARCHAR value[2000];
VARCHAR return_name[30];
VARCHAR ORA_username[30];
VARCHAR ORA_password[32];
VARCHAR proxydir[30];
VARCHAR ORA_uname[30];
VARCHAR ORA_pwd[30];
VARCHAR ORA_aclfile[60];
VARCHAR ORA_run_comm[30];
VARCHAR ORA_router_ip[128];
VARCHAR ORA_acclst[128];
VARCHAR acce_fg[2];
VARCHAR ORA_ip[15],ORA_mac[15],ORA_age[8];
VARCHAR ORA_ldap_hostip[30];
VARCHAR ORA_ldap_port[10];
VARCHAR ORA_ldap_pwd[30];
VARCHAR ORA_ldap_str[30];
VARCHAR ORA_ldap_dn[30];
VARCHAR ORA_ldap_cn[30];
VARCHAR ORA_ldap_uid[30];
VARCHAR ORA_givename[30];
VARCHAR ORA_email[30];
VARCHAR ORA_mailhost[30];
VARCHAR ORA_altmail[30];
VARCHAR ORA_mailquota[10];
VARCHAR ORA_tel_no[30];
VARCHAR ORA_grp_name[30];
VARCHAR ORA_user_chn[30];
VARCHAR ORA_user_clrpwd[30];
varchar user_name[30];
EXEC SQL END DECLARESECTION;
void sql_error()
{
char msg_buffer[512];
int msg_length;
int buffer_size = 512;
EXEC SQL WHENEVER SQLERROR CONTINUE;
sqlglm(msg_buffer, &buffer_size,&msg_length);
printf("执行 ORACLE 语句出错 !\n");
printf("%.*s", msg_length,msg_buffer);
printf("系统继续...\n");
}
main()
{
char msg_buffer[512];
int msg_length;
int buffer_size= 512;
EXEC SQL CONNECT :uid;
if ( sqlca.sqlcode < 0 )
{
printf("连接ORACE出错,确认ORACLE 系统是否 已启动!\n");
sqlglm(msg_buffer, &buffer_size,&msg_length);
printf("ORACLE 出错:%.*s\n", msg_length, msg_buffer);
printf("现退出系统。。。\n");
exit(0);
}
printf("连接 ORACLE 成功 ...\n");
EXEC SQL WHENEVER SQLERROR DO sql_error();
printf("正等待 WEB 端 命令... \n");
EXEC SQL EXECUTE
BEGIN
DBMS_PIPE.RESET_BUFFER;
END;
END-EXEC;
while(1)
{
char PPnewpasswd[32];
char PPoldpasswd[32];
char pathofpwdfile[256];
EXEC SQL EXECUTE
BEGIN
:status := DBMS_PIPE.RECEIVE_MESSAGE('acctsysd');
IF :status = 0 THEN
DBMS_PIPE.UNPACK_MESSAGE(:command);
END IF;
END;
END-EXEC;
if (status != 0)
{
printf("接收命令出错.");
printf(" status: %d\n", status);
}
else
{
command.arr[command.len] = '\0';
/* printf("接收命令command=%s\n",command.arr); */
/*----------------------------------------------------*/
if (!strcmp((char *) command.arr,"STOP"))
{
printf("Acctsysd exiting.\n");
break;
}
/*----------------------------------------------------*/
else
if (!strcmp((char *) command.arr,"SYSTEM"))
{
EXEC SQL EXECUTE
BEGIN
DBMS_PIPE.UNPACK_MESSAGE(:return_name);
DBMS_PIPE.UNPACK_MESSAGE(:value);
END;
END-EXEC;
value.arr[value.len] = '\0';
return_name.arr[return_name.len]='\0';
/*
printf("接收命令return_name=%s\n",return_name.arr);
printf("接收命令value=%s\n",value.arr );
*/
sscanf((char*)value.arr, "%s ", cmdstr);
/*----------------------------------------------------
if(!strcmp((char *)PIPE_EXECUTESYS,cmdstr))
{
sscanf((char *)value.arr, "%s%s",
cmdstr, pwdfile);
}
----------------------------------------------------*/
if(!strcmp((char *)PIPE_GETLDAPUSER,cmdstr))
{
sscanf((char *)value.arr, "%s %s%s",
cmdstr,
ldap_user,
ldap_flag);
DEBUG_PRINT("取 LDAP 用户 ");
status =get_ldap_user(ldap_user,ldap_flag);
}
if(!strcmp((char *)PIPE_ADDLDAPUSER,cmdstr))
{
sscanf((char *)value.arr,"%s %s ", cmdstr, ldap_user);
status = add_ldap_user(ldap_user);
}
if(!strcmp((char *)PIPE_DELLDAPUSER,cmdstr))
{
sscanf((char *)value.arr, "%s%s ", cmdstr, ldap_user);
status = del_ldap_user( ldap_user ) ;
}
if(!strcmp((char *)PIPE_MODLDAPUSER,cmdstr))
{
sscanf((char*)value.arr, "%s %s %s",
cmdstr,
ldap_user,ldap_pass );
status = mod_ldap_user( ldap_user,ldap_pass );
}
/*--------------------------------------------------*/
if(!strcmp((char *)PIPE_CHK_LDAP_USER,cmdstr))
{
sscanf((char*)value.arr, "%s %s %s",
cmdstr,
ldap_user, ldap_pass );
status = chk_ldap_user(ldap_user, ldap_pass );
/*
if ( status == 0 )
printf(" 用户 %s 存在, %d!\n",user_name.arr, user_cnt );
else printf(" 用户 %s 不存在!\n", ldap_user );
*/
}
/*----------------------------------------------------*/
value.arr[0] = '\0';
EXEC SQL EXECUTE
BEGIN
DBMS_PIPE.PACK_MESSAGE('done');
DBMS_PIPE.PACK_MESSAGE(:status);
:status:= DBMS_PIPE.SEND_MESSAGE(:return_name);
END;
END-EXEC;
if (status)
{
printf("Acctsysd Error while respondingto system command.");
printf(" status: %d\n",status);
}
}
/* 系统命令结束 */
else
if (!strcmp((char *)command.arr,"SQL"))
{
EXEC SQL EXECUTE
BEGIN
DBMS_PIPE.UNPACK_MESSAGE(:return_name);
DBMS_PIPE.UNPACK_MESSAGE(:value);
END;
END-EXEC;
value.arr[value.len] = '\0';
printf("将要执行:'%s'\n", value.arr);
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL EXECUTE IMMEDIATE :value;
status = sqlca.sqlcode;
EXEC SQL WHENEVER SQLERROR DOsql_error();
EXEC SQL EXECUTE
BEGIN
DBMS_PIPE.PACK_MESSAGE('done');
DBMS_PIPE.PACK_MESSAGE(:status);
:status:= DBMS_PIPE.SEND_MESSAGE(:return_name);
END;
END-EXEC;
if (status)
{
printf("Acctsysd Error whileresponding to sql command.");
printf(" status: %d\n",status);
}
}
else
{
printf("无效命令: %s\n", command.arr);
}
}
} /* While(1) 结束 */
EXEC SQL COMMIT WORKRELEASE;
exit(0);
}
/* 主程序 main() 结束 */
. . . . . .
§20.2 DBMS_ALERT
DBMS_ALERT 包实现对数据库会话的警报(ALERT),警报是当事务进行提交时发送的一条消息。下表是警告的错误信息:
错误信息 存储过程
ORU-10001lock request error, status: N SIGNAL
ORU-10015error: N waiting for pipe status WAITANY
ORU-10016error: N sending on pipe ’X’ SIGNAL
ORU-10017error: N receiving on pipe ’X’ SIGNAL
ORU-10019error: N on lock request WAIT
ORU-10020error: N on lock request WAITANY
ORU-10021lock request error; status: N REGISTER
ORU-10022lock request error, status: N SIGNAL
ORU-10023lock request error; status N WAITONE
ORU-10024there are no alerts registered WAITANY
ORU-10025lock request error; status N REGISTER
ORU-10037attempting to wait on
uncommittedsignal from same session WAITONE
§20.2.1 使用警告
可以在应用中为各种可能事件进行注册,然后用WAITANY过程等待可能的出现事件。应用也可以使用WAITONE或WAITANY来支持超时选件。
§20.2.2 警告所用的各个过程
1.注册警告的过程REGISTER
可以使用DBMS_ALERT.REGISTER 过程对应用进行注册,对已经注册的应用也可以用REMOVE去掉注册。DBMS_ALERT.REGISTER的语法如下:
DBMS_ALERT.REGISTER (
name IN VARCHAR2);
这里的name是 本次会话的警告名字。
2.删除警告的过程REMOVE
对于不再需要的已经注册的警告,可以用REMOVE去掉。语法如下:
DBMS_ALERT.REMOVE(
nameIN VARCHAR2);
这里的name是 本次会话的警告名字。
3.删除所有警告的过程REMOVEALL
本过程可以注册列表中删除会话中的所有警告。它的语法如下:
DBMS_ALERT.REMOVEALL;
4.设置缺省的过程SET_DEFAULTS
可以在循环下来设置警告,这个过程主要是设置循环检测的间隔。语法如下:
DBMS_ALERT.SET_DEFAULTS(
polling_intervalIN NUMBER);
5.消息过程SIGNAL
警告消息是通告 signal 过程来实现发送。语法如下:
DBMS_ALERT.SIGNAL(
nameIN VARCHAR2,
messageIN VARCHAR2);
6.等待警告过程WAITANY
用WAITONE来等待一个警告的发生,一旦警告已经发出,语法如下:
DBMS_ALERT.WAITANY (
name OUT VARCHAR2,
message OUT VARCHAR2,
status OUT INTEGER,
timeout IN NUMBER DEFAULT MAXWAIT);
其中:
name 警告发生的名字
message 返回警告的信息
status 返回值:
0 – 警告发生
1 – 超时
timeout 等待一个警告的最大时间
错误:-20000, ORU-10024: 没有注册的警告
措施:在等待前必须注册一个警告
§20.2.3 警告例子
例:当你正在按部门统计员工的平均工资时,你的应用需要知道是否有员工的工资在改变。
DBMS_ALERT.REGISTER(’emp_table_alert’);
readagain:
/*... 读emp表并显示结果 */
DBMS_ALERT.WAITONE(’emp_table_alert’,:message, :status);
ifstatus = 0 then goto readagain; else
/*... error condition */
类似,要为emp建立一个触发器:
CREATETRIGGER emptrig AFTER INSERT OR UPDATE OR DELETE ON emp
BEGIN
DBMS_ALERT.SIGNAL(’emp_table_alert’,’message_text’);
END;
当不再需要注册的警告时,可以用下面语句删除:
DBMS_ALERT.REMOVE(’emp_table_alert’);
§20.3 DBMS_PIPE 和 DBMS_ALERT
DBMS_PIPE 和DBMS_ALERT
l 两个包都作为 PL/SQL来实现
l 都连接到同一个实例中来实现会话间的发送消息
l 警告是基于事件的,不是管道的
l 当警告已作过通知后,所有注册的会话都接收到该消息。
l 警告在消息中只能传递单一的字符串,通过管道则可把消息缓冲区的所有内容都发送出去。
第二十一章 数据库作业和文件I/O
除前面介绍的DBMS包外,PL/SQL包还提供 DBMS_JOB, UTL_FILE包。
DBMS_JOB包在运行系统期间定期运行内置过程;而UTL_FILE 可添加对操作系统文件执行读和写的功能。
§21.1 数据库作业
DBMS_JOB包可以在PL/SQL程序中调度,使得它们在特定的时间运行。作业的运行是将它提交给一个队列,同时用参数指定该作业的运行时间。用户可从数据字典中查看作业的信息以及提交的情况。
§21.1.1后台进程
Oracle 实例是由许多在系统中运行的进程组成的。不同的进程负责不同方面的工作,例如 SNP 进程负责数据库的快照及作业队列。
Oracle7 可以有10 个进程,编号为 SNP0 至 SNP9;
Oracle8 可以有36 个进程,编号为 SNP0 至 SNP9,SNPA 至SNPZ。
参数文件 INITsid.ora 有三个参数控制 SNP 进程的行为:
参数 | 缺省值 | 返回值 | 说明 |
JOB_QUEUE_PROCESSES | 0 | 0,10 | 要启动的进程数,0不会执行任何作业 |
JOB_QUEUE_INTERVAL | 60 | 1,3600 | 进程唤醒之间的时间间隔 |
JOB_QUEUE_KEEP_CONNECTIONS | FALSE | TRUE,FALSE | 控制SNP进程是否关闭它的远程数据库连接 |
§21.1.2运行作业
有两种方式运行作业:将作业提交给作业队列;强行立即执行。当作业被提交给作业队列时,在对它进行调度时一个SNP进程将会运行它。如果被指定后,那么该作业稍后就可以自动执行。如果是强行立即执行,则只能运行一次。
1.提交作业SUBMIT
PROCEDURE SUBMIT ( job OUT BINARY_INTEGER,
What IN VARCHAR2,
Next_date IN DATE DEFAULT SYSDATE,
Interval IN VARCHAR2 DEFAULT NULL,
No_parse IN BOOLEAN DEFAULT FALSE ) ;
Job BINARY_INTEGER 在创建时赋予的号码(作业号)
What varchar2 作业的PL/SQL代码
Next_date date 作业的下一次运行的时间
Interval varchar2 计算作业再次运行的时间函数
No_parse boolean, true 作业代码在第一次被执行前不分析。
例
REM Version 1.0,last updated 5/18/97
REM This version ofTempInsert will remove itself from the job queue if
REM the sequencenumber is more than 15, as described in
REM Chapter 18 of_Oracle8 PL/SQL Programming_ by Scott Urman.
PROMPT Creatingprocedure...
CREATE OR REPLACEPROCEDURE TempInsert
(p_NextDate IN OUT DATE) AS
v_SeqNum NUMBER;
v_StartNum NUMBER;
v_SQLCode NUMBER;
v_Errmsg VARCHAR2(60);
BEGIN
SELECT temp_seq.nextval
INTO v_SeqNum
FROM dual;
-- See if this is the first time we're called
BEGIN
SELECT num_col
INTO v_StartNum
FROM temp_table
WHERE char_col = 'TempInsert Start';
-- We've been called before, so insert anew value
INSERT INTO temp_table (num_col, char_col)
VALUES (v_SeqNum, TO_CHAR(SYSDATE,'DD-MON-YY HH24:MI:SS'));
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- First time we're called, so insert
INSERT INTO temp_table (num_col,char_col)
VALUES (v_SeqNum, 'TempInsert Start');
END;
-- If we've been called more than 15 times,exit.
IF v_SeqNum - V_StartNum > 15 THEN
p_NextDate := NULL;
END IF;
COMMIT;
END TempInsert;
/
-- 用下面命令提交作业:
PROMPT Submitting tojob queue...
VARIABLE v_JobNum
BEGIN
DBMS_JOB.SUBMIT(:v_JobNum, 'TempInsert(next_date);', SYSDATE,
'SYSDATE + (5/(24*60*60))');
END;
/
PRINT v_JobNum
提交后,系统就从 sys.jobseq 为该作业指派一个作业号。
要确定每天运行多少次,由SYSDATE + (60/(24*60*60)) 决定,比如要30秒运行一次,则写成SYSDATE + (30/(24*60*60)) ,每2分钟运行一次,则SYSDATE + (2*60/(24*60*60))
一天运行一次,则SYSDATE + (( 24*60*60 )/(24*60*60)) 或SYSDATE +1 .
§21.1.3失效作业
如果作业运行失败,则oracle 会自动运行它,如果第2次作业运行失败,则在1分钟后尝试运行,如果第3次作业运行失败,则在2分钟后尝试运行,如果第4次作业运行失败,则在4分钟后尝试运行,… ,如果作业16 次失败,则该作业被置为失效作业。
你可以用run 命令运行一个失效作业,如果成功则失败计数复位为0。
BROKEN过程可用于改变作业的状态:
PROCEDURE BROKEN ( job IN BINARY_INTEGER,
Broken IN BOOLEAN,
Next_date IN DATE DEFAULT SYSDATE ) ;
如果作业已经损坏,则Oracle不会去运行它。但是可以用DBMS_JOB.RUN程序去运行它。
过程DBMS_JOB.BROKEN语法为:
DBMS_JOB.BROKEN (
job IN BINARY_INTEGER,
broken IN BOOLEAN,
next_date IN DATE DEFAULTSYSDATE);
job 作业号
broken 作业损坏布尔值,
next_data 下次刷新的日期
例:设置作业25为损坏,并设置下次运行的日期为将来的星期一:
EXECDBMS_JOB.BROKEN(25,FALSE,next_day(sysdate,’MONDAY’));
2.运行损坏的作业:
DBMS_JOB.RUN (
job IN BINARY_INTEGER,
force IN BOOLEAN DEFAULTFALSE);
例:直接运行25号作业:
EXECDBMS_JOB.RUN(25);
§21.1.4删除作业
对于用DBMS_JOB.SUBMIT过程提交的作业,可以用DBMS_JOB.REMOVE 过程对其进行取消。要取消某个作业,先要从数据字典user_jobs中查出:
l job作业编号;
l log_user登录的用户;
l what PL/SQL块的名字
注意:
l 用户可以从作业队列中删除作业,但是当前正在运行的操作不会立即被中断,即在完成当前正在进行的操作后才停止下次的运行。
l 用户只能删除属于自己的作业,不能删除其他用户的作业。
例:要查出PL/SQL块名为tmpint 的作业所对应的作业编号,并从队列中删除它:
1) 先登录到scott帐户中:
SQL> connect scott/tiger@ora816
Connected.
2)查询SCOTT用户的作业:
SQL> selectjob,log_user, WHAT from user_jobs;
JOB LOG_USER WHAT
---------------------------------------- --------------------
25 SCOTT TmpInt;
3)从队列中删除属于scott所有的 作业tmpint,其作业号为25 :
SQL> execdbms_job.remove(25);
PL/SQL proceduresuccessfully completed.
§21.1.5修改作业参数
如果对已经提交的作业某些参数不满意,可以通过下面过程实现对作业参数的修改:
1.DBMS_JOB.CHANGE 程序:
DBMS_JOB.CHANGE (
job IN BINARY_INTEGER,
what IN VARCHAR2,
next_date IN DATE,
interval IN VARCHAR2,
instance INBINARY_INTEGER DEFAULT NULL,
force IN BOOLEANDEFAULT FALSE);
job 运行的作业号.
what PL/SQL 程序
next_date 下次刷新的日期
interval 间隔启动的时间函数。
instance 数据库实例名,如果是(缺省)null,则为当前的实例。
Force 如果是FALSE 则要求的实例必须运行。否则返回错误。
注:instance和Force 是Oracle8I才提供的新功能。它们用于并行环境。
例:如要将作业编号为25的作业改为每3天运行一次,则:
EXECDBMS_JOB.CHANGE(25, null, null, ‘SYSDATE+3’ ) ;
2.DBMS_JOB.WHAT 程序:
可以更该作业的定义,语法如下:
DBMS_JOB.WHAT(
jobIN BINARY_INTEGER,
whatIN VARCHAR2);
job 正在运行的作业号
what PL/SQL程序.
下面例子更改作业编号为25 的变为另外的程序:
EXECDBMS_JOB.WHAT(25,’scott.emppackage’);
3.DBMS_JOB.NEXT_DATE 程序:
可以更改作业下次运行的时间,语法如下:
DBMS_JOB.NEXT_DATE(
jobIN BINARY_INTEGER,
next_dateIN DATE);
job 开始运行的作业号.
next_date 下次刷新的日期。
例:比如将作业号为25 的作业改为每天运行一次:
EXECDBMS_JOB.NEXT_DATE(25,’SYSDATE+1’);
4.DBMS_JOB.INTERVAL 程序:
可以更改Oracle运行作业的下次日期。语法如下:
DBMS_JOB.INTERVAL (
job IN BINARY_INTEGER,
interval IN VARCHAR2);
job作业号
interval日期函数,作业运行前求值。如:
sysdate+ 7 表示每星期运行一次
next_day(sysdate,’TUESDAY’) 表示每周二运行一次
null 只运行一次。
例1:如让25号作业永远不运行,则:
EXECDBMS_JOB.NEXT_DATE(26,null);
例2:如让25号作业每星期日运行一次,则:
EXECDBMS_JOB.NEXT_DATE(26, next_day(sysdate,’SUNDAY’) );
§21.1.6与作业参数有关数据字典
dba_jobs,user_jobs 记录作业的信息
dba_jobs_running 描述正在运行的作业
§21.2 文件I/O
在PL/SQL 2.3 及后来的版本,通过 UTL_FILE包可实现对文件进行I/O,但目前还不能对二进制文件进行I/O。ORACLE8 允许使用BFILE读取二进制文件(在后面章节讨论)。
§21.2.1 安全性
客户端通过TEXT_IO包来使创建的文件可以存放在客户端的任何地方。
1.数据库安全性
服务器上是通过叫做“可访问目录(accessible directory)”来指定 UTL_FILE可以写入的目录。即通过在参数文件 INITsid.ora 中的 UTL_FILE_DIR参数定义:
UTL_FILE_DIT =directory_name
UTL_FILE_DIR参数的指定有一些要求,DBA要根据环境来设置此参数。
2.操作系统安全性
ORACLE 用户是该目录的所有者,因此oracle 应具有对该目录的读写权。在该目录下创建的任何文件都归Oracle所有。如果需要其他的操作系统用户也要访问该目录的文件,还要操作系统的授权才行。
§21.2.2 打开和关闭文件
在UTL_FILE中的所有操作都使用文件的句柄来实现。文件句柄就是PL/SQL中用来标识文件的值,它类似于DBMS_SQL中使用的光标。所有的文件句柄都是UTL_FILE.FILE_TYPE类型。文件句柄由OPEN返回,并作为IN(输入)类型参数传递给UTL_FILE中的其它子程序使用。
1.文件打开(FOPEN)
使用FOPEN函数可以打开输入或输出文件。在使用中,所打开的文件一次只 能用于输入或用于输出。不能同时都用于输入、输出。FOPEN函数的语法如下:
FUNCTION FOPEN (location IN VARCHAR2,
Filename IN VARCHAR2,
Open_mode IN VARCHAR2)
RETURN FILE_TYPE;
Location 文件目录路径
Filename 要打开的文件名,如果打开方式为“w”,则现存的文件被覆盖。
Open_mode 文件的打开方式,“r”表示读文本;“w”表示写文本;“a”表示追加。
Return value 返回的值。
2.使用max_linesize参数的文件打开
在Oracle 8.0.5以后的版本,可以用下面函数来打开文件:
FUNCTION FOPEN(location IN VARCHAR2,
Filename IN VARCHAR2,
Open_mode IN VARCHAR2,
Max_linesize IN BINARY_INTEGER
)
RETURN FILE_TYPE;
这里的max_linesize 表示文件的最大行数。可以在1-32767范围内,缺省为1024。
3.关闭文件(FCLOSE)
当文件被打开后,就需要在用完后进行关闭,用下面函数实现对文件的关闭:
PROCEDUREFCLOSE(file_handle IN OUT FILE_TYPE ) ;
这里file_handle是文件句柄。
4.判断文件的打开(IS_OPEN)
文件是否被打开,可以用IS_OPEN函数来实现。如果文件已经打开,则返回TRUE;否则返回FALSE。
FUNCTIONIS_OPEN(file_name IN FILE_TYPE);
RETURN BOOLEAN;
5.使用FCLOSE_ALL关闭所有文件
对于那些想省事的编程人员,可以用FCLOSE_ALL来在程序的后部一次就关闭掉所有被打开的文件。它的语法很简单:
PROCEDURE FCLOSE_ALL;
§21.2.3文件输出
Oracle PL/SQL包提供5个过程来实现将数据输出到文件中。它们是PUT、PUT_LINE、NEW_LINE、PUTE及FFLUSH。下面分别给出说明。
1.PUT过程
PROCEDUREPUT(file_handle IN FILE_TYPE,
buffer IN VARCHAR2);
通过PUT过程可以将字符追加到文件中,如果需要的话,可以使用PUT_LINE或NEW_LINE在行中加入行结束符。
这里的file_handle 是文件的句柄(标识);buffer 是等待写入文件的字符串。如果文件打开没有使用“w”或“a”,则引发错误。
2.NEW_LINE过程
PROCEDURE NEW_LINE(file_handle IN FILE_TYPE,
Lines IN NATURAL:=1);
表示行结束符,它与操作系统有关。这里的lines 是输出的行结束符的个数。缺省为1。即输出一个新行。如果文件打开没有使用“w”或“a”,则引发错误。
3.PUT_LINE过程
PROCEDURE PUT_LINE( file_handle IN FILE_TYPE,
buffer IN VARCHAR2);
通过PUT_LINE过程等价于PUT与NEW_LINE一起的功能。
这里的file_handle 是文件的句柄(标识);buffer 是等待写入文件的字符串。如果文件打开没有使用“w”或“a”,则引发错误。
4.PUTF过程
PROCEDURE PUTF( file_handle IN FILE_TYPE,
Format IN VARCHAR2,
Arg1 IN VARCHAR2 DEFAULT NULL,
Arg2 IN VARCHAR2 DEFAULT NULL,
Arg3 IN VARCHAR2 DEFAULT NULL,
Arg4 IN VARCHAR2 DEFAULT NULL,
Arg5 IN VARCHAR2 DEFAULT NULL
);
这里的file_handle 是文件的句柄(标识);arg1至arg5是可省的变量。Format是带有字符和带有特殊符号“%s”和“\n”的格式说明。
5.FFLUSH过程
前面介绍的PUT、PUT_LINE、NEW_LINE、PUTE输出的数据首先是放在缓冲区里。当缓冲区填满后,缓冲区的内容自动被写入到文件。但我们可以不需要等待缓冲区填满就立即强行写入指定的文件。FFLUSH就是完成这样的过程:
PROCEDURE FFLUSH( file_handle IN FILE_TYPE ) ;
§21.2.3文件输入
文件输入使用的过程只有一个。即GET_LINE过程。此过程可以完成每次从指定的文件中读入一行文本并将文本送到缓冲区。新行的字符不包括在返回字符串中。
PROCEDURE FFLUSH ( file_handle IN FILE_TYPE ,
Buffer OUT VARCHAR2 );
这里的file_handle 是文件的句柄(标识);
buffer 将写入的缓冲区,如果文件没有以读(“r”)的方式打开,则引发UTL_FILE.INVALID_OPERATION异常。
当从文件中读入最后一行时,异常NO_DATA_FOUND就会发生。如果读入空行就会返回空字符NULL。
§21.2.4文件操作例子
下面例子选自原版资料 :
例子.
下面例子写一行文本字符:
Hello, world!
I come from Zork with greetings for all earthlings.
my_world varchar2(4) := ’Zork’;
...
PUTF(my_handle, ’Hello, world!\nI come from %s with %s.\n’,
my_world,
’greetings for all earthlings’);
第二十二章 在PL/SQL 使用SQL语句
在PL/SQL 程序中主要用SQL(StructuredQuery Language ) 进行编程,本章主要讨论在 PL/SQL 中使用 SQL 语句及保证数据一致性的事务控制语句等。
§22.1在PL/SQL 使用DML语句
1. SQL 语句
表 2.1 SQL语句类别
类 别 | SQL 语句 |
数据操纵语句 | SELECT,INSERT,UPDATE,DELETE SET TRANSACTION,EXPLAIN PLAN |
数据定义语句 | DROP ,CREATE,ALTER,GRANT, REVOKE |
事务控制 | COMMIT, ROLLBACK,SAVEPOINT |
会话控制 | ALTER SESSION,SET ROLE |
系统控制 | ALTER SYSTEM |
嵌入式SQL | CONNECT,DETCLARE CURSOR, ALLOCATE |
2. 在PL/SQL 中使用SQL
在 PL/SQL 程序中只能使用 DML 和 事务控制,不能使用 DDL 语句。虽然EXPLAIN PLAN 被归为DML 类,但也不能使用。
3. 在 PL/SQL 中使用 DDL
在PL/SQL V2.1 以后的版本可以采用动态的方法来使用 DDL 语句。
4. 使用 select 语句
在 PL/SQL 中,select 语句中只能处理一行,如果匹配了多行,则提示
ORA-01427 : single –rowquery returns more than one row.
要想返回一条记录,应该采用光标方法。
--节选自在线select.sql
DECLARE
V_studentRecord students%ROWTYPE;
V_department classes.department%TYPE;
V_course classes,course%TYPE;
Begin
-- 返回一条记录
Select * into v_studentRecord
From students where id=10000;
-- 返回两个字段
select department, course
into v_department, v_course
from classes
where room_id=99997;
END;
注:可以在where 后加 where rownum<2 来实现只取一行。此时不回出现上面的提示。
5. 使用INSERT语句
insert 语句可以包括 一个 select 语句,但要求选择列表要与插入列表相匹配。
-- 选自在先代码insert.sql
DECLARE
V_studentID students.id%TYPE;
BEGIN
-- 返回一新的id 号
SELECT student_sequence.NEXTVAL
INTO v_StudentID from dual;
-- 插入一新行到表中
INSERT INTO students(id,first_name,last_name)
VALUES( v_studentsID,’Timothy’, ‘Taller’ );
-- 插入第二行到表中
INSERT INTO students( id, first_name, last_name )
VALUES ( student_sequence.NEXTVAL,’Patrick’, ‘Poll’);
END;
6. 使用UPDATE语句
Update 语句可以包括 一个 select 语句,但要求选择列表要与插入列表相匹配。如果在WHERE后加 CURRENT OF cursor_name 则表示更新当前光标所在的行。
-- 节自在线代码update.sql
DECLARE
V_Mjor students.major%TYPE
V_creditincrease NUMBER :=3 ;
BEGIN
V_Major := ‘History’;
UPDATE students
SET current_credits = current_credits +v_creditincrease
WHERE major =v_Major;
END;
7.使用DELETE语句
如果在WHERE 后加 CURRENT OFcursor_name 则表示删除当前光标所在的行。
-- 节自在线代码 delete.sql
DECLARE
V_studentcutoff NUMBER;
BEGIN
V_studentcutoff := 10;
DELETE FROM students
WHERE current_credits = 0
AND major = ‘Economics’;
END;
§22.2 伪列
1.CURRVAL 和NEXTVAL
可以用sequence.CURRVAL得到当前的序列的值,而用sequence.NEXTVAL得到序列的下一个值。
2.LEVEL
用于对表执行层次树遍历的 SELECT 语句中。详见原版资料《ORACLE SERVER SQL REFERENCE》。
3.ROWID
rowid 返回一个字符串,ORACLE 7 和 ORACLE8 格式有所不同:
ORACLE 7 :
BBBBBBBB.RRRR.FFFF
BBBBBBBB 是文件的块号;RRRR 块中的行号;FFFF是文件的绝对号。
ORACLE8:
OOOOOO.FFF.BBBBBB.RRR
OOOOOO 代表数据对象号;FFF 代表在表空间中的相对文件号;BBBBBB代表在一个文件中的块号;RRR代表块中的位置段(行)号。
4. ROWNUM
使用 rownum 可以限制查询的行数。如:
selectfirst_name,last_name from students where rownum<3;
§22.3 GRANT、REVOKE 和权限
虽然不能直接在 PL/SQL 中使用GRANT和REVOKE语句,但他们对PL/SQL程序的处理会产生影响。因而应在使用PL/SQL来访问对象(如表)前,必须保证这些对象对于操作者来说应是有权访问的。
§22.3.1 对象和系统权限
对象权限 | 说明 | 数据模式,对象 |
ALTER | 被授权者可以对对象使用 alter 语句 | 表,序列 |
DELETE | 允许被授权者使用 DELETE 语句 | 表,视图 |
EXECUTE | 允许被授权者执行内置PL/SQL对象 | 过程,函数,包 |
INDEX | 允许被授权者使用 create index 对表建索引 | 表 |
INSERT | 允许被授权者对对象使用INSERT 语句 | 表,视图 |
REFERENCES | 允许被授权者创建一个表约束 | 表 |
SELECT | 允许被授权者对对象使用SELECT 语句 | 表,视图, 序列,快照 |
UPDATE | 允许被授权者对对象使用UPDATE 语句 | 表,视图 |
§22.3.2 GRANT和REVOKE命令语法
Oracle提供GRANT和REVOKE命令来实现将权限授予用户或从用户中撤消某些权限。
1.GRANT语法如下:
GRANT privilege ON object TO grantee [ WITH GRANT OPTION ];
Privilege 为权限
Object 为对象的名字
Grantee 被授权者的名字
WITH GRANT OPTION 加此关键字,表示该用户可以将对象再授权给另外的用户。
2.REVOKE命令的语法如下:
REVOKE privilege ON object FROM grantee [ CASCADECONSTRAINT ];
Privilege 为权限
Object 为对象的名字
Grantee 被授权者的名字
CASCADE CONSTRAINT表示撤消与当前对象有关的其它权限。
§22.4 事务控制
事务(transaction)是一系列作为一个单元被成功或不成功执行的SQL语句。
例如银行的事务:从一个帐号上汇出款到另一帐号上去(汇入):
UPDATE accounts SET balance = balance – transaction_amount
WHERE account_no= from_acct;
UPDATE accounts SET balance = balance + transaction_amount
WHERE account_no = to_acct;
如果第一条语句成功,而第二条语句失败,则产生款已汇出而对方没有收到的不一致的结果,我们可以将两条语句合并为一个事务来避免这种情况发生。这两条语句要不就一块成功,要不就一块失败(用 commit, rollback )。
§22.4.1COMMIT 和ROLLBACK
当向数据库发出 commit 语句,那该事务就被终结了,并且:
。 事务完成的所有工作永久化;
。其它事务可以看到此事务所作的修改;
。事务所需要执行的所有加锁(lock)处理被释放。
当向数据库发出 rollback 语句,那该事务就被终结了,并且:
。事务完成的所有工作被取消(undo);
。事务所需要执行的所有加锁(lock)处理被释放。
§22.4.2 保留点 SAVEPOINT
rollback 会撤消整个事务,使用 savepoint 可以做到部分撤消事务。
BEGIN
INSERT INTO temp_table ( char_col ) values(‘insertone’);
SAVEPOINT A;
INSERT INTO temp_table ( char_col ) values(‘inserttwo’);
SAVEPOINT B;
INSERT INTO temp_table ( char_col ) values(‘insertthree’);
SAVEPOINT C;
/* 可在此给出rollback to B 之类的语句 */
COMMIT;
END;
§22.5 在PL/SQL中使用SQL函数
ORACLE 系统给出许多内置的函数,这些函数可以在 PL/SQL 中用 SQL 语句来使用。这些内置函数分类如下:
错误报告(error reporting )
数字(number )
字符(character )
数据类型转换(datatypeconversion )
日期(date )
对象引用(object reference )
各种函数(miscellaneous )
表13-4 内置函数
Error | Number | Character | Conversion | Date | Obj Ref | Misc |
|
|
|
|
|
|
|
§22.5.1 错误处理函数
错误处理函数sqlcode 和sqlerrm是Oracle专门为处理内部错误而提供的函数。详细见“错误处理”章节。
§22.5.2 数字函数
数字函数大部分都是中学时所学的数学函数,它们都很简单,这里仅介绍两个不太被注意的函数,即CEIL
及
TRUNC
。
1.CEIL函数:
大于或等于value的最小整数,语法如下:
CEIL(value)
Var1:= CEIL (147.2754);
2. TRUNC
函数:
按照precision 截取value,语法如下:
TRUNC( value, precision )
Int_val := TRUNC(124.16666,2)
结果为 124.16
第二十三章 PL/SQL程序的测试和调试
任何高手程序员都不能保证程序在第一次就正确无误,要确保提交给用户的程序运行达到要求,必须对程序进行彻底的测试。
§23.1 问题诊断
由于错误总是多种多样的,因而调试和测试程序对于程序员来说是一种挑战。
§23.1.1 调试指南
调试要点如下:
1. 找到发生错误的地方
2. 确定发生了什么
3. 将程序简化为简单的测试情形
4. 建立测试环境
§23.1.2 显示在编译产生的错误
通常情况下,由PL/SQL解释所产生的错误除了在SQL>下显示外,还将所产生的错误信息插入到 USER_ERRORS 数据字典中,因此,我们可以用下面命令来检查某个存储过程是否在编译时产生过错误:
SQL>select to_char(line|| ‘/’ || to_char( position )line_pos,
Text FROM USER_ERRORS
WHERE name=’chp13-7’ ;
§23.2 插入测试表
由于oracle pl/sql不能象高级语言那样可以交互式地工作,所以调试最简单的方法是在程序运行时将局部变量的取值插入到一个临时表中,当程序结束时可查询该表以确定变量的值以确定运行时的情况。
§23.3DBMS_OUTPUT的使用
PL/SQL本身没有提供任何输入功能,但 PL/SQL 2.0 以后的版本提供了内装包DBMS_OUTPUT输出功能,在PL/SQL 2.3 还提供UTL_FILE包用于对操作系统文件执行读出和写入操作。
DBMS_OUTPUT包属于sys 帐户,但在创建时已将EXECUTE 权授予 PUBLIC,所以任何用户都可以直接使用而不加sys模式。
2. DBMS_OUTPUT中的过程
DBMS_OUTPUT包中的PUT例程是:PUT, PUT_LINE, NEW_LINE
DBMS_OUTPUT包中的GET例程是:GET_LINE,GET_LINES,ENABLE及
DISABLE.
例
-- 节选自在线代码 output.sql
REM output.sql
REM Version 1.0,last updated 5/15/97
REM This scriptdemonstrates DBMS_OUTPUT.PUT_LINE and DBMS_OUTPUT.GET_LINES,
REM as described inChapter 14 of _Oracle8 PL/SQL Programming_ by Scott Urman.
DECLARE
/* Demonstrates using PUT_LINE and GET_LINE.*/
v_Data DBMS_OUTPUT.CHARARR;
v_NumLines NUMBER;
BEGIN
-- Enable the buffer first.
DBMS_OUTPUT.ENABLE(1000000);
-- Put some data in the buffer first, soGET_LINES will
-- retrieve something.
DBMS_OUTPUT.PUT_LINE('LineOne');
DBMS_OUTPUT.PUT_LINE('Line Two');
DBMS_OUTPUT.PUT_LINE('Line Three');
-- Set the maximum number of lines which wewant to retrieve.
/* v_NumLines := 3;
Get the contents of the buffer back. Notethat v_Data is
declared of type DBMS_OUTPUT.CHARARR, sothat it matches
the declaration of DBMS_OUTPUT.GET_LINES.
DBMS_OUTPUT.GET_LINES(v_Data, v_NumLines);
FOR v_Counter IN 1..v_NumLines LOOP
INSERT INTO temp_table (char_col)
VALUES (v_Data(v_Counter));
END LOOP;*/
END;
/
buffer_size 是内部缓冲区的初始大小,缺省为20,000字节,最大为 1,000,000字节。
2.使用DBMS_OUTPUT
用 SET SERVEROUTPUT ON SIZE buffer_size; 指缓冲区的大小,即:
SET SERVEROUTPUT ON SIZE 1000000;
另外每一行的大小也受到限制,如行的长度为<= 255字节。否则得到:
ORA-20000:ORU-10027:buffer overflow,limit of <buf_limit> bytes.
ORA-20000:ORU-10028:linelength overflow, limit of 255 bytes perline.
§23.4PL/SQL调试器
可以用 ORACLE 公司的Procedure Builder 或Platinum 公司的 SQL_station Debugger 工具进行调试。
§23.4.1 ProcedureBuilder调试器
Procedure Builder是一个PL/SQL开发环境,它是Developer/2000的一部分。使用Procedure Builder,开发者可以在客户端采用设置断点,单步跟踪等对程序进行调试。
§23.4.2 SQL-Station调试器
SQL-Station 与 Procedure Builder调试器类似,也提供一个集成化的PL/SQL开发环境,它带有一个编辑器和调试器,SQL-Station直接提供在服务器端进行调试。
§23.5 程序设计方法
一个优秀的程序员应该在任何环境下(无论机器多快)无论用什么工具都要考虑程序风格和良好的习惯以及效率问题。
§23.5.1 模块化程序设计
模块化程序设计(modular programming ) 就是将复杂的大程序分隔成不同的部分,即模块,每个模块完成一个特定的功能,然后,你可以顺序开发和测试每个模块并将它们组装到最终的程序中。
§23.5.2 自顶向下设计
自顶向下设计(Top-downdesign)和模块化设计正好相反,它首先先开发程序的基本外壳,然后实现细节。
§23.5.3 数据抽象
数据抽象(data abstraction) 是另一种程序设计技巧,即可以对一个特定的算法隐去一些实施细节,而仅仅将接口展现给它。
第二十四章 性能及其它问题
到任何时候,编程人员都应该考虑性能问题,那种认为现在的机器已经很快了,只要考虑程序的正确性就够了的观点是非常错误。为了编写一个正确的并且高性能的程序并不容易。在PL/SQL里,起码要考虑以下一些问题:
§24.1共享池
这里的共享池指的是Oracle的SHARED POOL 。它是系统全局区(SGA)的一部分区域,该区域专门用于存放SQL语句的。设置合适的共享池大小是提高性能的关键。关于初始化参数文件INITsid.ORA的设置详细见《Oracle8i数据库高级管理》--赵元杰著。
§24.1.1 共享池工作原理
一般地,共享池存放的是被解释正确之后的SQL语句,当数据库接到一条SQL语句时首先查看共享池中是否有与该条语句完全一样的语句,如果完全一样,就不用解释就直接执行。如果语句类似,但稍微有点不同(如多了空格等)。Oracle就认为是不同的语句而重新解释。解释正确后就将该语句放在共享池中。那么,共享池到底能放多少语句呢?,这取决于我们将共享池设置成多大的问题。一般当共享池被太多语句填满后,共享池中的对象会按照一种算法找出最近很少用(LRU)的语句。对那些很少用语句进行作废。即最长时间没有被使用的语句被移出共享池。
一方面,Oracle在处理共享池时。新进来的语句如果有足够的共享池来分配的话。系统不会出现问题。但如果共享池中碎片过多,新进来语句有很长的话。就可能出现:
ORA-04031:Unableto allocate x bytes of shared memory
这样的提示表示共享池中没有足够的空间了。
1.冲洗共享池
当我们被提示 ORA-04031错误时。可以使用刷新(Flush)办法来清洗共享池。即将那些目前没有被使用的语句从共享池中清除掉。语法如下:
ALTER SYTEM FLUSH SHARED POOL;
只要具有ALTER SYSTEM权限就可以使用这条语句。
另外,当实例运行失败后,也可以运行对共享池进行清洗。
2.触发器与共享池
由于触发器经过解释后的p-code 不存放在数据字典中,只存放源代码。因而当某个触发器被触发(点火)时,系统是在当时将触发器的源码从字典中调出经过编译后才放在SQL共享池中。如果当这些触发器不再被触发(使用)时就会被移出共享池。因而考虑将共享池编得简短是编程人员要考虑的重要因素。另外可考虑将所执行的处理放在包装的子程序中。然后从共享池来调用该包。这样共享池仅包含对于该包的调用,自然代码也小多了。
§24.1.2 估计共享池大小
共享池大小由初始化INITsid.ORA文件中的 SHARED_POOL_SIZE参数确定。安装ORACLE完成后的缺值一般都太小。Oracle系统不会检测你的物理内存的大小而自动修改该参数的值。所以估计共享池的大小就落到DBA的身上。
关于更详细的内容还得参见有关ORACLE系统调整的资料。下面仅给出一点与PL/SQL程序有关简单的算法:
1.查询内置程序的大小
DBA_OBJECT_SIZE数据字典包含了所有PL/SQL程序对象的大小。可以用下面语句进行查询:
SQL> SELECTname, type, code_size FROM dba_object_size
2 WHERE name IN('DBMS_PIPE','STANDARD','DBMS_OUTPUT')
3 ORDER BY name, type ;
NAME TYPE CODE_SIZE
------------------------------ ------------- ----------------
DBMS_OUTPUT PACKAGE 858
DBMS_OUTPUT PACKAGE BODY 3729
DBMS_OUTPUT SYNONYM 0
DBMS_PIPE PACKAGE 1525
DBMS_PIPE PACKAGE BODY 4483
DBMS_PIPE SYNONYM 0
STANDARD PACKAGE 1773
STANDARD PACKAGE BODY 21982
8 rows selected.
2.查询会话内存的大小
可以从v$process 和v$session字典中查询到特定数据库会话所使用内存的大小,这样的操作分两步:
1)先查询会话的系统标识SID:
SQL> selectsid from v$process p, v$session s
2 where p.addr=s.paddr and s.username='SYSTEM';
SID
----------
12
2)针对特定用户,查询其所使用的内存情况:
SQL> select value from v$sesstat s, v$statname n
2 where s.statistic#=n.statistic#
3* and n.name='session uga memory max' andsid=12;
VALUE
----------
411036
这个结果反映了到目前止的特定会话所使用的内存大小(system用户用了411,036字节)。要想完整地反映系统的内容使用情况,应该在系统运行了一段时间后在运行以上查询才比较准确。这个结果值在乘以会话的数目就可得到所需 共享池的大小了。
§24.1.3 将PL/SQL 驻留在共享池中
当使用PL/SQL对象时,oracle将其存储在SGA区的共享SQL区的库缓存内。如果用户已经把程序加载到内存,那么其它用户在执行这个程序时就感到快多了。因此将程序驻留在内存将减少在执行过程中用户的响应时间。
要想将大的PL/SQL对象驻留在内存中,就应该在数据块刚一开始(系统一启动后)将它们加载到SGA中。使用 DBMS_SHARED_POOL程序包可以将PL/SQL对象驻留在内存中。步骤如下:
对PL/SQL程序包重新编译;
使用DBMS_SHARED_POOL.KEEP 来将程序驻留在内存中。如:
sql>alter package APPOWNER.ADD_CLIENT complie;
sql>execute DBMS_SHARED_POOL.KEEP(‘APPOWNER.ADD_CLIENT’,’P’);
这里’P’为 程序包,如果是’C’ 则表示指示器。
为了通过高速缓存管理标准的Leastrecently used 算法把驻留的对象从SGA中移走,使用DBMS_SHARED_POOL.UNKEEP程序来完成,如:
$sql>execute DBMS_SHARED_POOL.UNKEEP(‘APPOWNER.ADD_CLIENT’);
选择要驻留的程序包
每个数据库有两个程序包:1)数据库核心用的程序包(sys拥有);2)特殊应用程序的专用程序包。
下面给出SYS拥有的程序包:
要驻留的程序包
普通的应用程序 高级复制附加的程序包
DIUTIL DBMS_DEFER
STANDARD DBMS_REPUTIL
DIANA DBMS_SNAPSHOT
DBMS_SYS_SQL DBMS_REFRESH
DBMS_SQL DBMS_DEFER_INTERNAL_SYS
DBMS_UTILITY REP$WHAT_AM_I
DBMS_DESCRIBE
DBMS_JOB
DBMS_STANDARD
DBMS_OUTPUT
PIDL
应首先驻留最大的程序包,为了确定次序,可从DBA_OBJECT_SIZE字典中查出各个程序包的大小:
select owner, name, type
source_size + code_size+ parsed_size + error_size total_bytes
from dba_object_size
where type=’PACKAGE_BODY’ order by total_bytes desc;
§24.2 SQL 语句调整
Oracle为了执行每条SQL语句,首先对该语句确定执行方案(execution plan)。执行方案是Oracle系统实际执行SQL语句的方法,比如确定要访问哪个索引和表,是否需要排序等。
1.确定执行方案
有几种方法可以确定执行方案,这里介绍使用EXPLAINPLAN语句来查询方案的办法。
1)EXPLAIN PLAN
EXPLAIN PLAN SQL语句可以将一条给定的SQL语句确定其执行方案,并将执行方案插入到一个数据库表中。EXPLAIN PLAN语句的格式为:
EXPLAIN PLAN [SET STATEMENT_ID=’statement_info’]
[INTO plan_table] FOR sql-statement;
其中:
sql_statement 是要解释的SQL语句。
Plan_table输出表。
2)建立输出表
在发出EXPLAINPLAN前,需要先建立 PLAN_TABLE表。该表的脚本一般在:
$ORACLE_HOME/rdbms/admin目录下。要建立PLAN_TABLE 表,需要运行该目录的UTLXPLAN.SQL脚本。该表的结构如下:
CREATE TABLE PLAN_TABLE(
STATEMENT_IDVARCHAR2(30),
TIMESTAMP DATE,
REMARKSVARCHAR2(80),
OPERATIONVARCHAR2(30),
OPTIONSVARCHAR2(30),
OBJECT_NODEVARCHAR2(128),
OBJECT_OWNERVARCHAR2(30),
OBJECT_NAMEVARCHAR2(30),
OBJECT_INSTANCENUMERIC,
OBJECT_TYPEVARCHAR2(30),
OPTIMIZERVARCHAR2(255),
SEARCH_COLUMNSNUMBER,
ID NUMERIC,
PARENT_ID NUMERIC,
POSITION NUMERIC,
COST NUMERIC,
CARDINALITYNUMERIC,
BYTES NUMERIC,
OTHER_TAGVARCHAR2(255),
PARTITION_STARTVARCHAR2(255),
PARTITION_STOPVARCHAR2(255),
PARTITION_IDNUMERIC,
OTHER LONG,
DISTRIBUTIONVARCHAR2(30));
3)使用EXPLAINPLAN分析执行方案
比如建立了下面的EMP_COMP表:
CREATE TABLEemp_comp PARTITION BY RANGE(hiredate) SUBPARTITION BY HASH(deptno)
SUBPARTITIONS 3
(
PARTITION emp_p1VALUES LESS THAN (TO_DATE(’1-JAN-1991’,’DD-MON-YYYY’)),
PARTITION emp_p2VALUES LESS THAN (TO_DATE(’1-JAN-1993’,’DD-MON-YYYY’)),
PARTITION emp_p3VALUES LESS THAN (TO_DATE(’1-JAN-1995’,’DD-MON-YYYY’)),
PARTITION emp_p4VALUES LESS THAN (TO_DATE(’1-JAN-1997’,’DD-MON-YYYY’)),
PARTITION emp_p5VALUES LESS THAN (TO_DATE(’1-JAN-1999’,’DD-MON-YYYY’))
)
AS SELECT * FROMemp;
我们可以发出EXPLAIN PLAN命令:
EXPLAIN PLAN FORSELECT * FROM emp_comp;
Plan Table
--------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart | Pstop |
--------------------------------------------------------------------------------
| SELECT STATEMENT | |105 | 8K | 1 | | |
| PARTITION RANGEALL| | | | | 1 | 5 |
| PARTITION HASHALL | | | | | 1 | 3 |
| TABLE ACCESSFULL |EMP_COMP| 105 | 8K | 1 | 1 |15 |
--------------------------------------------------------------------------------
7 rows selected.
我们再发出EXPLAIN PLAN命令:
EXPLAIN PLAN FORSELECT * FROM emp_comp
WHERE hiredate =TO_DATE(’15-FEB-1997’, ’DD-MON-YYYY’);
方案的结果显示如下:
Plan Table
--------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost |Pstart| Pstop |
--------------------------------------------------------------------------------
| SELECT STATEMENT | | 1 | 96 | 1 | | |
| PARTITION HASHALL | | | | | 1 | 3 |
| TABLE ACCESSFULL |EMP_COMP| 1 | 96| 1 | 13 | 15|
--------------------------------------------------------------------------------
6 rows selected.
关于 语句的分析,请参考 《Oracle应用系统优化与性能调整》。
关于 plan_table 表的字段说明请参考 《Oracle8 I Designing and Tuning for Performance》的USING EXPLAIN PLAN 章节。
§24.3 网络问题
当SQL语句被分析并发送给Oracle系统后,该语句的构成就决定了执行方案和最终的性能。SQL语句通常是由用户端进程发给数据库系统中影子进程。即使用户和影子进程是同一台机器上,SQL语句或PL/SQL块也必须在用户和影子进程间传送。所以,就花费大量的时间在网络上进行传送。为了减少网络通信流量,程序设计人员就应考虑应用程序的调整问题(application tuning)。可以有下面的办法进行调整:
l 使用客户端的PL/SQL,避免不必要的重新分析(reparsing);
l 如果是PRO*或OCI编程,可使用数组接口(array interface)。
§24.4 PL/SQL wrap(转换器)
当我们在进行应用系统的源码调试时,可以对过程,函数,包进行修改,也可以从user_source数据字典中读出源码。也可以用其他工具(如 Procedure Builder)以及其它客户端工具来阅读源码,然而,这样的情况有时是不能满足需要的,比如有的算法需要对用户保密等。在 PL/SQL 2.2版后提供了一个 PL/SQL WRAPPER实用程序,该实用程序将 PL/SQL源码进行编码,使之成为客户不能阅读的十六进制格式。但数据库可以对打包的过程进行解码并将它存储在数据库中。
§24.4.1 运行 wrap实用程序
wrapper 是一个操作系统可执行的文件。它的命令是 wrap。命令格式为:
WRAP INAME=input_file[ ONAME=output_name]
这里的input_file 是包含一条 CREATE ORREPLACE 语句的名字。该文件可以是任何扩展名的文件,缺省为.sql 。输出文件的扩展类型为 .plb 。
如:
WRAP INAME=register.sql
WRAP INAME=register
WRAP INAME=register.sql ONAME=register.plb
§24.4.2 输入和输出文件
1. 输入文件
输入文件即明文的源代码文件,缺省类型为.SQL文件,但注释除外。如:
CREATE [ORREPLACE]PROCEDURE
CREATE [ORREPLACE]PACKAGE
CREATE [ORREPLACE]PACKAGE BODY
2. 输出文件
输出文件一般除了保留前面的 CREATEOR REPLACE XXXX WRAPPED 外,其余部分全部转换成十六进制的符号。并且文件类型为.PLB。PLB文件也象原来的.SQL文件可以装载到ORACLE系统中。当经过转换后再装入Oracle中,在DBA_SOURCE(或USER_SOURCE)看到就是经过变换了的源码。此外,这些源码比起原来的.SQL文件来要大得多。
§24.5 DBMS_OUTPUT的使用
PL/SQL本身没有提供任何输入功能,但 PL/SQL 2.0 以后的版本提供了内装包DBMS_OUTPUT输出功能,在PL/SQL 2.3 还提供UTL_FILE包用于对操作系统文件执行读出和写入操作。
DBMS_OUTPUT包属于sys 帐户,但在创建时已将EXECUTE 权授予 PUBLIC,所以任何用户都可以直接使用而不加sys模式。
3. DBMS_OUTPUT中的过程
DBMS_OUTPUT包中的PUT例程是:PUT, PUT_LINE, NEW_LINE
DBMS_OUTPUT包中的GET例程是:GET_LINE,GET_LINES,ENABLE及DISABLE.
例
-- 节选自在线代码 output.sql
REM output.sql
REM Version 1.0,last updated 5/15/97
REM This scriptdemonstrates DBMS_OUTPUT.PUT_LINE and DBMS_OUTPUT.GET_LINES,
REM as described inChapter 14 of _Oracle8 PL/SQL Programming_ by Scott Urman.
DECLARE
/* Demonstrates using PUT_LINE and GET_LINE.*/
v_Data DBMS_OUTPUT.CHARARR;
v_NumLines NUMBER;
BEGIN
-- Enable the buffer first.
DBMS_OUTPUT.ENABLE(1000000);
-- Put some data in the buffer first, soGET_LINES will
-- retrieve something.
DBMS_OUTPUT.PUT_LINE('Line One');
DBMS_OUTPUT.PUT_LINE('Line Two');
DBMS_OUTPUT.PUT_LINE('Line Three');
-- Set the maximum number of lines which wewant to retrieve.
v_NumLines := 3;
/* Get the contents of the buffer back. Notethat v_Data is
declared of typeDBMS_OUTPUT.CHARARR, so that it matches
the declaration of DBMS_OUTPUT.GET_LINES.*/
DBMS_OUTPUT.GET_LINES(v_Data, v_NumLines);
/* Loop through the returned buffer, andinsert the contents
into temp_table. */
FOR v_Counter IN 1..v_NumLines LOOP
INSERT INTO temp_table (char_col)
VALUES (v_Data(v_Counter));
END LOOP;
END;
/
buffer_size 是内部缓冲区的初始大小,缺省为20,000字节,最大为 1,000,000字节。
2.使用DBMS_OUTPUT
用 SET SERVEROUTPUT ON SIZE buffer_size; 指缓冲区的大小,另外每一行的大小也受到限制,如行的长度为<= 255字节。否则得到:
ORA-20000:ORU-10027:buffer overflow,limit of <buf_limit> bytes.
ORA-20000:ORU-10028:linelength overflow, limit of 255 bytes perline.
第二十五章 对象类型
对象是Oracle首先提出的概念,到Oracle8I 后进行一步得到了完善。对象类型是Oracle用于描述实体的一项技术。它给用户一种更灵活的方法,它可以定义自己需要的类型。
§25.1 对象类型定义
对象类型描述了与特殊类型种类的对象关联的属性和方法。对象类型与定义包有些相似。都有头说明和主体的说明。下面给出创建类型说明语法。
§25.1.1 定义对象类型头
定义对象类型头的语法如下:
CREATE[OR REPLACE] TYPE type_name
[AUTHID{CURRENT_USER | DEFINER}] {IS | AS} OBJECT (
attribute_namedatatype[, attribute_name datatype]...
[{MAP| ORDER} MEMBER function_spec,]
[{MEMBER| STATIC} {subprogram_spec | call_spec}
[,{MEMBER | STATIC} {subprogram_spec | call_spec}]...]
);
§25.1.2 定义对象类型体
定义对象类型体的语法如下:
[CREATE[OR REPLACE] TYPE BODY type_name {IS | AS}
{ {MAP | ORDER} MEMBER function_body;
| {MEMBER | STATIC} {subprogram_body |call_spec};}
[{MEMBER | STATIC} {subprogram_body |call_spec};]...
END;]
§25.1.3 定义对象类型例子
例:建立对象类型rational:
CREATE TYPE Rational AS OBJECT (
num INTEGER,
den INTEGER,
MAP MEMBER FUNCTION convert RETURN REAL,
MEMBER PROCEDURE normalize,
MEMBER FUNCTION reciprocal RETURN Rational,
MEMBER FUNCTION plus (x Rational) RETURNRational,
MEMBER FUNCTION less (x Rational) RETURNRational,
MEMBER FUNCTION times (x Rational) RETURNRational,
MEMBER FUNCTION divby (x Rational) RETURNRational,
PRAGMA RESTRICT_REFERENCES (DEFAULT,RNDS,WNDS,RNPS,WNPS)
);
下面开始建立对象类型的主体:
CREATE TYPE BODY Rational AS
MAP MEMBER FUNCTION convert RETURN REAL IS
-- 转换有理数到实数
BEGIN
RETURN num / den;
END convert;
MEMBER PROCEDURE normalize IS
g INTEGER;
BEGIN
g := gcd(num, den);
num := num / g;
den := den / g;
END normalize;
MEMBER FUNCTION reciprocal RETURN Rational IS
BEGIN
RETURN Rational(den, num); -- 调用构造器
END reciprocal;
MEMBER FUNCTION plus (x Rational) RETURNRational IS
-- 返回 SELF + x 的总和
r Rational;
BEGIN
r := Rational(num * x.den + x.num * den, den *x.den);
r.normalize;
RETURN r;
END plus;
MEMBER FUNCTION less (x Rational) RETURNRational IS
-- 返回 SELF – x 的差
r Rational;
BEGIN
r := Rational(num * x.den - x.num * den, den *x.den);
r.normalize;
RETURN r;
END less;
MEMBER FUNCTION times (x Rational) RETURNRational IS
-- 返回 SELF * x 的乘积
r Rational;
BEGIN
r := Rational(num * x.num, den * x.den);
r.normalize;
RETURN r;
END times;
MEMBER FUNCTION divby (x Rational) RETURNRational IS
--返回 SELF / x 的商
r Rational;
BEGIN
r := Rational(num * x.den, den * x.num);
r.normalize;
RETURN r;
END divby;
END;
§25.1.4 初始化对象类型
如果没有给对象定义进行初始化,它被自动置成null。所以在定义完成后,需要用constructor函数来进行初始化。
DECLARE
r Rational; -- r becomes atomically null
BEGIN
r := Rational(2,3); -- r becomes 2/3
§25.1.5 使用对象类型
建立下面对象,然后开始使用建立过的对象:
CREATETYPE Person AS OBJECT (
first_nameVARCHAR2(15),
last_nameVARCHAR2(15),
birthdayDATE,
home_addressAddress,
phone_numberVARCHAR2(15))
/
CREATETABLE persons OF Person
/
BEGIN
INSERTINTO employees -- another object table of type Person
SELECT* FROM persons p WHERE p.last_name LIKE ’%Smith’;
1.Select :
BEGIN
INSERTINTO employees
SELECTVALUE(p) FROM persons p
WHEREp.last_name LIKE ’%Smith’;
可以用value 来 返回 persoon 对象:
DECLARE
p1Person;
p2Person;
...
BEGIN
SELECTVALUE(p) INTO p1 FROM persons p
WHEREp.last_name = ’Kroll’;
p2:= p1;
...
END;
2.INSERT:
BEGIN
INSERTINTO persons
VALUES(Person(’Albert’, ’Brooker’,...));
DECLARE
p1_refREF Person;
p2_refREF Person;
...
BEGIN
INSERTINTO persons p
VALUES(Person(’Paul’, ’Chang’,...))
RETURNINGREF(p) INTO p1_ref;
INSERTINTO persons p
VALUES(Person(’Ana’, ’Thorne’,...))
RETURNINGREF(p) INTO p2_ref;
INSERTINTO department
VALUES(’Payroll’, Person(’Alan’, ’Tsai’,...), ’Los Angeles’)
/
3.UPDATE:
BEGIN
UPDATEpersons p SET p.home_address = ’341 OakdeneAve’
WHEREp.last_name = ’Brody’;
...
UPDATEpersons p SET p = Person(’Beth’, ’Steinberg’,...)
WHEREp.last_name = ’Steinway’;
...
END;
4.Delete
BEGIN
DELETEFROM persons p
WHEREp.home_address = ’108 Palm Dr’;
...
END;
§25.2 对象类型修改
可以用 ALTER TYPE 命令对已建立的对象进行重新编译:
ALTERTYPE 语法:
ALTERTYPE schema.type_name
{COMPILE { SPECIFICATION | BODY } |
REPLACEAS | AS TABLE | AS OBJECT }
{VARRAY (size) | VARYING ARRAY (size) }
{OF datatype}
{REF object_type_name}
{MAP | ORDER MEMBER function_specification }
{PRAGMA RESTRICT_REFERENCES
function_specificationrestriction}
变量:
schema: 建立类型的数据库用户名
type_name: 希望改变的对象名
size: VARRAY 大小的上限
datatype: 任意类型,如 CHAR, DATE, NUMBER等
object_type_name: 对象类型引用的名字
function_specification:成员函数名
restriction: 限制类型,如 WNDS, WNPS, RNDS, RNPS
例子:
ALTERTYPE loan_obj AS OBJECT
(customer_nameCHAR(20),
loan_amountNUMBER(5),
MEMBERFUNCTION get_amount RETURN NUMBER,
MEMBERFUNCTION get_loan_approved RETURN NUMBER,
pragma RESTRICT_REFERENCES (get_amount, WNDS));
§25.3 对象类型删除
DROP TYPE语法:
DROPTYPE [schema.] type_name [FORCE]
例子:
DROPTYPE customer_obj;
DROPTYPE loan_obj FORCE;
DROP TYPE BODY语法:
DROPTYPE BODY [schema.] type_name
例子:
DROPTYPE BODY customer_obj;
DROPTYPE BODY loan_obj;
第二十六章 动态PL/SQL简介
在前面介绍的内容中都不能在PL/SQL 程序使用DDL 语句,这章介绍通过DBMS_SQL 包可以动态地使用DDL语句。
§26.1 概述
一般在PL/SQL程序设计中,可以直接使用的SQL语句是DML语句和事务控制语句。另外的DDL语句、会话和系统控制语句不能在PL/SQL中直接使用。在这里,我们通过DBMS_SQL包可以(在 PL/SQL 2.1 或更高的版本上)使用DDL语句。实际上DBMS_SQL 将通常执行SQL和PL/SQL的处理给外部化了并把它们置于你的控制之下。
§26.1.1 静态SQL和动态SQL
l 静态SQL 语句:PL/SQL 程序中在编译时SQL语句是明确的。
l 动态SQL语句:PL/SQL程序中在编译时SQL语句是不明确的,编译程序对动态语句部分不进行处理。只在程序运行时动态创建并进行语法分析和执行。
l 可以通过使用DBMS_SQL包(PL/SQL 2.1及以上版本)来实现动态。
l 也可以通过本地动态SQL方法来实现动态。
§26.1.2 用DBMS_SQL 包实现动态
在PL/SQL 2.1及以上版本,可以使用DBMS_SQL包来实现动态。DBMS_SQL包执行的步骤如下:
1) 将SQL语句或PL/SQL语句块放到一个字符串中;
2) 使用DBMS_SQL.PARSE分析该字串;
3) 使用DBMS_SQL.BIND_VARIABLE联编输入变量;
4) 如果该语句不是查询语句,使用DBMS_SQL.EXECUTE 和/或DBMS_SQL.VARIABLE_VALUE执行它。如果是查询语句继续执行5);
5) 如果该语句是查询语句,使用DBMS_SQL.DEFINE_COLUMN定义输出变量;
6) 使用DBMS_SQL.EXECUTE,DBMS_SQL.FETCH_ROWS,
DBMS_SQL.COLUMN_VALUE,以及DBMS_SQL.VARIABLE_VALUE执行查询语句并提取结果。
例
REM 文件名:recrtemp.sql
REM 这个存储过程使用DBMS_SQL 包重新创建 temp_table
REM 作者:ScottUrman.
CREATE OR REPLACE PROCEDURE RecreateTempTable (
/*
删除 temp_table表再重建.调用方法:
RecreateTempTable('(num_col NUMBER, char_col VARCHAR2(2000))');
*/
p_Description INVARCHAR2) IS
v_Cursor NUMBER;
v_CreateString VARCHAR2(100);
v_DropString VARCHAR2(100);
v_NumRows INTEGER;
BEGIN
/* 打开光标. */
v_Cursor :=DBMS_SQL.OPEN_CURSOR;
/* 先删除表. */
v_DropString :='DROP TABLE temp_table';
/* 分析和执行 'DROP TABLE' 命令. 可能表不存在而出现 ORA-942 错误. */
BEGIN
--DBMS_SQL.NATIVE is a constant defined in the package header.
-- Oracle8是dbms_sql.native
--DBMS_SQL.PARSE(v_Cursor,v_DropString, dbms_sql.native);
DBMS_SQL.PARSE(v_Cursor, v_DropString, dbms_sql.native);
v_NumRows := DBMS_SQL.EXECUTE(v_Cursor);
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE !=-942 THEN
RAISE;
END IF;
END;
/* 先建立表,需要CREATE TABLE 字符串然后分析和执行. */
v_CreateString :='CREATE TABLE temp_table ' || p_Description;
DBMS_SQL.PARSE(v_Cursor, v_CreateString, dbms_sql.native);
v_NumRows := DBMS_SQL.EXECUTE(v_Cursor);
/* 关闭光标, 工作已经完成。 */
DBMS_SQL.CLOSE_CURSOR(v_Cursor);
EXCEPTION
WHEN OTHERS THEN
/* 先关闭光标,然后再启动错误以传播信息*/
DBMS_SQL.CLOSE_CURSOR(v_Cursor);
RAISE;
END RecreateTempTable;
/
警告:为了运行本程句,你必须被授予 CREATE TABLE 和DROP TABLE 系统权限。
§26.1.3 用本地动态SQL实现动态
在Oracle8I或更高的版本中,可以使用EXECUTE IMMEDATE语句来处理非查询语句和PL/SQL块的光标。
§26.2 使用DBMS_SQL进行动态编程
DBMS_SQL包可以实现动态编程,即在PL/SQL程序中使用DDL语句和会话语句等。
§26.2.1 执行DML、DDL及Alter session语句
在PL/SQL程序中,执行DML 、DDL及altersession 语句需要下面步骤:
1) 打开光标(opencursor)
2) 分析该语句(parse)
3) 联编输入变量(bind variable)
4) 执行该语句(execute)
5) 关闭该光标(closecursor)
1.打开光标
打开光标的语法如下:
OPEN_CURSOR RETURN INTEGER;
2. 分析语句
将语句发给服务器分析,如有错误则返回提示。语法如下:
PROCEDURE PARSE ( c IN INTEGER,
Statement IN VARCHAR2,
Language_flag IN INTEGER);
其中:
c integer 分析语句用的光标id 号
statement varchar2 要分析的SQL语句
language_flag integer 决定语句的处理方式,可能为:DBMS_SQL.V6
DBMS_SQL.NATIVE, DBMS_SQL.NATIVE
3. 联编输入变量
联编将语句中的占位符和实际的PL/SQl变量关联起来。
下面是用于联编不同类型变量的过程:
1)用于联编number变量的过程:
PROCEDURE BIND_VARIABLE ( c IN INTEGER,
Name IN VARCHAR2,
Value IN NUMBER ) ;
2)用于联编varchar2变量的过程:
PROCEDURE BIND_VARIABLE ( c IN INTEGER,
Name IN VARCHAR2,
Value IN VARCHAR2 ) ;
PROCEDURE BIND_VARIABLE ( c IN INTEGER,
Name IN VARCHAR2,
Value IN VARCHAR2,
Out_value_size IN INTEGER );
3)用于联编date变量的过程:
PROCEDURE BIND_VARIABLE ( c IN INTEGER,
Name IN VARCHAR2,
Value IN DATE ) ;
4)用于联编char变量的过程:
PROCEDURE BIND_VARIABLE ( c IN INTEGER,
Name IN VARCHAR2,
Value IN CHAR ) ;
PROCEDURE BIND_VARIABLE ( c IN INTEGER,
Name IN VARCHAR2,
Value IN CHAR ,
Out_value_size IN INTEGER );
5)用于联编RAW 变量的过程:
PROCEDURE BIND_VARIABLE ( c IN INTEGER,
Name IN VARCHAR2,
Value IN RAW ) ;
PROCEDURE BIND_VARIABLE ( c IN INTEGER,
Name IN VARCHAR2,
Value IN RAW,
Out_value_size IN INTEGER );
6)用于联编MLSLABLE变量的过程:
PROCEDURE BIND_VARIABLE ( c IN INTEGER,
Name IN VARCHAR2,
Value IN MLSLABLE ) ;
7)用于联编ROWID 变量的过程:
PROCEDURE BIND_VARIABLE ( c IN INTEGER,
Name IN VARCHAR2,
Value IN ROWID ) ;
4. 执行语句
用EXECUTE命令可以执行语句,执行后返回已经处理的行数。注意,EXECUTE执行的仅是合法的语句,对于DDL语句,EXECUTE执行的空操作(no_op)。这些DDL语句只在PARSE命令时被执行。EXECUTE函数的语法如下:
FUNCTION EXECUTE ( c IN INTEGER ) RETURN INTEGER;
C integer 是要执行的光标的ID号
Return integer 已经处理过语句的行数
5. 关闭光标
关闭光标用CLOSE_CURSOR命令来实现。其语法如下:
PROCEDURE CLOSE_CURSOR(c IN OUT INTEGER ) ;
§26.2.2示例
REM updclass.sql
REM 这是一个用 DBMS_SQL 来执行DML 语句的例子
CREATE OR REPLACE PROCEDURE UpdateClasses(
/* 使用DBMS_SQL 更新一个教室表 */
p_Department IN classes.department%TYPE,
p_NewCredits IN classes.num_credits%TYPE,
p_RowsUpdated OUT INTEGER) AS
v_CursorID INTEGER;
v_UpdateStmt VARCHAR2(100);
BEGIN
-- 为处理打开光标
v_CursorID :=DBMS_SQL.OPEN_CURSOR;
-- 确定 SQL 字串.
v_UpdateStmt :='UPDATE classes
SETnum_credits = :nc
WHEREdepartment = :dept';
-- 分析语句.
DBMS_SQL.PARSE(v_CursorID, v_UpdateStmt, DBMS_SQL.NATIVE);
-- 绑定变量 p_NewCredits 到占为符 :nc.
DBMS_SQL.BIND_VARIABLE(v_CursorID, ':nc', p_NewCredits);
-- 绑定变量p_Department 到占为符:dept.
DBMS_SQL.BIND_VARIABLE(v_CursorID, ':dept', p_Department);
-- 执行语句.
p_RowsUpdated :=DBMS_SQL.EXECUTE(v_CursorID);
--关闭光标.
DBMS_SQL.CLOSE_CURSOR(v_CursorID);
EXCEPTION
WHEN OTHERS THEN
--关闭光标,然后激活错误.
DBMS_SQL.CLOSE_CURSOR(v_CursorID);
RAISE;
/
§26.2.3 执行DDL语句
在PL/SQL程序中,与执行DML 语句不同之处:
1)因为在DDL语句中联编变量是非法的,所以分析后不需 BIND_VARIABLE
2)DDL语句实际上是在调用PARSE时执行的,因而EXECUTE 也不必要(不起作用)。
例:
REM recrtmp2.sql
REMRecreateTempTable 说明使用DBMS_SQL 执行 DDL命令的例子
REM 作者:Scott Urman.
CREATE OR REPLACEPROCEDURE RecreateTempTable (
/* 删除temp_table表在重建它. */
p_Description IN VARCHAR2) IS
v_Cursor NUMBER;
v_CreateString VARCHAR2(100);
v_DropString VARCHAR2(100);
BEGIN
/* 打开光标. */
v_Cursor := DBMS_SQL.OPEN_CURSOR;
/* 先删除表. */
v_DropString := 'DROP TABLE temp_table';
/* 分析 'DROP TABLE' 命令, 然后执行. 可能提示
ORA-942 error in case the table doesn'tyet exist. */
BEGIN
-- DBMS_SQL.NATIVE 是一个常数.
DBMS_SQL.PARSE(v_Cursor, v_DropString,DBMS_SQL.NATIVE);
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
/* 重新建表,需要 CREATE TABLE 字符串,然后分析执行 */
v_CreateString := 'CREATE TABLE temp_table '|| p_Description;
DBMS_SQL.PARSE(v_Cursor, v_CreateString,DBMS_SQL.NATIVE);
/* 关闭光标, 工作完成。 */
DBMS_SQL.CLOSE_CURSOR(v_Cursor);
EXCEPTION
WHEN OTHERS THEN
/* 首先关闭光标, 然后激活错误提示 */
DBMS_SQL.CLOSE_CURSOR(v_Cursor);
RAISE;
ENDRecreateTempTable;
/
§26.2.4 执行PL/SQL块
在PL/SQL程序中,可以在动态中执行PL/SQL块。对PL/SQL块的执行处理上与对DML、DDL的处理类似。它的调用步骤如下:
l 打开光标(OPEN_CURSOR);
l 对查询语句进行分析(PARSE);
l 绑定所有的输入/输出变量(BAND_VARIBLE);
l 执行查询语句(EXECUTE);
l 检索所有输出变量的值(COLUMN_VALUE);
l 关闭光标。
1 对PL/SQL 块语句进行语法分析
传到PARSE中的所有PL/SQL块字符(包括END;),包括绑定变量标识符、输出变量(如果存储过程带OUT参数)进行分析。在块中执行完VRIABLE_VALUE后,可以检索这些变量的新值。
提示:由于语法分析不能识别双减号(- -),建议使用/* */ 作为注释。
2 检索所有输出变量的值
在执行结束后,可以使用VRIABLE_VALUE来检索存放输出结果的变量的值。与查询语句类似,该值是由EXECUTE语句先存储在缓冲区中,然后使用VRIABLE_VALUE从缓冲区将它们检索出来。检索只是些被BIND_VRIABLE中变量才能用VRIABLE_VALUE进行操作。
§26.4 本地动态SQL
前面提到,有两方式可以使用动态SQL语句,一种是DBMS_SQL包;另外就是本地动态SQL。本地动态SQL是使用EXECUTE IMMEDIATE语句来实现的。下面给出介绍。
§26.4.1使用EXECUTE IMMEDIATE语句
可以用EXECUTE IMMEDIATE语句准备(分析)和立即执行一个动态的语句或一个匿名PL/SQL块。语法如下:
EXECUTE IMMEDIATE dynamic_string
[INTO {define_variable[, define_variable]... |record}]
[USING [IN | OUT | IN OUT] bind_argument
[, [IN | OUT | IN OUT] bind_argument]...]
[{RETURNING | RETURN} INTO bind_argument[,bind_argument]...];
其中:
dynamic_string SQL 语句表达式或 PL/SQL 块,
define_variable 存储select 列值的变量
record 用户定义的或选择的记录类型%ROWTYPE
bind_argument (IN 输入)可传到动态SQL的表达式或PL/SQL块
bind_argument 存储返回值的变量或PL/SQL块
例子:下面包括几个动态SQL:
DECLARE
sql_stmt VARCHAR2(200);
plsql_block VARCHAR2(500);
emp_id NUMBER(4) := 7566;
salary NUMBER(7,2);
dept_id NUMBER(2) := 50;
dept_name VARCHAR2(14) := ’PERSONNEL’;
location VARCHAR2(13) := ’DALLAS’;
emp_rec emp%ROWTYPE;
BEGIN
EXECUTE IMMEDIATE ’CREATE TABLE bonus (id NUMBER, amt NUMBER)’;
sql_stmt := ’INSERT INTO dept VALUES (:1, :2, :3)’;
EXECUTE IMMEDIATE sql_stmt USING dept_id, dept_name, location;
sql_stmt := ’SELECT * FROM emp WHERE empno = :id’;
EXECUTE IMMEDIATE sql_stmt INTO emp_rec USING emp_id;
plsql_block := ’BEGIN emp_pkg.raise_salary(:id, :amt); END;’;
EXECUTE IMMEDIATE plsql_block USING 7788, 500;
sql_stmt := ’UPDATE emp SET sal = 2000 WHERE empno = :1
RETURNING sal INTO :2’;
EXECUTE IMMEDIATE sql_stmt USING emp_id RETURNING INTO salary;
EXECUTE IMMEDIATE ’DELETE FROM dept WHERE deptno = :num’
USING dept_id;
EXECUTE IMMEDIATE ’ALTER SESSION SET SQL_TRACE TRUE’;
END;
§26.4.2向后兼容情况
当一个动态的INSERT、UPDATE或DELETE语句有一个RETURNING 子句时,输出绑定变量可以放进到RETURNING INTO 子句中或者使用该子句。在新的应用中,可以使用RETURNING INTO 子句;而在旧的应用中,你可继续使用USING子句。下面的语句都是合法的:
DECLARE
sql_stmt VARCHAR2(200);
my_empno NUMBER(4) := 7902;
my_ename VARCHAR2(10);
my_job VARCHAR2(9);
my_sal NUMBER(7,2) := 3250.00;
BEGIN
sql_stmt := ’UPDATE emp SET sal = :1 WHERE empno = :2
RETURNING ename, job INTO :3, :4’;
/* Bind returned values via USINGclause. */
EXECUTE IMMEDIATE sql_stmt
USING my_sal, my_empno, OUT my_ename, OUT my_job;
/* Bind returned values via RETURNINGINTO clause. */
EXECUTE IMMEDIATE sql_stmt
USING my_sal, my_empno RETURNINGINTO my_ename, my_job;
...
END;
§26.4.3指定参数模式
使用USING子句,你可以不用为输入绑定变量指定参数,因为这个模式的缺省是IN。使用RETURNING INTO子句,你不能为输出绑定变量指定参数模式,因为该模式是OUT。例如:
DECLARE
sql_stmt VARCHAR2(200);
dept_id NUMBER(2) := 30;
old_loc VARCHAR2(13);
BEGIN
sql_stmt :=
’DELETE FROM dept WHERE deptno = :1 RETURNING loc INTO :2’;
EXECUTE IMMEDIATE sql_stmt USING dept_id RETURNING INTO old_loc;
...
END;
在适当时,你可以以参数形式为绑定变量指定 OUT 或 IN 模式。例如,假定你希望调用下面独立的过程:
CREATE PROCEDURE create_dept (
deptno IN OUT NUMBER,
dname IN VARCHAR2,
loc IN VARCHAR2) AS
BEGIN
deptno := deptno_seq.NEXTVAL;
INSERT INTO dept VALUES (deptno, dname, loc);
END;
但是,从一个动态的PL/SQL块来调用过程时,你必须为绑定变量指定IN OUT模式。如:
DECLARE
plsql_block VARCHAR2(500);
new_deptno NUMBER(2);
new_dname VARCHAR2(14) := ’ADVERTISING’;
new_loc VARCHAR2(13) := ’NEW YORK’;
BEGIN
plsql_block := ’BEGIN create_dept(:a, :b, :c); END;’;
EXECUTE IMMEDIATE plsql_block
USING IN OUT new_deptno, new_dname, new_loc;
IF new_deptno > 90 THEN ...
END;
第二十七章LOB和DBMS_LOB包简介
Oracle7以后的版本都支持大型对象,Oracle8/8I支持的大型对象有:
LOB类型 | 说明 |
CLOB | 字符型类型大对象 |
NCLOB | 多字节国家字符数据,可存放变长的字符。 |
BLOB | 无结构的二进制数据。 |
BFILE | 外部二进制数据,数据文件存放操作系统目录下。 |
到Oracle8I以后,Oracle提供了更多的关于LOB处理的包,随着硬盘容量的迅速扩大和数据库存储技术的完善以及许多应用对LOB的需求的增加,希望能在应用中存放LOB类型数据(如图象、声音等)的要求已经提到日程上。下面简单介绍关于LOB数据的使用方法。
§27.1 LOB类型一般使用
在Oracle8/8I、Oracle9I系统中,仍支持LONG、LONG RAW等大对象类型,但是,在不同的版本中,所支持的LOB的大小有所不同。要使用LOB类型,要注意它有下面的特性:
l LONG 、LONG RAW 最大长度4GB
l 可以通过Oracle8I OCI或PL/SQL包来处理LOB
l LOB可以作为绑定变量来使用
§27.1.1 LOB类型存储
一般来说,LONG,LONGRAW类型数据都常常存放在数据库的表中,但LOB很少存放在数据库的表中,而是在数据库的原始表中存放指示LOB存储的位置。
1.LOB类型:
LOB是用来存放二进制数据的类型,它与RAW或LONG RAW类型一样。数据库不解释该类型中存放数据的真正含义,只是通过接口来对这些数据进行检索或修改。
2.CLOB和NCLOB 类型:
CLOB用来存放大的字符类型,而NCLOB则是用来存放国家字符集的类型。这两个实际就是用双字节统一字符集的数据。
3.BFILE类型:
为了访问操作系统下的文件,Oracle允许定义BFILE类型数据。为了使Oracle能知道外部文件所在目录和文件名字,还需要建立相应的目录:
CREATE DIRECTORYdir_name AS os_path;
CREATE DIRECTORY utils AS ‘/home/utils’ ;
已经创建的目录可以从 DBA_DIRECTORIES数据字典中查到。
如果取消目录可以用
DROP DIRECTORYdir_name;
来实现。
一般来说,每个会话可以使用的外部文件的打开数与SESSION_MAX_OPEN_FILES参数有关。
§27.1.2 临时LOB类型
在Oracle8I里,一般的LOB声明都具有永久的属性,也就是说LOB类型的数据可以永久地存放在数据库表里。此外,Oracle8I还提供了临时的LOB。它是通过定义指针来实现。
当创建表为临时的LOB表结构时,该表内的LOB只是会话期间存在,当会话结束后,临时LOB就被取消。
临时LOB使用要注意下面情况:
l 如果输入参数为NULL,在DBMS_LOB中的所有函数返回NULL;如果以NULL作为LOB指针,则所有DBMS_LOB引发例外。
l 如果字符集的ID匹配(CLOB参数、VARCHAR2 缓冲区和模式),则基于CLOB运算不作检查。
l 数据存储资源可以由DBA通过建立不同的临时表空间来控制。DBA可以为不同的用户定义不同的表空间。
l 为了与永久的LOB一致,临时LOB也可以跟语义值并符合ANSI标准。
例:
DECLARE
a blob;
b blob;
BEGIN
dbms_lob.createtemporary(b, TRUE);
-- the following assignment results in a deep copy
a := b;
END;
§27.1.3 LOB类型的接口
Oracle8I提供了下面6种接口:
接口 | Oracle/Oracle8i | 说明 |
DBMS_LOB | Oracle8 | 通过PL/SQL包来访问LOB。 |
OCI | Oracle8 | 通过Pro*C/C++来处理LOB数据 |
Pro*C/C++ | Oracle8i | 允许在Pro*C/C++中嵌入访问LOB的命令。 |
Pro*COBOL | Oracle8i | 允许在Pro*COBOL中嵌入访问LOB的命令。 |
OLE(OO4O) | Oracle8i | OO4O提供在VB中访问LOB。 |
JDBC | Oracle8i | JDBC API允许从Java 程序中对LOB进行操作。 |
§27.2 一般LOB表与数据加载
LOB类型是特殊的类型,它不象数据和字符那样容易,一般要使用数据库提供的包等来实现数据的操作。
§27.2.1 建立包含LOB的表
Oracle支持LOB,那么如何才能存放LOB到表中呢,可以有三种办法:
l 建立一个带一个或多个LOB类型的表;
l 建立一个带对象类型,并且对象类型包含LOB的表;
l 建立一个嵌套的表,嵌套包含LOB类型。
下面仅介绍在创建表结构时带有LOB类型的情况。
例:创建具有LOB的表
CONNECT system/manager;
DROP USER samp CASCADE;
DROP DIRECTORY AUDIO_DIR;
DROP DIRECTORY FRAME_DIR;
DROP DIRECTORY PHOTO_DIR;
DROP TYPE InSeg_typ force;
DROP TYPE InSeg_tab;
DROP TABLE InSeg_table;
CREATE USER samp identified by samp;
GRANT CONNECT, RESOURCE to samp;
CREATE DIRECTORY AUDIO_DIR AS ’/tmp/’;
CREATE DIRECTORY FRAME_DIR AS ’/tmp/’;
CREATE DIRECTORY PHOTO_DIR AS ’/tmp/’;
GRANT READ ON DIRECTORY AUDIO_DIR to samp;
GRANT READ ON DIRECTORY FRAME_DIR to samp;
GRANT READ ON DIRECTORY PHOTO_DIR to samp;
CONNECT samp/samp
CREATE TABLE a_table (blob_col BLOB);
CREATE TYPE Voiced_typ AS OBJECT (
Originator VARCHAR2(30),
Script CLOB,
Actor VARCHAR2(30),
Take NUMBER,
Recording BFILE
);
CREATE TABLE VoiceoverLib_tab of Voiced_typ (
Script DEFAULT EMPTY_CLOB(),
CONSTRAINT TakeLib CHECK (Take IS NOT NULL),
Recording DEFAULT NULL
);
CREATE TYPE InSeg_typ AS OBJECT (
Segment NUMBER,
Interview_Date DATE,
Interviewer VARCHAR2(30),
Interviewee VARCHAR2(30),
Recording BFILE,
Transcript CLOB
);
CREATE TYPE InSeg_tab AS TABLE of InSeg_typ;
CREATE TYPE Map_typ AS OBJECT (
Region VARCHAR2(30),
NW NUMBER,
NE NUMBER,
SW NUMBER,
SE NUMBER,
Drawing BLOB,
Aerial BFILE
);
CREATE TABLE Map_Libtab of Map_typ;
CREATE TABLE Voiceover_tab of Voiced_typ (
Script DEFAULT EMPTY_CLOB(),
CONSTRAINT Take CHECK (Take IS NOT NULL),
Recording DEFAULT NULL
);
CREATE TABLE Multimedia_tab (
Clip_ID NUMBER NOT NULL,
Story CLOB default EMPTY_CLOB(),
FLSub NCLOBdefault EMPTY_CLOB(),
Photo BFILE default NULL,
Frame BLOB default EMPTY_BLOB(),
Sound BLOB default EMPTY_BLOB(),
Voiced_ref REF Voiced_typ,
InSeg_ntab InSeg_tab,
Music BFILE default NULL,
Map_obj Map_typ
) NESTEDTABLE InSeg_ntab STORE AS InSeg_nestedtab;
§27.2.2 用EMPTY_CLOB或EMPTY_BLOB插入LOB
Oracle8提供了DML语句EMPTY_CLOB()和EMPTY_BLOB()可以实现将LOB类型数据加载到表中。但是,这些函数与DBMS_LOB没有关系。
/*
列STORY 和 FLSUB 用 EMPTY_CLOB()来初始化
列 FRAME 和SOUND用EMPTY_BLOB()来初始化
列TRANSSCRIPT用EMPTY_CLOB() 来初始化
列DRAWING 用 EMPTY_BLOB()来初始化
*/
INSERT INTO Multimedia_tab
VALUES (1, EMPTY_CLOB(), EMPTY_CLOB(), NULL,EMPTY_BLOB(), EMPTY_BLOB(),
NULL, InSeg_tab(InSeg_typ(1, NULL, ’TedKoppell’, ’Jimmy Carter’, NULL,
EMPTY_CLOB())), NULL, Map_typ(’Moon Mountain’,23, 34, 45, 56, EMPTY_BLOB(),
NULL));
/* 在 Voiceover_tab表的新行中,SCRIPT列要用来初始化 */
INSERT INTO Voiceover_tab
VALUES (’Abraham Lincoln’, EMPTY_CLOB(), ’JamesEarl Jones’, 1, NULL);
§27.2.3 一般的LOB插入
可以编写一个简单的过程来实现将LOB加载到多媒体表中:
例。
CREATE OR REPLACE PROCEDUREinsertUseBindVariable_proc
(Rownum IN NUMBER, Blob_loc IN BLOB) IS
BEGIN
INSERT INTO Multimedia_tab (Clip_ID, Sound)VALUES (Rownum, Blob_loc);
END;
DECLARE
Blob_loc BLOB;
BEGIN
/* Select the LOB from the row where Clip_ID =1,
Initialize the LOB locator bind variable: */
SELECT Sound INTO Blob_loc
FROM Multimedia_tab
WHERE Clip_ID = 1;
/* Insert into the row where Clip_ID = 2: */
insertUseBindVariable_proc (2, Blob_loc);
COMMIT;
END;
§27.3 内部LOB和DBMS_LOB的使用
Oracle系统提供的DBMS_LOB包可以完成对LOB对象进行内部读写和外部读写操作。DBMS_LOB包包括多个程序,如:
l DBMS_LOB.APPEND 将源LOB追加到目标LOB的尾部;
l DBMS_LOB.INST 返回LOB中给定模式的位置;
l DBMS_LOB.COMPARE 比较LOB是否相等。
。 。 。 。 。 。
§27.3.1 APPEND过程
此过程可以加内部的完整的源LOB到目标的LOB中。语法如下:
DBMS_LOB.APPEND (
dest_lob IN OUT NOCOPY BLOB,
src_lob IN BLOB);
DBMS_LOB.APPEND (
dest_lob IN OUT NOCOPY CLOB CHARACTER SETANY_CS,
src_lob IN CLOB CHARACTER SET dest_lob%CHARSET);
例子:
CREATE OR REPLACE PROCEDURE Example_1a IS
dest_lob, src_lob BLOB;
BEGIN
-- 得到 LOB 位置
-- 用FOR UPDATE锁住行
SELECT b_lob INTO dest_lob
FROM lob_table
WHERE key_value = 12 FOR UPDATE;
SELECT b_lob INTO src_lob
FROM lob_table
WHERE key_value = 21;
DBMS_LOB.APPEND(dest_lob, src_lob);
COMMIT;
EXCEPTION
WHEN some_exception
THEN handle_exception;
END;
CREATE OR REPLACE PROCEDURE Example_1b IS
dest_lob, src_lob BLOB;
BEGIN
-- 得到 LOB 位置
-- 用FOR UPDATE锁行
SELECT b_lob INTO dest_lob
FROM lob_table
WHERE key_value = 12 FOR UPDATE;
SELECT b_lob INTO src_lob
FROM lob_table
WHERE key_value = 12;
DBMS_LOB.APPEND(dest_lob, src_lob);
COMMIT;
EXCEPTION
WHEN some_exception
THEN handle_exception;
END;
§27.3.2 CLOSE过程
此过程关闭掉先前打开的内部或外部LOB,语法如下:
DBMS_LOB.CLOSE (
lob_loc IN OUT NOCOPY BLOB);
DBMS_LOB.CLOSE (
lob_loc IN OUT NOCOPY CLOB CHARACTER SET ANY_CS);
DBMS_LOB.CLOSE (
file_loc IN OUT NOCOPY BFILE);
§27.3.3 COMPARE函数
这个函数比较两个整体的LOB或部分的LOB。可以只比较相同数据类型部分。对于BFILE,要求该文件必须用FILEOPEN成功打开过。
比较返回0表示完全匹配,否则如果是非0,则表示不完全匹配。对于固定宽度n-字节CLOB,则COMPARE以(4294967295/n)或Max(length(clob1), length(clob2))范围大小来匹配字符。
语法如下:
DBMS_LOB.COMPARE (
lob_1 IN BLOB,
lob_2 IN BLOB,
amount IN INTEGER := 4294967295,
offset_1 IN INTEGER := 1,
offset_2 IN INTEGER := 1)
RETURN INTEGER;
DBMS_LOB.COMPARE (
lob_1 IN CLOB CHARACTER SET ANY_CS,
lob_2 IN CLOB CHARACTER SET lob_1%CHARSET,
amount IN INTEGER := 4294967295,
offset_1 IN INTEGER := 1,
offset_2 IN INTEGER := 1)
RETURN INTEGER;
DBMS_LOB.COMPARE (
lob_1 IN BFILE,
lob_2 IN BFILE,
amount IN INTEGER,
offset_1 IN INTEGER := 1,
offset_2 IN INTEGER := 1)
RETURN INTEGER;
lob_1 LOB 比较的第一目标指示
lob_2 LOB 比较的第二目标指示
amount 比较的字节数(BLOBs) 或字符 (对 CLOBs)
offset_1 LOB中以字节的偏移量
offset_2 LOB中以字节的偏移量
返回:
l 整数:0 ,如果比较成功,否则返回非0
l NULL,如果是下面:
– 数量 < 1
– 数量 > LOBMAXSIZE
– 偏移_1 或偏移_2 < 1
l 偏移_1 或偏移_2 > LOBMAXSIZE
例子:
CREATE OR REPLACE PROCEDURE Example2a IS
lob_1, lob_2 BLOB;
retval INTEGER;
BEGIN
SELECT b_col INTO lob_1 FROM lob_table
WHERE key_value = 45;
SELECT b_col INTO lob_2 FROM lob_table
WHERE key_value = 54;
retval := dbms_lob.compare(lob_1, lob_2, 5600, 33482,
128);
IF retval = 0 THEN
; -- 处理比较代码
ELSE
; -- 处理非比较代码
END IF;
END;
CREATE OR REPLACE PROCEDURE Example_2b IS
fil_1, fil_2 BFILE;
retval INTEGER;
BEGIN
SELECT f_lob INTO fil_1 FROM lob_table WHERE key_value = 45;
SELECT f_lob INTO fil_2 FROM lob_table WHERE key_value = 54;
dbms_lob.fileopen(fil_1, dbms_lob.file_readonly);
dbms_lob.fileopen(fil_2, dbms_lob.file_readonly);
retval := dbms_lob.compare(fil_1, fil_2, 5600,
3348276, 2765612);
IF (retval = 0)
THEN
; -- 处理比较代码
ELSE
; -- 处理非比较代码
END IF;
dbms_lob.fileclose(fil_1);
dbms_lob.fileclose(fil_2);
END;
§27.3.4 COPY过程
COPY过程拷贝所有或部分的源内部LOB到目标的内部LOB上。如果目标LOB指定的偏移量超出源LOB当前数据的尾部,则超出部分用0字节或空字符填充。如果目标LOB指定的偏移量小于源LOB当前数据,则目标LOB的尾部被覆盖。
语法如下:
DBMS_LOB.COPY (
dest_lob IN OUTNOCOPY BLOB,
src_lob INBLOB,
amount ININTEGER,
dest_offset ININTEGER := 1,
src_offset ININTEGER := 1);
DBMS_LOB.COPY (
dest_lob IN OUTNOCOPY CLOB CHARACTER SET ANY_CS,
src_lob IN CLOBCHARACTER SET dest_lob%CHARSET,
amount ININTEGER,
dest_offset ININTEGER := 1,
src_offset ININTEGER := 1);
其中:
dest_offset 置入目标LOB字节数或字符数。
src_offset 源LOB的字节或字符数的偏移量。
dest_lob 目标LOB的定位指针。
src_lob 源LOB的定位指针。
Amount 要复制的字节或字符数。
例1.用PL/SQL拷贝部分LOB的过程:
CREATE OR REPLACE PROCEDURE copyLOB_proc IS
Dest_loc BLOB;
Src_loc BLOB;
Amount NUMBER;
Dest_pos NUMBER;
Src_pos NUMBER;
BEGIN
SELECT Sound INTO Dest_loc FROM Multimedia_tab
WHERE Clip_ID = 2 FOR UPDATE;
/* 查询 LOB,并放入src_lob中: */
SELECT Sound INTO Src_loc FROM Multimedia_tab
WHERE Clip_ID = 1;
/* 打开 LOB : */
DBMS_LOB.OPEN(Dest_loc, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
/* 从源 LOB 拷贝到目标LOB位置:*/
DBMS_LOB.COPY(Dest_loc,Src_loc, Amount, Dest_pos, Src_pos);
/* 如果已经打开LOB,则强制关闭LOB: */
DBMS_LOB.CLOSE(Dest_loc);
DBMS_LOB.CLOSE(Src_loc);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(’Operation failed’);
END;
例2.用OCI(C语言)拷贝部分LOB的过程:
/* 选择定位 */
sb4select_lock_sound_locator_2(Lob_loc, dest_type, errhp, svchp, stmthp)
OCILobLocator*Lob_loc;
ub1 dest_type;
OCIError*errhp;
OCISvcCtx*svchp;
OCIStmt*stmthp;
{
charsqlstmt[150];
OCIDefine*defnp1;
if (dest_type== TRUE)
{
strcpy(sqlstmt,
(char*)"SELECT Sound FROM Multimedia_tab
WHERE Clip_ID=2FOR UPDATE");
printf ("select destination sound locator\n");
}
else
{
strcpy(sqlstmt,(char *)"SELECT Sound FROM Multimedia_tab WHERE Clip_ID=1");
printf ("select source sound locator\n");
}
checkerr(errhp, OCIStmtPrepare(stmthp, errhp, (text *)sqlstmt,
(ub4)strlen((char*)sqlstmt),
(ub4)OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT));
checkerr(errhp, OCIDefineByPos(stmthp, &defnp1, errhp, (ub4) 1,
(dvoid*)&Lob_loc, (sb4)0,
(ub2)SQLT_BLOB,(dvoid *) 0,
(ub2 *) 0, (ub2*) 0, (ub4) OCI_DEFAULT));
/* 执行 select并返回一行记录 */
checkerr(errhp,OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
(CONSTOCISnapshot*) 0, (OCISnapshot*) 0,
(ub4)OCI_DEFAULT));
return 0;
}
§27.3.5 ERASE过程
ERASE过程是用来删除LOB的全部或部分数据,被删除的LOB是以空格(CLOB)或0(BLOB)填充。ERASE过程的语法如下:
PROCEDUREERASE(lob_loc IN OUT BLOB,
amount IN OUT INTEGER,
offset IN INTEGER := 1);
lob_loc 要删除的LOB定位指针。
Amount offset + amount 大于LOB值的长度,则该LOB全部被删除,此时amount 返回的实际删除字节数。
Offset 偏移量,即删除的开始位置。
例.删除部分LOB
/* 本程序是删除部分LOB的包: */
CREATE OR REPLACE PROCEDURE eraseLOB_proc IS
Lob_loc BLOB;
Amount INTEGER := 3000;
BEGIN
/* 查询LOB, 得到 LOB 位置: */
SELECT Sound INTO lob_loc FROM Multimedia_tab
WHERE Clip_ID = 1
FOR UPDATE;
/* 打开LOB : */
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READWRITE);
/* 删除数据:*/
DBMS_LOB.ERASE(Lob_loc, Amount, 2000);
/* 如果已经打开LOB,则强行关闭LOB */
DBMS_LOB.CLOSE (Lob_loc);
COMMIT;
/* 异常处理:*/
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(’Operation failed’);
END;
§27.3.6 GETCHUNKSIZE函数
可以用GETCHUNKSIZE函数来得到要处理的LOB的块的长度。语法如下:
FUNCTION GETCHUNKSIZE ( lob_loc IN BLOB )
RETURN INTEGER;
FUNCTION GETCHUNKSIZE ( lob_loc IN CLOB CHARACTER any_cs)
RETURN INTEGER;
例。从LOB读数据:
CREATE OR REPLACE PROCEDURE readLOB_proc IS
Lob_loc BLOB;
Buffer RAW(32767);
Amount BINARY_INTEGER := 32767;
Position INTEGER := 1000;
Chunksize INTEGER;
BEGIN
/* 查询LOB: */
SELECT Frame INTO Lob_loc
FROM Multimedia_tab
WHERE Clip_ID = 1;
/* 对LOB列找出 chunksize: */
Chunksize :=DBMS_LOB.GETCHUNKSIZE(Lob_loc);
IF (Chunksize < 32767) THEN
Amount := (32767 / Chunksize) * Chunksize;
END IF;
/* 打开 LOB : */
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READONLY);
/* 从 LOB 读数据: */
DBMS_LOB.READ (Lob_loc, Amount, Position, Buffer);
/* 关闭LOB: */
DBMS_LOB.CLOSE (Lob_loc);
END;
§27.3.7 GETLENGTH函数
函数GETLENGTH可以返回LOB的当前长度。语法如下:
FUNCTION GETLENGTH ( lob_loc IN BLOB )
RETURN INTEGER;
FUNCTION GETLENGTH ( lob_loc IN CLOB CHARACTER any_cs)
RETURN INTEGER;
例。从LOB得到长度:
CREATE OR REPLACE PROCEDURE getLengthLOB_proc IS
Lob_loc NCLOB;
Length INTEGER;
BEGIN
/* 查询 LOB: */
SELECT FLSub INTO Lob_loc FROM Multimedia_tab
WHERE Clip_ID = 2;
/* 打开 LOB: */
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READONLY);
/* 得到 LOB 长度:*/
length:= DBMS_LOB.GETLENGTH(Lob_loc);
IF length IS NULL THEN
DBMS_OUTPUT.PUT_LINE(’LOB is null.’);
ELSE
DBMS_OUTPUT.PUT_LINE(’The length is ’|| length);
END IF;
/* 关闭 LOB: */
DBMS_LOB.CLOSE (Lob_loc);
END;
§27.3.8 INSTR函数
INSTR用于确定LOB的第n 个模式值出现的位置。语法如下:
FUNCTION INSTR ( lob_loc IN BLOB ,
Pattern IN RAW,
Offset IN INTEGER := 1,
Nth IN INTEGER := 1
);
FUNCTION INSTR ( lob_loc IN BFILE ,
Pattern IN RAW,
Offset IN INTEGER := 1,
Nth IN INTEGER := 1
);
例子:
CREATE OR REPLACE PROCEDURE instringTempLOB_proc IS
Lob_loc CLOB;
Temp_clob CLOB;
Pattern VARCHAR2(30) := ’children’; Position INTEGER := 0;
Offset INTEGER := 1;
Occurrence INTEGER := 1;
BEGIN
/* 建立临时 LOB 和拷贝 CLOB 到该临时区: */
DBMS_LOB.CREATETEMPORARY(Temp_clob,TRUE);
SELECT Story INTO Lob_loc
FROM Multimedia_tab
WHERE Clip_ID = 1;
DBMS_LOB.OPEN(Temp_clob,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(Lob_loc,DBMS_LOB.LOB_READONLY);
/* 拷贝 CLOB 到临时 CLOB: */
DBMS_LOB.COPY(Temp_clob,Lob_loc,DBMS_LOB.GETLENGTH(Lob_loc),1,1);
/* 查找模式 CLOB: */
Position :=DBMS_LOB.INSTR(Temp_clob, Pattern, Offset, Occurrence);
IF Position = 0 THEN
DBMS_OUTPUT.PUT_LINE(’Pattern not found’);
ELSE
DBMS_OUTPUT.PUT_LINE(’The pattern occurs at ’|| position);
END IF;
DBMS_LOB.CLOSE(Lob_loc);
DBMS_LOB.CLOSE(Temp_clob);
/* 释放临时 LOB: */
DBMS_LOB.FREETEMPORARY(Temp_clob);
END;
§27.3.9 READ过程
READ过程返回给定的偏移量开始的LOB的部分数据。如果在操作中遇到了LOB的结尾处,则amount返回值为0 。READ过程的语法如下:
PROCEDURE READ ( lob_loc IN BLOB,
Amount IN OUT BINARY_INTEGER,
Offset IN INTEGER,
Buffer OUT RAW );
PROCEDURE READ ( lob_loc IN CLOB CHARARCTER SET ANY_CS,
Amount IN OUT BINARY_INTEGER,
Offset IN INTEGER,
Buffer OUT VARCHAR2 );
PROCEDURE READ ( lob_loc IN BFILE,
Amount IN OUT BINARY_INTEGER,
Offset IN INTEGER,
Buffer OUT RAW );
其中:
lob_loc LOB位置。
Amount 要读的字节或字符数。
Offset 读操作使用的lob_loc 中的字节或字符数。
Buffer 接收输出缓冲区的大小。
例子:
CREATE OR REPLACE PROCEDURE checkOutLOB_proc IS
Lob_loc CLOB;
Buffer VARCHAR2(32767);
Amount BINARY_INTEGER := 32767;
Position INTEGER := 2147483647;
BEGIN
/* 查询 LOB */
SELECT Intab.Transcript INTO Lob_loc
FROM TABLE(SELECT Mtab.InSeg_ntab FROMMultimedia_tab Mtab
WHERE Mtab.Clip_ID = 1) Intab
WHERE Intab.Segment = 1;
/* 打开LOB */
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READONLY);
LOOP
DBMS_LOB.READ(Lob_loc, Amount, Position, Buffer);
/* 处理缓冲区 */
Position := Position + Amount;
END LOOP;
/* 关闭LOB */
DBMS_LOB.CLOSE(Lob_loc);
§27.3.10 SUBSTR函数
SUBSTR函数返回给定的LOB的部分数据。SUNSTR函数的语法如下:
PROCEDURE SUBSTR ( lob_loc IN BLOB,
Amount IN INTEGER:= 32767,
Offset IN INTEGER := 1 )
RETURN RAW;
PROCEDURE SUBSTR ( lob_loc IN CLOBCHARACTER SET ANY_CS,
Amount IN INTEGER := 32767,
Offset IN INTEGER := 1 );
RETURN VARCHAR2;
例.读LOB的一部分:
CREATE OR REPLACE PROCEDURE substringLOB_proc IS
Lob_loc BLOB;
Amount BINARY_INTEGER := 32767;
Position INTEGER := 1024;
Buffer RAW(32767);
BEGIN
/* 查询 LOB: */
SELECT Sound INTO Lob_loc FROM Multimedia_tab
WHERE Clip_ID = 1;
/* 打开 LOB : */
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READONLY);
Buffer := DBMS_LOB.SUBSTR(Lob_loc, Amount,Position);
/* 处理数据,强制关闭 LOB */
DBMS_LOB.CLOSE (Lob_loc);
END;
/* 下面语句: 255 要读的总数,1是开始偏移量*/
SELECT DBMS_LOB.SUBSTR(Sound, 255, 1) FROMMultimedia_tab WHERE Clip_ID = 1;
§27.3.11 WRITE过程
WRITE过程用于将数据写到LOB中。它的执行结果可将LOB的某段数据覆盖。如果指定的偏移量超过目标LOB的长度,则将插入空格或0字节来代替。WRITE过程的语法如下:
PROCEDURE WRITE ( lob_loc IN OUT BLOB,
Amount IN BINARY_INTEGER ,
Offset IN INTEGER,
Buffer IN RAW
);
PROCEDURE WRITE ( lob_loc IN OUT CLOB CHARACTER SET ANY_CS,
Amount IN BINARY_INTEGER ,
Offset IN INTEGER,
Buffer IN VARCHAR2
);
其中:lob_loc 要写入的LOB数据。
Amount 写入的字节数或字符数(应该大于或等于buffer的长度).
Offset 从1开始的偏移量(字节数或字符数)。
Buffer 写目标LOB的数据。
例子.检查LOB:
CREATE OR REPLACE PROCEDURE checkInLOB_proc IS
Lob_loc CLOB;
Buffer VARCHAR2(32767);
Amount BINARY_INTEGER := 32767;
Position INTEGER := 2147483647;
i INTEGER;
BEGIN
/* 查询 LOB: */
SELECT Intab.Transcript INTO Lob_loc
FROM TABLE(SELECT Mtab.InSeg_ntab FROMMultimedia_tab Mtab
WHERE Clip_ID = 2) Intab
WHERE Intab.Segment = 1
FOR UPDATE;
/* 打开 LOB : */
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READWRITE)
FOR i IN 1..3 LOOP
/* 用数据填缓冲区以便可写 */
DBMS_LOB.WRITE(Lob_loc, Amount, Position, Buffer);
Position := Position + Amount;
END LOOP;
/* 关闭 LOB : */
DBMS_LOB.CLOSE (Lob_loc);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(’Operation failed’);
END;
§27.3.12 WRITEAPPEND过程
WRITEAPPEND过程用于将数据写到LOB的结尾处。语法如下:
PROCEDURE WRITEAPPEND ( lob_loc IN OUT BLOB,
Amount IN BINARY_INTEGER ,
Buffer IN RAW
);
PROCEDURE WRITE ( lob_loc IN OUT CLOB CHARACTER SET ANY_CS,
Amount IN BINARY_INTEGER ,
Buffer IN VARCHAR2
);
其中:
lob_loc 要写入的LOB数据。
Amount 写入的字节数或字符数(应该大于或等于buffer的长度).
Buffer 写目标LOB的数据。
例子.写附加数据到LOB
CREATE OR REPLACE PROCEDURE lobWriteAppend_procIS
Lob_loc BLOB;
Buffer RAW(32767);
Amount Binary_integer := 32767;
BEGIN
SELECT Frame INTO Lob_loc FROM Multimedia_tab
where Clip_ID = 1 FOR UPDATE;
/* 用数据填充缓冲区... ,打开LOB*/
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READWRITE);
/* 从缓冲区将数据加到LOB的尾部 */
DBMS_LOB.WRITEAPPEND(Lob_loc,Amount, Buffer);
/* 关闭 LOB: */
DBMS_LOB.CLOSE(Lob_loc);
END;
§27.4临时LOB
与前介绍的永久的LOB不同,临时的LOB就是指将数据建立在用户临时表空间内,这些数据仅在运行期内存在。
临时LOB的操作可以是PL/SQL语言,也可以是OCI、PRO*C等。下面介绍PL/SQL的一些例子。
§27.4.1 建立临时LOB
可以用CREATETEMPORARY 过程创建和初始化并返回LOB的定位指针。
PROCEDURE CREATETEMPORARY
( lob_loc IN OUT BLOB,
cache IN BOOLEAN ,
dur IN PLS_INTEGER :=DBMS_LOB.SESSION
);
PROCEDURE CREATETEMPORARY
( lob_loc IN OUT CLOB CHARACTER SET ANY_CS,,
cache IN BOOLEAN ,
dur IN PLS_INTEGER :=DBMS_LOB.SESSION
);
其中:
lob_loc 指向临时LOB的定位指针。
Cache TRUE,则LOB数据读入缓冲区。
Dur 可以是call、transaction或session。
例子。 创建一临时LOB:
DECLARE
Dest_loc BLOB;
Src_loc BLOB;
Amount INTEGER := 4000;
BEGIN
SELECT Frame INTO Src_loc FROM Multimedia_tab WHERE Clip_ID = 1;
/* 建立临时 LOB: */
DBMS_LOB.CREATETEMPORARY(Dest_loc,TRUE);
/* 拷贝正确框架Src_loc 到建立临时 LOB: */
DBMS_LOB.COPY(Dest_loc,Src_loc,DBMS_LOB.GETLENGTH(Src_loc),1,1);
DBMS_LOB.FREETEMPORARY(Dest_loc);
END;
§27.4.2 查看临时LOB
可以用DBMS_LOB.ISTEMPORARY函数来返回LOB是否是临时的。语法如下:
DBMS_LOB.ISTEMPORARY (
lob_loc IN BLOB)
RETURN INTEGER;
DBMS_LOB.ISTEMPORARY (
lob_loc IN CLOB CHARACTER SET ANY_CS)
RETURN INTEGER;
其中:
lob_loc LOB的位置。
返回的是布尔值。
例.看LOB是否是临时
CREATE or REPLACE PROCEDUREfreeTempLob_proc(Lob_loc IN OUT BLOB) IS
BEGIN
/* 释放临时 LOB 指针*/
/* 第1个检查是确定指针是否是临时LOB */
IFDBMS_LOB.ISTEMPORARY(Lob_loc) = 1 THEN
/* 释放临时 LOB 指针*/
DBMS_LOB.FREETEMPORARY(Lob_loc);
DBMS_OUTPUT.PUT_LINE(’ temporary LOB was freed’);
ELSE
/* 显示错误: */
DBMS_OUTPUT.PUT_LINE(
’Locator passed in was not a temporary LOB locator’);
END IF;
END;
§27.4.3 释放临时LOB
例子:
DECLARE
Dest_loc BLOB;
Src_loc BFILE := BFILENAME(’AUDIO_DIR’,’Washington_audio’);
Amount INTEGER := 4000;
BEGIN
DBMS_LOB.CREATETEMPORARY(Dest_loc,TRUE);
/* 强制打开BFILE */
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
/* 打开 LOB */
DBMS_LOB.OPEN(Dest_loc,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(Dest_loc, Src_loc, Amount);
/* 强制关闭 LOB */
DBMS_LOB.CLOSE(Src_loc);
DBMS_LOB.CLOSE(Dest_loc);
/* 释放LOB */
DBMS_LOB.FREETEMPORARY(Dest_loc);
END;
§27.4.4 从BFILE中加载临时LOB
DBMS_LOB.FREETEMPORARY (
lob_loc IN OUT NOCOPY BLOB);
DBMS_LOB.FREETEMPORARY (
lob_loc IN OUT NOCOPY CLOB CHARACTER SETANY_CS);
其中:lob_loc 是指针。
CREATE or REPLACE PROCEDUREfreeTempLob_proc(Lob_loc IN OUT BLOB) IS
BEGIN
DBMS_LOB.CREATETEMPORARY(Lob_loc,TRUE);
/* 在这里使用临时 LOB 指针 ,然后释放 */
/* 释放临时 LOB 指针:*/
DBMS_LOB.FREETEMPORARY(Lob_loc);
DBMS_OUTPUT.PUT_LINE(’Temporary LOB was freed’);
END;
§27.4.5 查看临时LOB是否打开
可以用DBMS_LOB.ISOPEN函数来得到临时LOB是否已经打开,语法如下:
DBMS_LOB.ISOPEN (
lob_loc IN BLOB)
RETURN INTEGER;
DBMS_LOB.ISOPEN (
lob_loc IN CLOB CHARACTER SET ANY_CS)
RETURN INTEGER;
DBMS_LOB.ISOPEN (
file_loc IN BFILE)
RETURN INTEGER;
其中:
lob_loc LOB指针。
File_loc 文件指针。
例子。
CREATE OR REPLACE PROCEDUREseeTempLOBIsOpen_proc(Lob_loc IN OUT BLOB,
Retval OUT INTEGER) IS
BEGIN
/* 建立临时 LOB: */
DBMS_LOB.CREATETEMPORARY(Lob_loc,TRUE);
/* See If the LOB is open: */
Retval :=DBMS_LOB.ISOPEN(Lob_loc);
/* 释放临时 LOB: */
DBMS_LOB.FREETEMPORARY(Lob_loc);
END;
§27.4.6 显示临时LOB数据
可以用DBMS_LOB.LOADFROMFILE过程来显示LOB数据,它的语法如下:
DBMS_LOB.LOADFROMFILE (
dest_lob IN OUT NOCOPY BLOB,
src_file IN BFILE,
amount IN INTEGER,
dest_offset IN INTEGER := 1,
src_offset IN INTEGER := 1);
DBMS_LOB.LOADFROMFILE(
dest_lob IN OUT NOCOPY CLOB CHARACTER SET ANY_CS,
src_file IN BFILE,
amount IN INTEGER,
dest_offset IN INTEGER := 1,
src_offset IN INTEGER := 1);
其中:
dest_lob 要加载的目标LOB 指针。
src_file 加载的源BFILE指针。
amount 加载的 BFILE的字节数。
dest_offset 目标 LOB 的字节或字符数(开始: 1)。
src_offset 从开始起的源BFILE 字节数。
例子。
DECLARE
Dest_loc BLOB;
Src_loc BFILE := BFILENAME(’AUDIO_DIR’,’Washington_audio’);
Amount INTEGER := 128;
Bbuf RAW(128);
Position INTEGER :=1;
BEGIN
DBMS_LOB.CREATETEMPORARY(Dest_loc,TRUE);
/* 强行打开 FILE : */
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
/* 打开 LOB : */
DBMS_LOB.OPEN(Dest_loc,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(Dest_loc,Src_loc,Amount);
LOOP
DBMS_LOB.READ (Dest_loc, Amount, Position,Bbuf);
/* 显示缓冲区内容 */
DBMS_OUTPUT.PUT_LINE(’Result :’||utl_raw.cast_to_varchar2(Bbuf));
Position := Position + Amount;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(’End of data loaded into tempLOB’);
DBMS_LOB.CLOSE(Dest_loc);
DBMS_LOB.FREETEMPORARY(Dest_loc);
/* 关闭文件: */
DBMS_LOB.CLOSE(Src_loc);
END;
§27.4.7 从临时LOB读数据
DBMS_LOB.READ过程用于读临时LOB的数据,它的语法如下:
DBMS_LOB.READ (
lob_loc IN BLOB,
amount IN OUT NOCOPY BINARY_INTEGER,
offset IN INTEGER,
buffer OUT RAW);
DBMS_LOB.READ (
lob_loc IN CLOB CHARACTER SET ANY_CS,
amount IN OUT NOCOPY BINARY_INTEGER,
offset IN INTEGER,
buffer OUT VARCHAR2 CHARACTER SET lob_loc%CHARSET);
DBMS_LOB.READ (
lob_loc IN BFILE,
amount IN OUT NOCOPY BINARY_INTEGER,
offset IN INTEGER,
buffer OUT RAW);
其中:
lob_loc 要读的LOB 指针。
amount 要读的字节数 (for BLOBs) 字符数(for CLOBs) 。
offset 偏移字节数 (for BLOBs) 字符数(for CLOBs) 。
buffer 输出缓冲区大小。
DECLARE
Dest_loc BLOB;
Src_loc BFILE := BFILENAME(’AUDIO_DIR’, ’Washington_audio’);
Amount INTEGER := 4000;
Bbuf RAW(32767);
Position INTEGER :=1;
BEGIN
DBMS_LOB.CREATETEMPORARY(Dest_loc,TRUE);
/* 强行打开 FILE : */
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
/* 打开LOB : */
DBMS_LOB.LOADFROMFILE(Dest_loc, Src_loc, Amount);
DBMS_LOB.READ (Dest_loc,Amount, Position, Bbuf);
/* 强行关闭 LOB : */
DBMS_LOB.CLOSE(Src_loc);
§27.4.8 从临时LOB读部分数据
可以用SUBSTR过程来实现对临时LOB的模式操作。其语法见前面。
/* 假定用户在AUDIO_DIR别名上有文件 ’Washington_audio’ */
CREATE or REPLACE PROCEDURE substringTempLOB_proc IS
Dest_loc BLOB;
Src_loc BFILE := BFILENAME(’AUDIO_DIR’, ’Washington_audio’);
Amount INTEGER := 32767;
Bbuf RAW(32767);
Position INTEGER :=128;
BEGIN
DBMS_LOB.CREATETEMPORARY(Dest_loc,TRUE);
/* 强行打开 FILE文件: */
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
/* 选择打开 LOB */
DBMS_LOB.OPEN(Dest_loc,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(Dest_loc, Src_loc, Amount);
Bbuf :=DBMS_LOB.SUBSTR(Dest_loc, Amount, Position);
/* 强行关闭 LOB : */
DBMS_LOB.CLOSE(Src_loc);
DBMS_LOB.CLOSE(Dest_loc);
END;
§27.4.9 比较两个临时LOB数据
DBMS_LOB.CREATETEMPORARY过程用来比较两个LOB的数据,看下面例子。
CREATE OR REPLACE PROCEDURE compareTwoTmpPerLOBs_proc IS
Lob_loc1 BLOB;
Lob_loc2 BLOB;
Temp_loc BLOB;
Amount INTEGER := 32767;
Retval INTEGER;
BEGIN
/* 选择 LOB: */
SELECT Frame INTO Lob_loc1 FROM Multimedia_tab
WHERE Clip_ID = 1;
SELECT Frame INTO Lob_loc2 FROM Multimedia_tab
WHERE Clip_ID = 2;
/* 拷贝框架到临时 LOB 并覆盖不同的部分 */
/* 比较前要做的工作 */
DBMS_LOB.CREATETEMPORARY(Temp_loc,TRUE);
DBMS_LOB.OPEN(Temp_loc, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(Lob_loc1, DBMS_LOB.LOB_READONLY);
DBMS_LOB.OPEN(Lob_loc2, DBMS_LOB.LOB_READONLY);
/* 拷贝固定 LOB 到临时 LOB: */
DBMS_LOB.COPY(Temp_loc,Lob_loc2,DBMS_LOB.GETLENGTH(Lob_loc2),1,1);
/* 在比较前,转换临时 LOB */
retval :=DBMS_LOB.COMPARE(Lob_loc1, Temp_loc, Amount, 1, 1);
IF retval = 0 THEN
DBMS_OUTPUT.PUT_LINE(’Processing for equal frames’);
ELSE
DBMS_OUTPUT.PUT_LINE(’Processing for non-equal frames’);
END IF;
DBMS_LOB.CLOSE(Temp_loc);
DBMS_LOB.CLOSE(Lob_loc1);
DBMS_LOB.CLOSE(Lob_loc2);
/* 释放临时 LOB */
DBMS_LOB.FREETEMPORARY(Temp_loc);
END;
§27.4.10 查看临时LOB模式的存在
CREATE OR REPLACE PROCEDURE instringTempLOB_proc IS
Lob_loc CLOB;
Temp_clob CLOB;
Pattern VARCHAR2(30) := ’children’; Position INTEGER := 0;
Offset INTEGER := 1;
Occurrence INTEGER := 1;
BEGIN
/* 建立临时 LOB 和拷贝 CLOB */
DBMS_LOB.CREATETEMPORARY(Temp_clob,TRUE);
SELECT Story INTO Lob_loc
FROM Multimedia_tab
WHERE Clip_ID = 1;
DBMS_LOB.OPEN(Temp_clob,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(Lob_loc,DBMS_LOB.LOB_READONLY);
/* 拷贝CLOB到临时 CLOB: */
DBMS_LOB.COPY(Temp_clob,Lob_loc,DBMS_LOB.GETLENGTH(Lob_loc),1,1);
/* 在临时CLOB找模式 */
Position := DBMS_LOB.INSTR(Temp_clob,Pattern, Offset, Occurrence);
IF Position = 0 THEN
DBMS_OUTPUT.PUT_LINE(’Pattern not found’);
ELSE
DBMS_OUTPUT.PUT_LINE(’The pattern occurs at ’|| position);
END IF;
DBMS_LOB.CLOSE(Lob_loc);
DBMS_LOB.CLOSE(Temp_clob);
/*释放临时LOB: */
DBMS_LOB.FREETEMPORARY(Temp_clob);
END;
§27.4.11 得到临时LOB的长度
CREATE OR REPLACE PROCEDUREgetLengthTempCLOB_proc IS
Length INTEGER;
tlob CLOB;
bufc VARCHAR2(8);
Amount NUMBER;
pos NUMBER;
Src_loc BFILE := BFILENAME(’AUDIO_DIR’,’Washington_audio’);
BEGIN
DBMS_LOB.CREATETEMPORARY(tlob,TRUE);
/* 打开LOB */
DBMS_LOB.OPEN(tlob,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
Amount := 32767;
DBMS_LOB.LOADFROMFILE(tlob, Src_loc, Amount);
/* 得到LOB长度*/
length := DBMS_LOB.GETLENGTH(tlob);
IF length = 0 THEN
DBMS_OUTPUT.PUT_LINE(’LOB is empty.’);
ELSE
DBMS_OUTPUT.PUT_LINE(’The length is ’ ||length);
END IF;
/* 关闭已经打开的LOB */
DBMS_LOB.CLOSE(tlob);
DBMS_LOB.CLOSE(Src_loc);
/* 释放LOB */
DBMS_LOB.FREETEMPORARY(tlob);
END;
§27.4.12 拷贝部分临时LOB数据
/* 注意:存储过程 copyTempLOB_proc不是DBMS_LOB 包的一部分 */
CREATE OR REPLACE PROCEDURE copyTempLOB_proc IS
Dest_pos NUMBER;
Src_pos NUMBER;
Dest_loc BLOB;
Dest_loc2 BLOB;
Src_loc BFILE := BFILENAME(’AUDIO_DIR’,’Washington_audio’);
Amount INTEGER := 32767;
BEGIN
DBMS_LOB.CREATETEMPORARY(Dest_loc2,TRUE);
DBMS_LOB.CREATETEMPORARY(Dest_loc,TRUE);
/* 强制打开 FILE */
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
/* 打开临时 LOB */
DBMS_LOB.OPEN(Dest_loc,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(Dest_loc2,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(Dest_loc, Src_loc,Amount);
/* 设置 目标临时 LOB的 Dest_pos 位置 */
/* 从源拷贝LOB到目标位置*/
/* 设置希望拷贝的数量 */
Amount := 328;
Dest_pos := 1000;
Src_pos := 1000;
DBMS_LOB.COPY(Dest_loc2,Dest_loc, Amount, Dest_pos, Src_pos);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(’Operation failed’);
DBMS_LOB.CLOSE(Dest_loc);
DBMS_LOB.CLOSE(Dest_loc2);
DBMS_LOB.CLOSE(Src_loc);
DBMS_LOB.FREETEMPORARY(Dest_loc);
DBMS_LOB.FREETEMPORARY(Dest_loc2);
END;
§27.4.13 为临时LOB拷贝位置
/* 注意:存储过程 copyTempLOBLocator_proc不是DBMS_LOB 包的一部分 */
CREATE OR REPLACE PROCEDUREcopyTempLOBLocator_proc(
Lob_loc1 IN OUT CLOB, Lob_loc2 IN OUT CLOB) IS
bufp VARCHAR2(4);
Amount NUMBER := 32767;
Src_loc BFILE := BFILENAME(’AUDIO_DIR’,’Washington_audio’);
BEGIN
DBMS_LOB.CREATETEMPORARY(Lob_loc1,TRUE);
DBMS_LOB.CREATETEMPORARY(Lob_loc2,TRUE);
/* 强行打开 */
DBMS_LOB.OPEN(Src_loc,DBMS_LOB.LOB_READONLY);
/* 打开LOB */
DBMS_LOB.OPEN(Lob_loc1,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(Lob_loc2,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(Lob_loc1,Src_loc,Amount);
/* 分配 Lob_loc1给Lob_loc2以建立一个拷贝值 */
Lob_loc2 := Lob_loc1;
/* 强行关闭 LOB */
DBMS_LOB.CLOSE (Src_loc);
DBMS_LOB.CLOSE (Lob_loc1);
DBMS_LOB.CLOSE (Lob_loc2);
DBMS_LOB.FREETEMPORARY(Lob_loc1);
DBMS_LOB.FREETEMPORARY(Lob_loc2);
END;
§27.4.14 加临时LOB到另外的LOB
/* 注意:存储过程 appendTempLOB_proc 不是DBMS_LOB 包的一部分 */
CREATE OR REPLACE PROCEDURE appendTempLOB_proc IS
Dest_loc2 CLOB;
Dest_loc CLOB;
Amount NUMBER;
Src_loc BFILE := BFILENAME(’AUDIO_DIR’, ’Washington_audio’);
BEGIN
DBMS_LOB.CREATETEMPORARY(Dest_loc,TRUE);
DBMS_LOB.CREATETEMPORARY(Dest_loc2,TRUE);
DBMS_LOB.OPEN(Dest_loc,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(Dest_loc2,DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(Src_loc,DBMS_LOB.LOB_READWRITE);
Amount := 32767;
DBMS_LOB.LOADFROMFILE(Dest_loc, Src_loc, Amount);
DBMS_LOB.LOADFROMFILE(Dest_loc2, Src_loc, Amount);
DBMS_LOB.APPEND(Dest_loc, Dest_loc2);
DBMS_LOB.CLOSE(Dest_loc);
DBMS_LOB.CLOSE(Dest_loc2);
DBMS_LOB.CLOSE(Src_loc);
DBMS_LOB.FREETEMPORARY(Dest_loc);
DBMS_LOB.FREETEMPORARY(Dest_loc2);
END;
§27.4.15 写追加到临时LOB
/* 注意:存储过程writeAppendTempLOB_proc不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDUREwriteAppendTempLOB_proc IS
Lob_loc BLOB;
Buffer RAW(32767);
Src_loc BFILE := BFILENAME(’AUDIO_DIR’,’Washington_audio’);
Amount Binary_integer := 32767;
Position Binary_integer := 128;
BEGIN
DBMS_LOB.CREATETEMPORARY(Lob_loc,TRUE);
/* 打开 LOB */
DBMS_LOB.OPEN(Lob_loc,DBMS_LOB.LOB_READWRITE);
/* 强行打开 FILE */
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
/* 用 数据填缓冲区 */
DBMS_LOB.LOADFROMFILE (Lob_loc,Src_loc, Amount);
/* 从缓冲区加数据到LOB的尾部*/
DBMS_LOB.WRITEAPPEND(Lob_loc, Amount, Buffer);
DBMS_LOB.CLOSE(Src_loc);
DBMS_LOB.CLOSE(Lob_loc);
DBMS_LOB.FREETEMPORARY(Lob_loc);
END;
§27.4.16 写数据到临时LOB
/* 注意:存储过程writeDataToTempLOB_proc不是DBMS_LOB包的一部分 */
CREATE or REPLACE PROCEDURE writeDataToTempLOB_proc IS
Lob_loc CLOB;
Buffer VARCHAR2(26);
Amount BINARY_INTEGER := 26;
Position INTEGER := 1;
i INTEGER;
BEGIN
DBMS_LOB.CREATETEMPORARY(Lob_loc,TRUE);
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READWRITE);
/* 用数据填缓冲区以写到LOB: */
Buffer := ’abcdefghijklmnopqrstuvwxyz’;
FOR i IN 1..3 LOOP
DBMS_LOB.WRITE (Lob_loc, Amount, Position, Buffer);
/* 用更多数据填缓冲区以写到LOB: */
Position := Position + Amount;
END LOOP;
/* 强行关闭 LOB */
DBMS_LOB.CLOSE (Lob_loc);
DBMS_LOB.FREETEMPORARY(Lob_loc);
END;
§27.4.17 修理临时LOB数据
/* 注意:存储过程 trimTempLOB_proc 不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE trimTempLOB_proc IS
Lob_loc CLOB;
Amount number;
Src_loc BFILE := BFILENAME(’AUDIO_DIR’, ’Washington_audio’);
TrimAmount number := 100;
BEGIN
/* 建立临时 LOB: */
DBMS_LOB.CREATETEMPORARY(Lob_loc,TRUE);
/* 打开 LOB */
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READWRITE);
/* 强行打开文件 */
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
Amount := 32767;
DBMS_LOB.LOADFROMFILE(Lob_loc, Src_loc, Amount);
DBMS_LOB.TRIM(Lob_loc,TrimAmount);
DBMS_LOB.CLOSE (Lob_loc);
DBMS_LOB.CLOSE(Src_loc);
DBMS_LOB.FREETEMPORARY(Lob_loc);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(’Operation failed’);
END;
§27.4.18 删除临时LOB数据
/* 注意:存储过程 eraseTempLOB_proc 不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE trimTempLOB_proc IS
Lob_loc CLOB;
amt number;
Src_loc BFILE := BFILENAME(’AUDIO_DIR’, ’Washington_audio’);
Amount INTEGER := 32767;
BEGIN
/* 建立临时 LOB: */
DBMS_LOB.CREATETEMPORARY(Lob_loc,TRUE);
/* 打开LOB */
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READWRITE);
Amount := 32767;
DBMS_LOB.LOADFROMFILE(Lob_loc, Src_loc, Amount);
/* 删除 LOB 数据*/
amt := 3000;
DBMS_LOB.ERASE(Lob_loc, amt, 2);
DBMS_LOB.CLOSE (Lob_loc);
DBMS_LOB.CLOSE(Src_loc);
DBMS_LOB.FREETEMPORARY(Lob_loc);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(’Operation failed’);
END;
§27.5外部LOB (BFILE)
外边LOB指的是 BFILE类型,它的使用方式与CLOB等不同,BFILE类型对象只存放文件的路径和文件名,而不存放具体的文件内容。
§27.5.1 BFILE目录指定
可以用CREATE DIRECTORY命令来实现建立目录,如:
在UNIX 环境:
CREATE DIRECTORY "Mary_Dir" AS '/usr/home/mary';
在NT环境:
CREATE DIRECTORY "big_cap_dir" AS"g:\data\source";
CREATEDIRECTORY "small_cap_dir" AS "G:\DATA\SOURCE";
§27.5.2 建立包括BFILE列的表
在建立前,要登录到system帐户并执行下面的脚本:
CONNECT system/manager;
DROP USER samp CASCADE;
DROP DIRECTORY AUDIO_DIR;
DROP DIRECTORY FRAME_DIR;
DROP DIRECTORY PHOTO_DIR;
CREATE USER samp identified by samp;
GRANT CONNECT, RESOURCE to samp;
CREATE DIRECTORY AUDIO_DIR AS ’/tmp/’;
CREATE DIRECTORY FRAME_DIR AS ’/tmp/’;
CREATE DIRECTORY PHOTO_DIR AS ’/tmp/’;
GRANT READ ON DIRECTORY AUDIO_DIR to samp;
GRANT READ ON DIRECTORY FRAME_DIR to samp;
GRANT READ ON DIRECTORY PHOTO_DIR to samp;
CREATE TABLE VoiceoverLib_tab of Voiced_typ
( Script DEFAULT EMPTY_CLOB(),
CONSTRAINT TakeLib CHECK (Take IS NOT NULL),
Recording DEFAULT NULL
);
连接到新建的帐户,执行建立带LOB的表结构命令:
CONNECT samp/samp
CREATE TABLE a_table (blob_col BLOB);
CREATE TYPE Voiced_typ AS OBJECT
( Originator VARCHAR2(30),
Script CLOB,
Actor VARCHAR2(30),
Take NUMBER,
Recording BFILE );
CREATE TYPE InSeg_typ AS OBJECT
( Segment NUMBER,
Interview_Date DATE,
Interviewer VARCHAR2(30),
Interviewee VARCHAR2(30),
Recording BFILE,
Transcript CLOB );
CREATE TYPE InSeg_tab AS TABLE of InSeg_typ;
CREATE TYPE Map_typ AS OBJECT
( Region VARCHAR2(30),
NW NUMBER,
NE NUMBER,
SW NUMBER,
SE NUMBER,
Drawing BLOB,
Aerial BFILE);
CREATE TABLE Map_Libtab of Map_typ;
CREATE TABLE Voiceover_tab of Voiced_typ
( Script DEFAULT EMPTY_CLOB(),
CONSTRAINT Take CHECK (Take IS NOT NULL),
Recording DEFAULT NULL);
可直接用DDL语句建立表:
CREATE TABLE Multimedia_tab
( Clip_ID NUMBER NOT NULL,
Story CLOB default EMPTY_CLOB(),
FLSub NCLOB default EMPTY_CLOB(),
Photo BFILE default NULL,
Frame BLOB default EMPTY_BLOB(),
Sound BLOB default EMPTY_BLOB(),
Voiced_ref REF Voiced_typ,
InSeg_ntab InSeg_tab,
Music BFILE default NULL,
Map_obj Map_typ
) NESTEDTABLE InSeg_ntab STORE AS InSeg_nestedtab;
§27.5.3 用BFILENAME()插入数据
/* 注:这与插入内部的LOB一样,但采用BFILENAME()函数来实现初始化一个BFULE列 */
INSERT INTO Multimedia_tab VALUES (1,EMPTY_CLOB(), EMPTY_CLOB(),
FILENAME(’PHOTO_DIR’, ’LINCOLN_PHOTO’),
EMPTY_BLOB(), EMPTY_BLOB(),
VOICED_TYP(’Abraham Lincoln’,EMPTY_CLOB(),’James Earl Jones’, 1, NULL),
NULL, BFILENAME(’AUDIO_DIR’,’LINCOLN_AUDIO’),
MAP_TYP(’Gettysburg’, 23, 34, 45,56,EMPTY_BLOB(), NULL));
§27.5.4 从另外表选择BFILE插入数据
INSERT INTO Voiceover_tab
(SELECT * from VoiceoverLib_tab
WHERE Take = 12345);
§27.5.5 用初始化BFILE位置来插入数据BFILE行
DECLARE
/* 初始化 BFILE 位置*/
Lob_loc BFILE := BFILENAME(’PHOTO_DIR’,’Washington_photo’);
BEGIN
INSERT INTO Multimedia_tab (Clip_ID, Photo)VALUES (3, Lob_loc);
COMMIT;
END;
§27.5.6 动态加载数据BFILE的表
例1:
控制文件:
LOAD DATA
INFILE sample9.dat
INTO TABLE Multimedia_tab
FIELDS TERMINATED BY ’,’
(Clip_ID INTEGER EXTERNAL(5),
FileName FILLER CHAR(30),
Photo BFILE(CONSTANT"DETECTIVE_PHOTO", FileName))
数据文件:
007, JamesBond.jpeg,
008, SherlockHolmes.jpeg,
009, MissMarple.jpeg,
例2.文件名和目录是动态。
控制文件:
LOAD DATA
INFILE sample10.dat
INTO TABLE Multimedia_tab
replace
FIELDS TERMINATED BY ','
(
Clip_ID INTEGER EXTERNAL(5),
Photo BFILE (DirName, FileName),
FileName FILLER CHAR(30),
DirName FILLER CHAR(30)
)
数据文件:
007,JamesBond.jpeg,DETECTIVE_PHOTO,
008,SherlockHolmes.jpeg,DETECTIVE_PHOTO,
009,MissMarple.jpeg,PHOTO_DIR,
§27.5.7 用BFILE数据来加载LOB数据
/* 注:本存储过程loadLOBFromBFILE_proc不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDUREloadLOBFromBFILE_proc IS
Dest_loc BLOB;
Src_loc BFILE := BFILENAME(’FRAME_DIR’,’Washington_frame’);
Amount INTEGER := 4000;
BEGIN
SELECT Frame INTO Dest_loc FROM Multimedia_tab
WHERE Clip_ID = 3
FOR UPDATE;
/* 强行打开 LOB */
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
/* 打开 LOB */
DBMS_LOB.OPEN(Dest_loc, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(Dest_loc, Src_loc,Amount);
/* 强行关闭 LOB */
DBMS_LOB.CLOSE(Dest_loc);
DBMS_LOB.CLOSE(Src_loc);
COMMIT;
END;
§27.5.8 用FILEOPEN打开BFILE
/* 注:本存储过程openBFILE_procOne不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE openBFILE_procOne IS
Lob_loc BFILE := BFILENAME(’PHOTO_DIR’,’Lincoln_photo’);
BEGIN
/* 打开 BFILE */
DBMS_LOB.FILEOPEN (Lob_loc,DBMS_LOB.FILE_READONLY);
/* ... 做某些处理 */
DBMS_LOB.FILECLOSE(Lob_loc);
END;
§27.5.9 用OPEN打开BFILE
/* 注:本存储过程openBFILE_procTwo不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE openBFILE_procTwo IS
Lob_loc BFILE := BFILENAME(’PHOTO_DIR’,’Lincoln_photo’);
BEGIN
/* 打开BFILE: */
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READONLY);
/* ... 进行其他处理*/
DBMS_LOB.CLOSE(Lob_loc);
END;
§27.5.10 用FILEISOPEN看BFILE是否打开
/* 注:本存储过程seeIfOpenBFILE_procOne不是 DBMS_LOB包的一部分*/
CREATE OR REPLACE PROCEDUREseeIfOpenBFILE_procOne IS
Lob_loc BFILE;
RetVal INTEGER;
BEGIN
/* 选择 LOB,初始化BFILE位置*/
SELECT Music INTO Lob_loc FROM Multimedia_tab
WHERE Clip_ID = 3;
RetVal := DBMS_LOB.FILEISOPEN(Lob_loc);
IF (RetVal = 1)
THEN
§27.5.11 用ISOPEN看BFILE是否打开
/* 注:本存储过程seeIfOpenBFILE_procTwo不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE seeIfOpenBFILE_procTwoIS
Lob_loc BFILE;
RetVal INTEGER;
BEGIN
/* 选择 LOB,初始化BFILE位置*/
SELECT Music INTO Lob_loc FROM Multimedia_tab
WHERE Clip_ID = 3;
RetVal := DBMS_LOB.ISOPEN(Lob_loc);
IF (RetVal = 1)
THEN
DBMS_OUTPUT.PUT_LINE(’File is open’);
ELSE
DBMS_OUTPUT.PUT_LINE(’File is not open’);
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(’Operation failed’);
END;
§27.5.12 显示BFILE
/* 注:本存储过程displayBFILE_proc不是 DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE displayBFILE_proc IS
Lob_loc BFILE;
Buffer RAW(1024);
Amount BINARY_INTEGER := 1024;
Position INTEGER := 1;
BEGIN
/* 查询 LOB */
SELECT Music INTO Lob_loc
FROM Multimedia_tab WHERE Clip_ID = 1;
/* 打开 BFILE */
DBMS_LOB.OPEN (Lob_loc, DBMS_LOB.LOB_READONLY);
LOOP
DBMS_LOB.READ (Lob_loc, Amount, Position,Buffer);
/* 显示缓冲区内容 */
DBMS_OUTPUT.PUT_LINE(utl_raw.cast_to_varchar2(Buffer));
Position := Position + Amount;
END LOOP;
/* 关闭 BFILE */
DBMS_LOB.CLOSE (Lob_loc);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(’End of data’);
END;
§27.5.13 从BFILE中读数据
/* 注:本存储过程readBFILE_proc不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE readBFILE_proc IS
Lob_loc BFILE := BFILENAME(’PHOTO_DIR’, ’Jefferson_photo’);
Amount INTEGER := 32767;
Position INTEGER := 1;
Buffer RAW(32767);
BEGIN
/* 查询 LOB */
SELECT Photo INTO Lob_loc FROM Multimedia_tab
WHERE Clip_ID = 3;
/* 打开BFIL */
DBMS_LOB.OPEN(Lob_loc, DBMS_LOB.LOB_READONLY);
/* 读数据 */
DBMS_LOB.READ(Lob_loc, Amount, Position, Buffer);
/* 关闭 BFILE */
DBMS_LOB.CLOSE(Lob_loc);
END;
§27.5.14 读部分BFILE
/* 注:本存储过程substringBFILE_proc不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE substringBFILE_procIS
Lob_loc BFILE;
Position INTEGER := 1;
Buffer RAW(32767);
BEGIN
/* 查询 LOB */
SELECT Mtab.Voiced_ref.Recording INTO Lob_locFROM Multimedia_tab Mtab
WHERE Mtab.Clip_ID = 3;
/* 打开BFILE */
DBMS_LOB.OPEN(Lob_loc, DBMS_LOB.LOB_READONLY);
Buffer := DBMS_LOB.SUBSTR(Lob_loc, 255,Position);
/* 关闭BFILE */
DBMS_LOB.CLOSE(Lob_loc);
END;
§27.5.15 比较BFILE
/* 注:本存储过程instringBFILE_proc不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE instringBFILE_procIS
Lob_loc BFILE;
Pattern RAW(32767);
Position INTEGER;
BEGIN
/* 查询LOB */
SELECT Intab.Recording INTO Lob_loc
FROM THE(SELECT Mtab.InSeg_ntab FROMMultimedia_tab Mtab
WHERE Clip_ID = 3) Intab
WHERE Segment = 1;
/* 打开 BFILE */
DBMS_LOB.OPEN(Lob_loc, DBMS_LOB.LOB_READONLY);
/* 初始化模式,从BFILE中找出第2个出现的模式 */
Position := DBMS_LOB.INSTR(Lob_loc, Pattern, 1,2);
/* 关闭 BFILE */
DBMS_LOB.CLOSE(Lob_loc);
END;
§27.5.16 判断BFILE是否存在模式
/* 注:本存储过程compareBFILEs_proc不是 DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE compareBFILEs_procIS
/* Initialize the BFILE locator: */
Lob_loc1 BFILE := BFILENAME(’PHOTO_DIR’,’RooseveltFDR_photo’);
Lob_loc2 BFILE;
Retval INTEGER;
BEGIN
/* 选择 LOB */
SELECT Photo INTO Lob_loc2 FROM Multimedia_tab
WHERE Clip_ID = 3;
/* 打开 BFILE */
DBMS_LOB.OPEN(Lob_loc1, DBMS_LOB.LOB_READONLY);
DBMS_LOB.OPEN(Lob_loc2, DBMS_LOB.LOB_READONLY);
Retval := DBMS_LOB.COMPARE(Lob_loc2, Lob_loc1,DBMS_LOB.LOBMAXSIZE, 1, 1);
/* 关闭BFILE */
DBMS_LOB.CLOSE(Lob_loc1);
DBMS_LOB.CLOSE(Lob_loc2);
END;
§27.5.17 判断BFILE是否存在
/* 注:本存储过程seeIfExistsBFILE_proc不是 DBMS_LOB包的一部分*/
CREATE OR REPLACE PROCEDUREseeIfExistsBFILE_proc IS
Lob_loc BFILE;
BEGIN
/* 查询 LOB: */
SELECT Intab.Recording INTO Lob_loc
FROM THE(SELECT Mtab.InSeg_ntab FROMMultimedia_tab Mtab
WHERE Mtab.Clip_ID = 3) Intab
WHERE Intab.Segment = 1;
/* 检查 BFILE 存在*/
IF (DBMS_LOB.FILEEXISTS(Lob_loc) != 0)
THEN
DBMS_OUTPUT.PUT_LINE(’Processing given that theBFILE exists’);
ELSE
DBMS_OUTPUT.PUT_LINE(’Processing given that theBFILE does not exist’);
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(’Operation failed’);
END;
§27.5.18 得到BFILE长度
/* 注:本存储过程getLengthBFILE_proc不是 DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE getLengthBFILE_procIS
Lob_loc BFILE;
Length INTEGER;
BEGIN
/* 选择LOB来初始化 BFILE 位置 */
SELECT Mtab.Voiced_ref.Recording INTO Lob_locFROM Multimedia_tab Mtab
WHERE Mtab.Clip_ID = 3;
/* 打开 BFILE: */
DBMS_LOB.OPEN(Lob_loc, DBMS_LOB.LOB_READONLY);
/* 得到LOB: */
Length := DBMS_LOB.GETLENGTH(Lob_loc);
IF Length IS NULL THEN
DBMS_OUTPUT.PUT_LINE(’BFILE is null.’);
ELSE
DBMS_OUTPUT.PUT_LINE(’The length is ’ ||length);
END IF;
/* 关闭 BFILE: */
DBMS_LOB.CLOSE(Lob_loc);
END;
§27.5.19 拷贝BFILE的LOB位置
/* 注:本存储过程BFILEAssign_proc不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE BFILEAssign_proc IS
Lob_loc1 BFILE;
Lob_loc2 BFILE;
BEGIN
SELECT Photo INTO Lob_loc1
FROM Multimedia_tab WHERE Clip_ID = 3 FORUPDATE;
/* 分配 Lob_loc1 给 Lob_loc2 使它们能引用相同的操作系统文件 */
Lob_loc2 := Lob_loc1;
/* 现在可以从Lob_loc1 或Lob_loc2来读 bfile */
END;
§27.5.20 得到目录别名和文件名字
CREATE OR REPLACE PROCEDURE getNameBFILE_proc IS
Lob_loc BFILE;
DirAlias_name VARCHAR2(30);
File_name VARCHAR2(40);
BEGIN
SELECT Music INTO Lob_loc FROM Multimedia_tabWHERE Clip_ID = 3;
DBMS_LOB.FILEGETNAME(Lob_loc, DirAlias_name,File_name);
/* 在目录和别名上进行一些基本处理 */
END;
§27.5.21 用初始化BFILE位置更新BFILE
/* 注:本存储过程updateUseBindVariable_proc不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDUREupdateUseBindVariable_proc (Lob_loc BFILE) IS
BEGIN
UPDATE Multimedia_tab SET Photo = Lob_loc WHEREClip_ID = 3;
END;
DECLARE
Lob_loc BFILE;
BEGIN
SELECT Photo INTO Lob_loc
FROM Multimedia_tab
WHERE Clip_ID = 1;
updateUseBindVariable_proc (Lob_loc);
COMMIT;
END;
§27.5.22 用FILECLOSE关闭BFILE
/* 注:本存储过程closeBFILE_procOne不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE closeBFILE_procOneIS
Lob_loc BFILE := BFILENAME(’PHOTO_DIR’,’Lincoln_photo’);
BEGIN
DBMS_LOB.FILEOPEN(Lob_loc,DBMS_LOB.FILE_READONLY);
/*…做相应处理 */
DBMS_LOB.FILECLOSE(Lob_loc);
END;
§27.5.23 用CLOSE关闭BFILE
/* 注:本存储过程closeBFILE_procTwo不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDURE closeBFILE_procTwoIS
Lob_loc BFILE := BFILENAME(’PHOTO_DIR’,’Lincoln_photo’);
BEGIN
DBMS_LOB.OPEN(Lob_loc, DBMS_LOB.LOB_READONLY);
/* ...进行相应处理. */
DBMS_LOB.CLOSE(Lob_loc);
END;
§27.5.24 用CLOSEALL关闭所有BFILE
/* 注:本存储过程closeAllOpenFilesBFILE_proc不是DBMS_LOB包的一部分 */
CREATE OR REPLACE PROCEDUREcloseAllOpenFilesBFILE_proc IS
BEGIN
/* 关闭所有 BFILE文件*/
DBMS_LOB.FILECLOSEALL;
END;
§27.5.25 用DELETE等删除BFILE数据
DELETE FROM Multimedia_tab
WHERE Clip_ID = 3;
DROP
DROP TABLE Multimedia_tab;
TRUNCATE
TRUNCATE TABLE Multimedia_tab;
§27.6 使用SQL*loader加载LOB
下面给出用SQL*Loader加载LOB的一些例子,供大家参考。
§27.6.1 加载图象和文本文件
下面给出Pankaj Garg编写的加载图象和文本数据的程序。
/* 作者:Pankaj Garg,Virginia数据库分析员 */
/* 下面例子加载的图象可以达 20k,文本文件可达 4000字符 */
/* 根据情况,你可以改变参数 */
/* 对于图象: */
create table Image (imagefile_no number(4), imagefile long raw);
/* 对于文本文件: */
create table Text(textfile_no number(4), textfile long);
/* 加载图象的控制文件image.ctl如下: */
load data
infile 'Imagefile.gif' "fix 20000"
append into table Image
(ImageFile_noconstant 101,
imagefile raw(20000))
/* 加载文本文件的控制文件text.ctl 如下: */
load data
infile 'Textfile.txt' "fix 4000"
append into table Text
fields (textfile_no constant 101,
textfile char(4000))
用下面命令加载图象文件:
sqlload control=image.ctl username/password
§27.6.2 加载文本文件
例1.用定界来加载 LOB数据
控制文件
LOAD DATA
INFILE ’sample1.dat’ "str ’<endrec>\n’"
INTO TABLE Multimedia_tab
FIELDS TERMINATED BY ’,’
(
Clip_ID CHAR(3),
Story CHAR(507) ENCLOSED BY ’<startlob>’ AND ’<endlob>’
)
数据文件sample1.dat
007, <startlob> Once upon a time,The end. <endlob>|
008, <startlob> Once upon another time ....The end.<endlob>|
例2.用记录符来加载数据
控制文件
LOAD DATA
INFILE ’sample2.dat’ "str ’<endrec>\n’"
INTO TABLE Multimedia_tab
FIELDS TERMINATED BY ’,’
(
Clip_ID INTEGER EXTERNAL (3),
Story VARCHARC (3, 500)
)
数据文件sample2.dat
007,041 Once upon a time... .... The end. <endrec>
008,000 <endrec>
例3.加载一个LOB文件
控制文件
LOAD DATA
INFILE ’sample3.dat’
INTO TABLE Multimedia_tab
REPLACE
FIELDS TERMINATED BY ’,’
(
Clip_ID INTEGER EXTERNAL(5),
ext_FileName FILLER CHAR(40),
Story LOBFILE(ext_FileName) TERMINATED BY EOF
)
数据文件sample3.dat
007,FirstStory.txt,
008,/tmp/SecondStory.txt,
第2个数据文件FirstStory.txt
Once upon a time ...
The end.
第2个数据文件SecondStory.txt
Once upon another time ....
The end.
例4.加载预定大小
控制文件
LOAD DATA
INFILE ’sample4.dat’
INTO TABLE Multimedia_tab
FIELDS TERMINATED BY ’,’
(
Clip_ID INTEGER EXTERNAL(5),
Story LOBFILE (CONSTANT ’FirstStory1.txt’) CHAR(32)
)
数据文件sample4.dat
007,
008,
第2个数据文件FirstStory1.txt
Once upon the time ...
The end,
Upon another time ...
The end,
第二十八章 PL/SQL编程技巧
本章给出作者在工作常用到的一些PL/SQL编程方法,有些是作者亲自编写的程序,有些是从其它渠道得到和修改而成。希望能对PL/SQL编程有所帮助。
§28.1用触发器实现日期格式的自动设置
前面给出了如何设置NLS_DATE_FORMAT环境参数来实现日期格式的设置。许多学员都问我,在设置了NLS_DATE_FORMAT后如果退出实例再登录后,NLS_DATE_FORMAT的格式就又回到默认的格式。那么如何按照自己所习惯的格式进行设置而不影响其他用户的习惯呢?我们可以编写一个触发器来实现动态的设置。
例:用户zhao喜欢格式为“yyyy/mm/dd”;用户scott喜欢“yyyy.mm.dd”,则可以用AFTER LOGON(登录后触发)来编写程序,使得当用户登录成功后,日期格式就变为自己喜欢的格式。
/* 作者:Krishna Sarabu,修改:赵元杰 */
Create Or Replace Trigger Set_nls
AFTER LOGON
ON DATABASE
declare
c1integer;
r1integer;
BEGIN
IFSYS_CONTEXT('userenv','session_user') = 'ZHAO' THEN
C1 := dbms_sql.open_cursor();
dbms_sql.parse(c1,'ALTER session set nls_date_format='||chr(39)||'YYYY/MM/DD'||chr(39),dbms_sql.NATIVE);
r1:= dbms_sql.execute(c1);
dbms_sql.close_cursor(c1);
ELSIF SYS_CONTEXT('userenv','session_user') = 'SCOTT' THEN
C1 := dbms_sql.open_cursor();
dbms_sql.parse(c1,'altersession set nls_date_format='||chr(39)||'YYYY.MM.DD'||chr(39),dbms_sql.NATIVE);
r1:=dbms_sql.execute(c1);
dbms_sql.close_cursor(c1);
ENDIF;
END;
/
这样后,只要是用户zhao或scott 登录到Oracle,都会引起NLS_DATE_FORMAT的改变,请看下面的查询结果:
连接到:
Oracle8i EnterpriseEdition Release 8.1.7.0.0 - Production
With the Partitioningoption
JServer Release8.1.7.0.0 - Production
SQL> show user
USER 为"ZHAO"
SQL> select sysdatefrom dual;
SYSDATE
----------
2002/02/08
再登录到scott用户,结果为:
连接到:
Oracle8i EnterpriseEdition Release 8.1.7.0.0 - Production
With the Partitioningoption
JServer Release8.1.7.0.0 - Production
SQL> show user
USER 为"SCOTT"
SQL> select sysdatefrom dual;
SYSDATE
----------
2002.02.08
需要注意的是,这个触发器是在开始登录成功后(AFTERLOGON)的第一个用户有效,如果你不退出本次会话而用connect 连接到另外的用户的话,则不会触发该触发器。
§28.2如何避免TOO_MANY_ROWS错误
许多使用PL/SQL编写程序的程序人员都会遇到TOO_MANY_ROWS这样的错误提示。这是因为在PL/SQL中,每次查询只允许一条记录取出放到变量中,如果被查询的条件满足一条以上的话,就会出现这样的错误。但如果查询时没有满足条件的记录,则也会出现no_data_found错误。解决这两个问题的办法如下:
假设要查询的EMP表有下面结果:
SQL> select * fromemp;
ENAME SAL DEPTNO TEL
------------------------------ ---------- --------------------------------
赵元杰 9000 10 1360 136 5681
张三 8888 10 123456
李四 7000 10 654321
已选择3行。
例1. 没有声明例外的PL/SQL代码,先设置set serveroutput on 以保证能显示信息。
declare
v_sal number(9,2);
v_ename varchar2(20);
begin
begin
select ename, sal into v_ename, v_sal from emp
where sal >= 8888 ;
dbms_output.put_line(’姓名:’||v_ename ||’,工资:’||to_char(v_sal) );
end;
begin
select ename, sal into v_ename, v_sal from emp
where sal = 9000 ;
dbms_output.put_line(’姓名:’||v_ename ||’,工资:’||to_char(v_sal) );
end;
end;
/
从上面的查询条件来看,由于sal>=8888有两条记录被满足,并且程序中没有声明例外处理情况,所以出现错误后第二条语句也就不执行了。执行显示信息如下:
SQL> declare
2 v_sal number(9,2);
3 v_ename varchar2(20);
4 begin
5 begin
6 select ename, sal into v_ename, v_sal from emp
7 where sal >= 8888 ;
8 dbms_output.put_line('姓名:'|| v_ename ||',工资:'||to_char(v_sal));
9 end;
10 begin
11 select ename, sal into v_ename, v_sal from emp
12 where sal = 9000 ;
13 dbms_output.put_line('姓名:'|| v_ename ||',工资:'||to_char(v_sal));
14 end;
15 end;
16 /
declare
*
ERROR 位于第1 行:
ORA-01422: 实际返回的行数超出请求的行数
ORA-06512: 在line6
例2. 在上面代码的每个begin…end之间加上例外处理(Exception)后,代码脚本如下,在运行前,先设置set serveroutput on 以保证能显示信息。
declare
v_sal number(9,2);
v_ename varchar2(20);
begin
begin
select ename, sal into v_ename, v_sal from emp
where sal >= 8888 ;
dbms_output.put_line(’姓名:’||v_ename ||’,工资:’||to_char(v_sal) );
exception
when no_data_found then
dbms_output.put_line(‘sal >= 8888出现no_data_found ‘);
when too_many_rows then
dbms_output.put_line(‘sal >= 8888出现 too_many_rows’);
end;
begin
select ename, sal into v_ename, v_sal from emp
where sal = 9000 ;
dbms_output.put_line(’姓名:’||v_ename ||’,工资:’||to_char(v_sal) );
exception
when no_data_found then
dbms_output.put_line(‘sal = 9000出现no_data_found ‘);
when too_many_rows then
dbms_output.put_line(‘sal = 9000出现 too_many_rows’);
end;
end;
/
上面的代码执行显示如下:
SQL> declare
2 v_sal number(9,2);
3 v_ename varchar2(20);
4 begin
5 begin
6 select ename, sal into v_ename, v_sal from emp
7 where sal >= 8888 ;
8 dbms_output.put_line('姓名:'|| v_ename ||',工资:'||to_char(v_sal));
9 exception
10 when no_data_found then
11 dbms_output.put_line('sal >= 8888出现 no_data_found ');
12 when too_many_rows then
13 dbms_output.put_line('sal >= 8888出现 too_many_rows');
14 end;
15 begin
16 select ename, sal into v_ename, v_sal from emp
17 where sal = 9000 ;
18 dbms_output.put_line('姓名:'|| v_ename ||',工资:'||to_char(v_sal));
19 exception
20 when no_data_found then
21 dbms_output.put_line('sal = 9000出现no-data_found ');
22 when too_many_rows then
23 dbms_output.put_line('sal = 9000出现 too_many_rows');
24 end;
25 end;
26 /
sal >= 8888出现too_many_rows
姓名:赵元杰,工资:9000
PL/SQL 过程已成功完成。
SQL>
从显示的信息来看,虽然第一个语句出现错误,但是在显示出错信息“sal >= 8888出现 too_many_rows”之后,接着执行下一个语句,并且显示出了结果。
§28.3如何解决TOO_MANY_ROWS问题
如果我们在写程序时,并不知道要查询的表满足条件是否是多于1条以上记录,总担心出现“TOO_MANY_ROWS”错误提示。为了解决这个问题,建议无论所查询的表的记录有多少,都要采用光标来处理。可将上例2该成为下面的完整例子:
例1:使用光标实现查询多条记录。
declare
v_sal number(9,2);
v_ename varchar2(20);
CURSOR c1 is select ename, sal from emp where sal >= 8888 ;
Begin
Open c1;
Fetch c1 into v_ename, v_sal ;
WHILE c1%FOUND LOOP
dbms_output.put_line(’姓名:’||v_ename ||’,工资:’||to_char(v_sal) );
Fetch c1 into v_ename, v_sal ;
END LOOP;
end;
/
执行的结果如下:
SQL> declare
2 v_sal number(9,2);
3 v_ename varchar2(20);
4 CURSOR c1 is select ename,sal from emp where sal >= 8888 ;
5 Begin
6 Open c1;
7 Fetch c1 into v_ename, v_sal ;
8 WHILE c1%FOUND LOOP
9 dbms_output.put_line('姓名:'|| v_ename ||',工资:'||to_char(v_sal) );
10 Fetch c1 into v_ename, v_sal ;
11 ENDLOOP;
12 end;
13 /
姓名:赵元杰,工资:9000
姓名:张三, 工资:8888
PL/SQL 过程已成功完成。
此外,还可以采用循环来实现查询多条记录。
§28.4如何在PL/SQL中使用数组
在PL/SQL中,要想使用数组来存放数据,不能象高级语言那样将变量声明成数组类型,而是使用PL/SQL中规定的类型来实现。即要创建表描述类型和表说明变量类型,用下面语句定义表类型:
TYPE(类型名) IS
TABLE OF ( 数据类型 )
INDEX BYBINARY_INTEGER;
看下面例子:
DECLARE
/* 创建表描述类型 */
TYPE t_ename is TABLE OF emp.ename%TYPE
INDEX BYBINARY_INTEGER;
TYPE t_sal is TABLE OF emp.sal%TYPE
INDEX BYBINARY_INTEGER;
/* 创建变量描述类型 */
v_ename t_ename;
v_sal t_sal ;
Begin
/* 将结果存到数组变量中 */
select ename,sal into v_ename(1) ,v_sal(1) from emp where sal=9000;
select ename,sal into v_ename(2) ,v_sal(2) from emp where sal=8888;
dbms_output.put_line( v_ename(1) ||’:’||to_char(v_sal(1)) );
dbms_output.put_line( v_ename(2) ||’:’||to_char(v_sal(2)));
end;
/
执行的结果显示如下:
赵元杰:9000
张三:8888
PL/SQL 过程已成功完成
§28.5如何使用触发器完成数据复制
Oracle的企业版支持分布环境,如果我们的环境已经建立了分布的话,只要用CREATE DATABASE LINK语句创建相应的数据库的连接,就可以使用AFTER INSERT 来实现将一个源基表的记录插入到远程数据库中。
CREATE OR REPLACETRIGGER emp_ins
AFTER INSERT ON emp FOR EACH ROW
BEGIN
INSERT INTO remote_emp@dblink_emp2
Values(:new.deptno,:new.ename, :new.sal …);
END;
同样,如果源基表进行删除,则也可以写一个AFTER DELETE 触发器:
CREATE OR REPLACETRIGGER emp_del
AFTER DELETE ON emp FOR EACH ROW
BEGIN
DELETE FROM remote_emp@dblink_emp2
WHERE deptno = :old.deptno;
END;
§28.6在PL/SQL中实现Truncate
前面介绍过,在SQL>下可以用TRUNCATE命令来快速删除一个表的所有记录,但是一般在PL/SQL程序中,不能直接使用TRUNCATE命令。许多程序员感叹,要是能在PL/SQL中能使用TRUNCATE那该多方便。下面是笔者所进行的一点例子,供PL/SQL编程爱好者参考:
主要分两步:
1.先创建一个存储过程,比如代码如下:
CREATE or replace procedure truncate_tab(table_namein varchar2) as
Cursor_name Integer;
Begin
Cursor_name := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(Cursor_name,'Truncate table '||table_name||' dropstorage', dbms_sql.native);
DBMS_SQL.CLOSE_CURSOR(Cursor_name);
Exception
When Others then DBMS_SQL.CLOSE_CURSOR(Cursor_name);
RAISE;
End truncate_tab;
/
2.在SQL>下运行上面代码的提示如下:
SQL> CREATEor replace procedure truncate_tab(table_name in varchar2) as
2 CursorId Integer;
3 Begin
4 Cursor_name := DBMS_SQL.OPEN_CURSOR;
5 DBMS_SQL.PARSE(Cursor_name,'Truncate table '||table_name||' drop storage',dbms_sql.native);
6 DBMS_SQL.CLOSE_CURSOR(Cursor_name);
7 Exception
8 When Others then DBMS_SQL.CLOSE_CURSOR(Cursor_name);
9 RAISE;
10 Endtruncate_tab;
11 /
过程已创建。
SQL> select* from abc;
RQ NAME
------------------------------
02-2月 -02 zhaoyuan
已选择 1 行。
上面查询已经知道表ABC有一条记录,现在执行truncate_tab 来删除表ABC的所有记录:
3.执行TRUNCATE_TAB过程来删除表中记录:
SQL> exectruncate_tab('ABC');
PL/SQL 过程已成功完成。
SQL> select* from abc;
未选定行
SQL>
§28.7如何导出存储过程、触发器的代码
REM 作者: Scott Urman
REM 中外注释:赵元杰
CREATE OR REPLACEPACKAGE Export AS
/* 导出一个对象。参数使用如下:
* p_Schema: 指定对象的所有者
* p_ObjType: 指定对象的类型,可以是 'PACKAGE', 'PACKAGE BODY',
* 'PROCEDURE', 'FUNCTION', 或 'TRIGGER'. If NULL,
* 如果类型为空,则表示选择所有的类型。
* p_BothTypes: 如果为TRUE,并当p_ObjType= 'PACKAGE'时,则导出包头和包体。
* p_FileDir: 要导出存放的目录.
* p_FileName: 导出的文件名.
* p_Mode: 模式 ( 'A'—追加 或 'W'—写 ) */
PROCEDURE OneObj(p_Schema IN VARCHAR2,
p_ObjName IN VARCHAR2,
p_ObjType IN VARCHAR2 DEFAULTNULL,
p_BothTypes IN BOOLEANDEFAULT TRUE,
p_FileDir IN VARCHAR2,
p_FileName IN VARCHAR2,
p_Mode IN VARCHAR2);
/* 导出一个对象。参数使用如下:
* p_Schema: 指定对象的所有者
* p_ObjType: 指定对象的类型,可以是 'PACKAGE', 'PACKAGE BODY',
* 'PROCEDURE', 'FUNCTION', 或 'TRIGGER'. If NULL,
* 如果类型为空,则表示选择所有的类型。
* p_BothTypes: 如果为TRUE,并当p_ObjType= 'PACKAGE'时,则导出包头和包体。
* p_FileDir: 要导出存放的目录.
* p_FileName: 导出的文件名.
* p_Mode: 模式 ( 'A'—追加 或 'W'—写 ) */
PROCEDURE AllObjs(p_Schema IN VARCHAR2,
p_ObjType IN VARCHAR2DEFAULT NULL,
p_FileDir IN VARCHAR2,
p_FileName IN VARCHAR2,
p_Mode IN VARCHAR2);
END Export;
/
CREATE OR REPLACEPACKAGE BODY Export AS
/* 这个存储过程挂在包里.它实现输出一个对象处理。输出的对象由p_Schema,
p_ObjName, and p_ObjType 指定。需要确保文件已经以'W'或'A'方式打开. */
PROCEDURE OutputObj(p_FileHandle IN OUTUTL_FILE.FILE_TYPE,
p_Schema IN VARCHAR2,
p_ObjName IN VARCHAR2,
p_ObjType IN VARCHAR2) IS
/* 这里的变量用于返回触发器的文本,因为所有触发器都是以LONG类型进行存放,
所以要用DBMS_SQL来提取 */
v_SQLStmt VARCHAR2(200) :=
'SELECT description, trigger_body
FROM all_triggers
WHERE owner = :v_owner
AND trigger_name = :v_name';
v_Cursor INTEGER;
v_NumRows INTEGER;
v_Dummy INTEGER;
v_Description all_triggers.description%TYPE;
v_BodyChunk VARCHAR2(100);
v_ChunkSize NUMBER := 100;
v_CurPos NUMBER := 0;
v_ReturnedLength NUMBER := 0;
/* 这里的变量用于返回其他类型对象的文本,不用DBMS_SQL来提取,
因为在ALL_SOURCE中都是以行来存放。 */
v_TextLine all_source.text%TYPE;
CURSOR c_ObjCur IS
SELECT text
FROM all_source
WHERE owner = p_Schema
AND name = p_ObjName
AND type = p_ObjType
ORDER BY line;
BEGIN
-- 首先,要写 'CREATE OR REPLACE ' 到打开的文件中.
UTL_FILE.PUT(p_FileHandle, 'CREATE ORREPLACE ');
IF (p_ObjType = 'TRIGGER') THEN
BEGIN
-- 输出对象的类型 (TRIGGER in this case) 到打开的文件中.
UTL_FILE.PUT(p_FileHandle, 'TRIGGER ');
-- 打开光标和分析语句.
v_Cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_Cursor, v_SQLStmt,DBMS_SQL.NATIVE);
-- 绑定变量到占位符.
DBMS_SQL.BIND_VARIABLE(v_Cursor,':v_owner', p_Schema);
DBMS_SQL.BIND_VARIABLE(v_Cursor, ':v_name',p_Objname);
-- 绑定输出变量,为触发器包体定义 DEFINE_COLUMN_LONG
DBMS_SQL.DEFINE_COLUMN(v_Cursor, 1,v_Description, 2000);
DBMS_SQL.DEFINE_COLUMN_LONG(v_Cursor,2);
-- 执行语句返回触发器行,不用循环是由于每个触发器只有一行
v_Dummy := DBMS_SQL.EXECUTE(v_Cursor);
v_NumRows :=DBMS_SQL.FETCH_ROWS(v_Cursor);
-- 返回触发器的描述并写到文件中。
-- 这里用 UTL_FILE.PUT 而不用 UTL_FILE.PUT_LINE,
-- 详细请参考“文件的I/O”章节—赵元杰
DBMS_SQL.COLUMN_VALUE(v_Cursor, 1,v_Description);
UTL_FILE.PUT(p_FileHandle,v_Description);
-- 循环直到返回所有的触发器的主体部分
LOOP
DBMS_SQL.COLUMN_VALUE_LONG(v_Cursor,2, v_ChunkSize, v_CurPos,
v_BodyChunk, v_ReturnedLength);
IF v_ReturnedLength < v_ChunkSizeTHEN
-- 我们刚返回最后一个大块,由于NULL是一个特殊的字符,
-- 它以一个新行来存放.所以需要将它删除掉。
v_BodyChunk := SUBSTR(v_BodyChunk,1, LENGTH(v_BodyChunk) - 1);
-- Output the trimmed chunk, andexit the loop.
UTL_FILE.PUT(p_FileHandle,v_BodyChunk);
EXIT;
ELSE
-- 从LONG返回一个大的块并写到文件中,然后更新下次循环的位置
UTL_FILE.PUT(p_FileHandle,v_BodyChunk);
v_CurPos := v_CurPos +v_ReturnedLength;
END IF;
END LOOP;
-- 已经处理完成,要关闭光标.
DBMS_SQL.CLOSE_CURSOR(v_Cursor);
EXCEPTION
WHEN OTHERS THEN
-- 先关闭第1个光标,然后触发例外处理
DBMS_SQL.CLOSE_CURSOR(v_Cursor);
RAISE;
END;
ELSE
-- 到这里,还没有真正输出触发器,所以要通过all_source来循环输出每行。If we
-- 第1行是对象的类型和名字并加 'IS' 或 'AS'.
OPEN c_ObjCur;
LOOP
FETCH c_ObjCur INTO v_TextLine;
EXIT WHEN c_ObjCur%NOTFOUND;
-- 每行包含新行字符,所以要用UTL_FILE.PUT 而不用 UTL_FILE.PUT_LINE.
UTL_FILE.PUT(p_FileHandle, v_TextLine);
END LOOP;
CLOSE c_ObjCur;
END IF;
-- 最后输出一个 '/'.
UTL_FILE.PUT_LINE(p_FileHandle, '/');
END OutputObj;
PROCEDURE OneObj(p_Schema IN VARCHAR2,
p_ObjName IN VARCHAR2,
p_ObjType IN VARCHAR2DEFAULT NULL,
p_BothTypes IN BOOLEANDEFAULT TRUE,
p_FileDir IN VARCHAR2,
p_FileName IN VARCHAR2,
p_Mode IN VARCHAR2) IS
v_FileHandle UTL_FILE.FILE_TYPE;
v_ObjType all_objects.object_type%TYPE;
BEGIN
-- Validate the input parameters.
IF p_BothTypes AND (p_ObjType != 'PACKAGE')THEN
RAISE_APPLICATION_ERROR(-20000,
'Export.OneObj: BothTypes set but type!= PACKAGE');
ELSIF p_ObjType IS NOT NULL AND p_ObjTypeNOT IN
('PACKAGE', 'PACKAGE BODY','PROCEDURE', 'FUNCTION', 'TRIGGER') THEN
RAISE_APPLICATION_ERROR(-20001,
'Export.OneObj: Illegal value ' ||p_ObjType || ' for object type');
ELSIF p_FileDir IS NULL OR p_FileName ISNULL or p_Mode IS NULL THEN
RAISE_APPLICATION_ERROR(-20002,
'Export.OneObj: Directory, Filename andMode must be non-NULL');
ELSIF p_Mode NOT IN ('A', 'a', 'W', 'w')THEN
RAISE_APPLICATION_ERROR(-20003,
'Export.OneObj: Mode ' || p_Mode || 'not ''A'' or ''W''');
END IF;
-- 确定相应的对象类型和对象的存在.
BEGIN
IF p_ObjType IS NULL THEN
-- 没有指定对象类型(表示所有类型的对象),现在要检查这些对象的存在。
SELECT object_type
INTO v_ObjType
FROM all_objects
WHERE owner = UPPER(p_Schema)
AND object_name = UPPER(p_Objname)
AND object_type IN ('PROCEDURE','FUNCTION', 'PACKAGE',
'PACKAGE BODY','TRIGGER');
ELSIF p_BothTypes THEN
-- 检查 package header
SELECT object_type
INTO v_ObjType
FROM all_objects
WHERE owner = UPPER(p_Schema)
AND object_name = UPPER(p_Objname)
AND object_type = 'PACKAGE';
-- 检查package body.
BEGIN
SELECT object_type
INTO v_ObjType
FROM all_objects
WHERE owner = UPPER(p_Schema)
AND object_name = UPPER(p_Objname)
AND object_type = 'PACKAGE BODY';
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20006,
'Export.ObjObj: BothTypes set butpackage body ' ||
p_Schema || '.' || p_Objname || 'not found');
END;
ELSE
-- 对象类型指定、都不指定的检查存在性.
SELECT object_type
INTO v_ObjType
FROM all_objects
WHERE owner = UPPER(p_Schema)
AND object_name = UPPER(p_Objname)
AND object_type = p_ObjType;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20004,
'Export.OneObj: Object ' || p_Schema|| '.' || p_Objname ||
' not found');
WHEN TOO_MANY_ROWS THEN
RAISE_APPLICATION_ERROR(-20005,
'Export.OneObj: More than one matchfor ' || p_Schema || '.' ||
p_Objname);
END;
-- 到这里,表明对象已经存在,所以要打开文件并写到文件中。
v_FileHandle := UTL_FILE.FOPEN(p_FileDir,p_FileName, p_Mode);
IF p_Mode IN ('A', 'a') THEN
UTL_FILE.NEW_LINE(v_FileHandle, 2);
END IF;
-- 输出对象.
IF p_ObjType = 'PACKAGE' AND p_BothTypesTHEN
OutputObj(v_FileHandle, p_Schema, p_ObjName, 'PACKAGE');
UTL_FILE.NEW_LINE(v_FileHandle, 2);
OutputObj(v_FileHandle, p_Schema,p_ObjName, 'PACKAGE BODY');
ELSE
OutputObj(v_FileHandle, p_Schema,p_ObjName, v_ObjType);
END IF;
-- 关闭输出文件.
UTL_FILE.FCLOSE(v_FileHandle);
EXCEPTION
--处理 UTL_FILE 例外并关闭文件
WHEN UTL_FILE.INVALID_PATH THEN
UTL_FILE.FCLOSE(v_FileHandle);
RAISE_APPLICATION_ERROR(-20010,
'Export.OneObj:Invalid Path');
WHEN UTL_FILE.INVALID_OPERATION THEN
UTL_FILE.FCLOSE(v_FileHandle);
RAISE_APPLICATION_ERROR(-20010,
'Export.OneObj:Invalid Operation');
WHEN UTL_FILE.INVALID_FILEHANDLE THEN
UTL_FILE.FCLOSE(v_FileHandle);
RAISE_APPLICATION_ERROR(-20010,
'Export.OneObj:Invalid File Handle');
WHEN UTL_FILE.WRITE_ERROR THEN
UTL_FILE.FCLOSE(v_FileHandle);
RAISE_APPLICATION_ERROR(-20010,
'Export.OneObj:Write Error');
WHEN UTL_FILE.INTERNAL_ERROR THEN
UTL_FILE.FCLOSE(v_FileHandle);
RAISE_APPLICATION_ERROR(-20010,
'Export.OneObj:Internal Error');
WHEN OTHERS THEN
UTL_FILE.FCLOSE(v_FileHandle);
RAISE;
END OneObj;
PROCEDURE AllObjs(p_Schema IN VARCHAR2,
p_ObjType IN VARCHAR2DEFAULT NULL,
p_FileDir IN VARCHAR2,
p_FileName IN VARCHAR2,
p_Mode IN VARCHAR2) IS
v_FileHandle UTL_FILE.FILE_TYPE;
v_ObjName all_objects.object_name%TYPE;
v_ObjType all_objects.object_type%TYPE;
v_ObjectFound BOOLEAN := FALSE;
-- 使用光标变量查询,因为采用两种格式。
TYPE t_AllObjs IS REF CURSOR;
c_AllObjsCur t_AllObjs;
BEGIN
-- 生效输入参数.
IF p_ObjType IS NOT NULL AND p_ObjType NOTIN
('PACKAGE', 'PACKAGE BODY','PROCEDURE', 'FUNCTION', 'TRIGGER') THEN
RAISE_APPLICATION_ERROR(-20001,
'Export.AllObjs: Illegal value ' ||p_ObjType || ' for object type');
ELSIF p_FileDir IS NULL OR p_FileName ISNULL or p_Mode IS NULL THEN
RAISE_APPLICATION_ERROR(-20002,
'Export.AllObjs: Directory, Filenameand Mode must be non-NULL');
ELSIF p_Mode NOT IN ('A', 'a', 'W', 'w')THEN
RAISE_APPLICATION_ERROR(-20003,
'Export.AllObjs: Mode ' || p_Mode || 'not ''A'' or ''W''');
END IF;
-- 如果对象类型没有指定,则要打开查询所有对象的光标
-- 如果对象类型已经指定,则要打开对象类型的光标
IF p_ObjType IS NULL THEN
OPEN c_AllObjsCur FOR
SELECT object_name, object_type
FROM all_objects
WHERE owner = UPPER(p_Schema)
AND object_type in ('PACKAGE','PACKAGE BODY', 'PROCEDURE', 'FUNCTION',
'TRIGGER');
ELSE
OPEN c_AllObjsCur FOR
SELECT object_name, object_type
FROM all_objects
WHERE owner = UPPER(p_Schema)
AND object_type = p_ObjType;
END IF;
-- 循环超过选择的要求,并且在行间输出2个空行。
LOOP
FETCH c_AllObjsCur INTO v_ObjName,v_ObjType;
EXIT WHEN c_AllObjsCur%NOTFOUND;
IF NOT v_ObjectFound THEN
-- 如果找到至少一个对象,则以追加(’A’)打开文件并且写2个空行。
v_ObjectFound := TRUE;
v_FileHandle :=UTL_FILE.FOPEN(p_FileDir, p_FileName, p_Mode);
IF p_Mode IN ('A', 'a') THEN
UTL_FILE.NEW_LINE(v_FileHandle, 2);
END IF;
END IF;
OutputObj(v_FileHandle, p_Schema,v_ObjName, v_ObjType);
UTL_FILE.NEW_LINE(v_FileHandle, 2);
END LOOP;
-- 检查对象和关闭光标及文件句柄
CLOSE c_AllObjsCur;
IF NOT v_ObjectFound THEN
RAISE_APPLICATION_ERROR(-20004,
'Export.AllObjs: No objects found');
END IF;
UTL_FILE.FCLOSE(v_FileHandle);
EXCEPTION
-- 处理 UTL_FILE 例外和关闭文件
WHEN UTL_FILE.INVALID_PATH THEN
UTL_FILE.FCLOSE(v_FileHandle);
RAISE_APPLICATION_ERROR(-20010,
'Export.AllObjs:Invalid Path');
WHEN UTL_FILE.INVALID_OPERATION THEN
UTL_FILE.FCLOSE(v_FileHandle);
RAISE_APPLICATION_ERROR(-20010,
'Export.AllObjs:Invalid Operation');
WHEN UTL_FILE.INVALID_FILEHANDLE THEN
UTL_FILE.FCLOSE(v_FileHandle);
RAISE_APPLICATION_ERROR(-20010,
'Export.AllObjs:Invalid File Handle');
WHEN UTL_FILE.WRITE_ERROR THEN
UTL_FILE.FCLOSE(v_FileHandle);
RAISE_APPLICATION_ERROR(-20010,
'Export.AllObjs:Write Error');
WHEN OTHERS THEN
UTL_FILE.FCLOSE(v_FileHandle);
RAISE;
END AllObjs;
END Export;
/
附录A:SQL及SQL*PLUS命令参考
命令参考按照字母顺序排列:
%FOUND
参见:
%ISOPEN, %NOTFOUND,
CURSOR, %ROWCOUNT
语法:
c1%FOUND
或
SQL%FOUND
c1 为PL/SQL 光标名字
例子:
PL/SQL
LOOP
FETCH loan_cursor INTOcustomer_name, loan_amount;
IF loan_cursor%FOUND THEN
approved_loan :=loan_amount * 0.80;
ELSE
EXIT;
END IF;
END LOOP;
INSERT INTO approved_loanVALUES (customer_name,
loan_amount);
IF SQL%FOUND THEN
COMMIT;
ELSE
ROLLBACK;
ENDIF;
%ISOPEN
参见:
%FOUND, %NOTFOUND,
CURSOR, %ROWCOUNT
语法:
c1%ISOPEN
或
SQL%ISOPEN
c1:为 PL/SQL 光标
例子:
PL/SQL
IF loan_cursor%ISOPEN THEN
FETCH loan_cursor INTOcustomer_name,
loan_amount;
ELSE
OPEN loan_cursor;
ENDIF;
%NOTFOUND
参见:
%ISOPEN, %FOUND,
CURSOR, %ROWCOUNT
语法:
c1%NOTFOUND
或
SQL%NOTFOUND
变量:
c1:有效的 PL/SQL 光标
例子:
PL/SQL
LOOP
FETCH loan_cursor INTOcustomer_name,
loan_amount;
EXIT WHENloan_cursor%NOTFOUND
END LOOP;
INSERT INTO approved_loanVALUES (customer_name,
loan_amount);
IF SQL%NOTFOUND THEN
ROLLBACK;
ELSE
COMMIT;
ENDIF;
%ROWCOUNT
参见:
%ISOPEN, %FOUND,
%NOTFOUND, CURSOR
语法:
c1%ROWCOUNT
或
SQL%ROWCOUNT
变量:
c1:有效的 PL/SQL 光标。
例子:
PL/SQL
DECLARE
CURSOR c1 IS SELECTcustomer_name, loan_amount FROM
Loan ORDER BY loan_amount DESC;
cust_name VARCHAR2(50);
loan_amt NUMBER(5,2);
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO cust_name,loan_amt;
EXIT WHEN (c1%ROWCOUNT >5) OR
(c1%NOTFOUND);
INSERT INTO loan_approvedVALUES
(cust_name, loan_amt);
COMMIT;
END LOOP;
CLOSE c1;
END;
UPDATE approved_loan SETapproved_flag = ‘Y’ WHERE
loan_amount < 5000;
IF SQL%ROWCOUNT > 10THEN
INSERT INTO approvalsVALUES (‘STOP MORE
APPROVAL’);
ENDIF;
%ROWTYPE
参见:
%TYPE, FETCH
语法:
( [user.]table | cursor )%ROWTYPE
变量:
user: 用户名
table : 要访问的表或视图名
cursor: 任何有效的 PL/SQL 光标
例子:
DECLARE
CURSOR loan_cursor ISSELECT customer_name, loan_amount
FROM loan;
loan_recordloan_cursor%ROWTYPE
BEGIN
OPEN loan_cursor;
LOOP
FETCH loan_cursor INTOloan_record;
EXIT WHENloan_cursor%NOTFOUND;
IF loan_record.loan_amount< 5000 THEN
UPDATE loan
SET loan_approval = ‘Y’
WHERE customer_name =
loan_record.customer_name;
IF SQL%FOUND THEN
COMMIT;
END IF;
END IF;
END LOOP;
CLOSE loan_cursor;
END;
%TYPE
参见:
%ROWTYPE, FETCH
语法:
( [user.]table.column | variable )%TYPE
变量:
user: 用户名
table or view: 要访问的表名或视图名
column: 表名或视图的列名
variable: 任何有效的PL/SQL 变量
例子:
PL/SQL
new_balance old_balance%TYPE;
customer_name loan.customer_name%TYPE;
(+)
参见:
Joining Tables PRIOR, CONNECT BY
语法:
WHERE table1.column1 =table2.column1 (+)
例子:
SQL
SELECT SALES_AGENT, MANAGERFROM SALESMEN, MANAGER
WHERE SALESMEN.MANGER =MANGER.MANGER(+);
SALES_AGENT MANAGER
---------- --------
JAMES BOND M
SADDAM HUSSAIN
MICHAEL JORDAN PHIL JACKSON
@ (“at” 号)
参见:
@@, CREATE DATABASE LINK
语法:
@ filename[.ext][arg_1,...,arg_N]
例子:
SQL
@PRINT_LOAN_RPT
@PRINT_RESULTS.QRY
@@
参见:
@
语法:
@@ filename [.ext]
例子:
SQL
@@PRINT_LOAN_STATUS.QRY
@@PRINT_RESULTS.QRY
ABS
参见:
SIGN
语法:
ABS (n)
变量:
n:一个数字变量
例子:
PL/SQL
Var1:= ABS (-1);
SQL
SELECT ABS (-1) “AbsoluteValue” FROM DUAL;
Absolute Value
-------
1
ACCEPT
参见:
INPUT
语法:
ACCEPT variable [format][default] [PROMPT]
例子:
SQL
ACCEPT loan NUMBER FORMAT‘99999.99’ DEFAULT ‘0.00’ PROMPT
‘Enter weekly salary: ‘
ACCEPT last_name CHARFORMAT ‘35’ PROMPT “Enter customer’s last
name: “
ACOS
参见:
ASIN, ATAN, and ATAN2
语法:
ACOS (n)
变量:
n:-1到1 的数字变量
例子:
PL/SQL
Var1:= ACOS (-1);
SQL
SELECT ACOS (-1) “ArcCosine” FROM DUAL;
Arc Cosine
----------
3.1415927
ADD_MONTHS
参见:
MONTHS_BETWEEN
语法:
ADD_MONTHS (d, n)
变量:
d:有效的日期型变量
n:整型变量
例子:
PL/SQL
Date1:= ADD_MONTHS(‘26-JAN-47’, 10);
SQL
SELECT ADD_MONTHS(‘26-JAN-47’, 10) “Add Months” FROM DUAL;
Add Month
---------
26-NOV-47
ALTER CLUSTER
参见:
CREATE CLUSTER,
DROP CLUSTER
语法:
ALTER CLUSTER[user.]cluster
[INITRANS n]
[MAXTRANS n]
[PCTFREE n]
[PCTUSED n]
[SIZE n [K | M] ]
[STORAGE n]
[ALLOCATE EXTENT [SIZE n [K| M] ] ]
[DATAFILE ‘filename’]
[INSTANCE n]
变量:
user:建立cluster 的用户名
n:任意正整数
filename:文件名
例子:
SQL
ALTERCLUSTER customer PCTFREE 12
ALTER DATABASE
参见:
CREATE DATABASE,DROP DATABASE
语法:
ALTER DATABASE [database]
[ADD LOGFILE
[THREAD n] [GROUP n]
file_definition[.file_definition] ... |
ADD LOGFILE MEMBER
File [REUSE] [, file[REUSE] ...]
TO [GROUP n |
(file [, file] ...) |
file
file [REUSE] [, file[REUSE] ...]
TO [GROUP n |
(file [, file] ...) |
file) ] .. |
DROP LOGFILE
{ GROUP n | (file [,file]... ) | file}
[GROUP n | (file [,file]... ) | file] ... |
DROP LOGFILE MEMBER file [,file] |
RENAME FILE file to file |
NOARCHIVELOG | ARCHIVELOG |
MOUNT [EXCLUSIVE | PARALLE]|
OPEN [RESETLOGS |NORESETLOGS] |
ENABLE [PUBLIC] THREAD n |
DISABLE THREAD n |
BACKUP CONTROLFILE TO file[REUSE]
DATAFILE file [ONLINE |OFFLINE [DROP]] |
CREATE DATAFILE file[,file]
AS file_definition [.file_definition]... |
RENAME GLOBAL_NAME TOdatabase [.domain] |
RECOVER recover_clause |
SET [DBMAC [ON | OFF] |DBHIGH = string | DBLOW =
string ]
例子:
SQL
ALTER DATABASE NO MOUNT ;
ALTERDATABASE ADD LOGFILE GROUP 2 ‘customer.002’ size 2m;
ALTER FUNCTION
参见:
CREATE FUNCTION,DROP FUNCTION
语法:
ALTER FUNCTION[user.]function COMPILE
变量:
user:建立函数的用户名
function:数据库中函数名
例子:
SQL
ALTER FUNCTIONloan_calculation COMPILE;
ALTER FUNCTIONcredit_points COMPILE;
ALTER INDEX
参见:
CREATE INDEX, DROP INDEX
语法:
ALTER [UNIQUE] INDEX[user.]index
[INITRANS n]
[MAXTRANS n]
[STORAGE n]
变量:
user:建立索引的用户名
index:数据库中的索引名
例子:
SQL
ALTER INDEXloan_application;
ALTER PACKAGE
参见:
CREATE PACKAGE,DROP PACKAGE
语法:
ALTER PACKAGE[user.]package COMPILE [PACKAGE | BODY]
变量:
user:建立包的用户名
package:数据库中包名字
例子:
SQL
ALTER PACKAGEloan_processing;
ALTER PROCEDURE
参见:
CREATE PROCEDURE, DROP PROCEDURE
语法:
ALTER PROCEDURE[user.]procedure COMPILE
变量:
user:建立包的用户名
procedure:数据库中的存储过程名
例子:
SQL
ALTER PROCEDUREloan_calculation COMPILE;
ALTER PROCEDURE credit_pointsCOMPILE;
ALTER PROFILE
参见:
CREATE PROFILE, DROP PROFILE
语法:
ALTER PROFILE profile LIMIT
[ { SESSIONS_PER_USER |
CPU_PER_SESSION |
CPU_PER_CALL |
CONNECT_TIME |
IDLE_TIME |
LOGICAL_READS_PER_SESSION |
LOGICAL_READS_PER_CALL |
COMPOSITE_LIMIT } { n |UNLIMITED |
DEFAULT } |
PRIVATE_SGA { n {K | M} |UNLIMITED |
DEFAULT } ]
变量:
profile:想要改变的数据库profile名字
n:任意整数值
例子:
SQL
ALTER PROFILE customerLIMIT SESSION_PER_USER 12 ;
ALTERPROFILE customer LIMIT CONNECT_TIME 600 ;
ALTER RESOURCE COST
参见:
CREATE PROFILE
语法:
ALTER RESOURCE COST
[ CPU_PER_SESSION n |
CONNECT_TIME n |
LOGICAL_READS_PER_SESSION n|
PRIVATE_SGA n]
变量:
n:any integer value
例子:
SQL
ALTER PROCEDUREloan_calculation COMPILE;
ALTERPROCEDURE credit_points COMPILE;
ALTER ROLE
参见:
CREATE ROLE, DROP ROLE
语法:
ALTER ROLE role
[ NOT IDENTIFIED |IDENTIFIED
[ BY PASSWORD | EXTERNALLY] ]
变量:
role:需要改变的数据库中的角色
例子:
SQL
ALTER ROLE custom_user;
ALTER ROLLBACK SEGMENT
参见:
CREATE ROLLBACK SEGMENT,
DROP ROLLBACK SEGMENT
语法:
ALTER [PUBLIC] ROLLBACKSEGMENT rollback_segment
[STORAGE storage]
例子:
SQL
ALTER PUBLIC ROLLBACKSEGMENT humanresources;
ALTER ROLLBACK SEGMENTinsurance;
ALTER SEQUENCE
参见:
CREATE SEQUENCE, DROP SEQUENCE
语法:
ALTER SEQUENCE[user.]sequence
[ INCREMENT BY n]
[ MAXVALUE n | NOMAXVALUE]
[ MINVALUE n | NOMINVALUE]
[ CYCLE | NO CYCLE]
[CACHE n | NO CACHE]
[ORDER | NO ORDER]
变量:
user:建立sequence 的用户名
sequence:需要改变的数据库中sequence
n:任意整数值
例子:
SQL
ALTER SEQUENCEcustomer_code_seq
INCREMENT BY 1;
ALTER SEQUENCEloan_application_seq
NOMAXVALUE;
ALTER SESSION
参见:
SET ROLE, ALTER SYSTEM
语法:
ALTER SESSION
[ SET parameter
CLOSE DATABASE LINK link |
ADVICE [ COMMIT | ROLLBACK| NOTHING ] |
[ ENABLE | DISABLE | COMMITIN PROCEDURE ]
变量:
parameter: 可以设置的下列命令:
[ SQL_TRACE [TRUE | FALSE]
[ NLS_LANGUAGE = language ]
[ NLS_TERRITORY = territory]
[ NLS_DATE_FORMAT = format]
[ NLS_DATE_FORMAT =language ]
[ NLS_NUMERIC_CHARACTERS =string]
[ NLS_ISO_CURRENCY =territory]
[ NLS_CURRENCY = string]
[ NLS_SORT = sort]
[ LABEL = [string | DBHIGH| DBLOW | OSLABEL] ]
[ MLS_LABEL = format ] |
例子:
SQL
ALTER SESSION ENABLE COMMITIN PROCEDURE
ALTERSESSION DISABLE COMMIT IN PROCEDURE
ALTER SNAPSHOT
参见:
CREATE SNAPSHOT,
DROP SNAPSHOT
语法:
ALTER SNAPSHOT [user.]snapshot
[PCTFREE n |
PCTUSED n |
INITRANS n |
MAXTRANS n |
STORAGE n]
[ REFRESH { FAST | COMPLETE| FORCE}
[START WITH start_date][NEXT
next_date]
变量:
user:建立 snapshot的用户名
n:任意的正整数值
start_date:启动snapshot 进程的开始日期
next_date:刷新snapshot的日期
snapshot:需要改变的数据库中 snapshot 的名字
例子:
SQL
ALTER SNAPSHOTcustomer.inactive_cust_snapshot
[PCTUSED 12]
ALTER SNAPSHOT LOG
参见:
CREATE SNAPSHOT LOG,
DROP SNAPSHOT LOG
语法:
ALTER SNAPSHOT LOG ON[user.] table
[PCTFREE n |
PCTUSED n |
INITRANS n |
MAXTRANS n |
STORAGE n]
变量:
n:任意正整数值
table:需要改变的与snapshot 日志有关的主表
例子:
SQL
ALTER SNAPSHOT LOG ONinventory
PCTUSED 12
ALTER SYSTEM
参见:
COMMIT, ROLLBACK,
SAVEPOINT,
SET TRANSACTION
语法:
ALTER SYSTEM
[ SET { RESOURCE_LIMIT = {TRUE | FALSE } |
MTS_SERVERS = n
MTS_DSIPATCHERS =‘protocol.n’
SWITCH LOGFILE |
{ CHECKPOINT | CHECKDATAFILES } { GLOBAL | LOCAL } |
{ ENABLE | DISABLE }
{ DISTRIBUTED RECOVERY |RESTRICTED SESSION } |
ARCHIVE LOGarchive_log_clause
FLUSH SHARE_POOL
KILL SESSION ‘sid, serialnumber’ ]
变量:
archive_log_clause:归档日志项
sid:希望杀掉( KILL)的会话的sid号
serial number:希望杀掉( KILL)的会话的序列号
例子:
SQL
ALTERSYSTEM SET RESOURCE_LIMIT = TRUE
ALTER TABLE
参见:
CREATE TABLE, DROP TABLE
语法:
ALTER TABLE [user.] table
{ [ ADD ( { column1 |table_constraint }
[, column2 |table_constraint} ] ... ) ]
[ MODIFY ( column1,column2) ]
[ DROP drop_constraint]
[PCTFREE n]
[PCTUSED n]
[INITRANS n]
[MAXTRANS n]
[STORAGE n]
ALLOCATE EXTENT
[ SIZE n [K | M]]
[DATAFILE file]
[INSTANCE n]
[ ENABLE | DISABLE]
变量:
user:建立表的用户名
table:希望改变的表名
column:列名
table_constraint:表的限制,如 NULL, NOTNULL 等
n:任意正整数值
file:物理数据文件名
例子:
SQL
ALTER TABLE customer
( ADD ( addressVARCHAR2(50));
ALTER TABLE customer
(MODIFY ( name VARCHAR2(50) NOT NULL);
ALTER TABLESPACE
参见:
CREATE TABLESPACE
语法:
ALTER TABLESPACE tablespace
[ ADD DATAFILEfile_definition
[,file_definition] |
RENAME DATAFILE file[,file] ... TO
file [, file] |
DEFAULT STORAGE storage |
ONLINE | OFFLINE [NORMAL |IMMEDIATE]
|
[ BEGIN | END] BACKUP ]
变量:
tablespace:希望改变的数据库表空间名
file_definition:带大小( K or M)的文件名
例子:
SQL
ALTER TABLESPACE customer
ADD DATAFILE ‘customer02.dat’ size 10m;
ALTER TRIGGER
参见:
CREATE TRIGGER, DROP TRIGGER
语法:
ALTER TRIGGER[user.]trigger [ENABLE | DISABLE |
COMPILE]
变量:
user:建立触发器的用户名
trigger:希望改变的数据库中的触发器
例子:
SQL
ALTER TRIGGERloan_calculation COMPILE;
ALTER TRIGGER credit_pointsCOMPILE;
ALTER TYPE
参见:
CREATE TYPE, CREATE TYPE BODY, DROP TYPE
语法:
ALTER TYPE schema.type_name
{ COMPILE { SPECIFICATION |BODY } |
REPLACE AS | AS TABLE | ASOBJECT }
{ VARRAY (size) | VARYINGARRAY (size) }
{ OF datatype}
{ REF object_type_name}
{ MAP | ORDER MEMBERfunction_specification }
{ PRAGMA RESTRICT_REFERENCES
function_specificationrestriction}
变量:
schema:建立类型的数据库用户名
type_name:希望改变的对象名
size: VARRAY 大小的上限
datatype:任意类型,如 CHAR, DATE, NUMBER等
object_type_name:对象类型引用的名字
function_specification:成员函数名
restriction:限制类型,如 WNDS, WNPS, RNDS, RNPS
例子:
SQL
ALTER TYPE loan_obj ASOBJECT
(customer_name CHAR(20),
loan_amount NUMBER(5),
MEMBER FUNCTION get_amountRETURN NUMBER,
MEMBER FUNCTIONget_loan_approved RETURN NUMBER,
pragmaRESTRICT_REFERENCES (get_amount, WNDS));
ALTER USER
参见:
CREATE USER, DROP USER
语法:
ALTER USER user [IDENTIFIED[BY password | EXTERNALLY]]
[ DEFAULT TABLESPACEtablespace]
[TEMPORARY TABLESPACEtablespace]
[QUOTA {n [K | M] |UNLIMITED} ON tablespace]
[,QUOTA {n [K | M] |UNLIMITED} ON tablespace]
[PROFILE profile]
[DEFAULT ROLE (role1,role2, ...) | ALL EXCEPT
(role1, role2, ...) |
NONE]
变量:
user:希望改变的数据库用户名
tablespace:用户建立的表空间名
n:任意正整数值
profile:用户要使用的profile名
role:用户指定(或不指定)的角色
例子:
SQL
ALTER USER customer
DEFAULT tablespacecust_tablespace
TEMPORARYtablespace temp;
ALTER VIEW
参见:
CREATEVIEW, DROP VIEW
语法:
ALTER VIEW [user.]viewCOMPILE
变量:
user:建立视图的用户名
view:希望改变的数据库的视图名
例子:
SQL
ALTER VIEW loan_calculationCOMPILE;
ALTER VIEW credit_pointsCOMPILE;
ANALYZE
参见:
EXPLAIN PLAN, AUDIT
语法:
ANALYZE
{ INDEX [user.]index
{ { COMPUTE | ESTIMATE |DELETE } STATISTICS
[ SAMPLE ( n PERCENT | nROWS } ] |
VALIDATE STRUCTURE } |
{ TABLE [user.]table |CLUSTER [user.]cluster}
{ { COMPUTE | ESTIMATE |DELETE } STATISTICS
[ SAMPLE ( n PERCENT | nROWS } ] |
VALIDATE STRUCTURE }[CASCASE] |
LIST CHAINED ROWS [INTO[user.]table] }
变量:
index:希望分析的索引的名字
n:任意正整数值
table:希望分析的表名
cluster:希望分析的cluster名
例子:
SQL
ANALYZEINDEX cust_index ESTIMATE STATISTICS;
APPEND
参见:
DEL, EDIT
语法:
A[PPEND] text
变量:
text:在 SQL 缓冲区希望增加的字符串
例子:
SQL
A FROM TELECOM
APPEND WHERE SQLCODE = 2
ASCII
参见:
CHR
语法:
ASCII (‘x’)
变量:
x: 用单引号引起的字符串
例子:
PL/SQL
Var1:= ASCII (‘A’);
SQL
SELECT ASCII (‘Z’) FROMDUAL;
ASCII(‘Z’)
----------
90
ASIN
参见:
ACOS, ATAN, ATAN2
语法:
ASIN (n)
变量:
n: -1 到 1 的数字变量
例子:
PL/SQL
Var1:= ASIN (-1);
SQL
SELECT ASIN (-1) “Arc Sine”FROM DUAL;
Arc Sine
---------
-1.570796
ATAN
参见:
ACOS, ASIN, ATAN2
语法:
ATAN (n)
变量:
n:数字变量
例子:
PL/SQL
Var1:= ATAN (-1);
SQL
SELECT ATAN (-1) “ArcTangent” FROM DUAL;
Arc Tangent
-----------
-.7853982
ATAN2
参见:
ACOS, ASIN, ATAN
语法:
ATAN2 (x, y)
变量:
n:数字变量
例子:
PL/SQL
Var1:= ATAN2 (-1, 1);
SQL
SELECT ATAN2 (-1, 1) “ArcTangent2” FROM DUAL;
Arc Tangent2
------------
-.7853982
AUDIT
参见:
NO AUDIT, ANALYZE
语法:
AUDIT command | ALL
ON [user.]object | DEFAULT
[ BY SESSION | ACCESS ]
[ WHENEVER [NOT] SUCCESSFUL]
变量:
command:可以审计的命令: ALTER, AUDIT, COMMENT, DELETE, EXECUTE, GRANT, INDEX, INSERT, LOCK, RENAME, SELECT及 UPDATE
User:建立对象的用户名
Object:对象名,可以是 table, view, synonym, sequence, procedure, function, package, 或 snapshot.
例子:
SQL
AUDIT INSERT ON customerWHENEVER NOT SUCCESSFUL;
AVG
参见:
Group by SUM
语法:
AVG ( [DISTINCT | ALL] n)
变量:
DISTINCT:如果多个一样只考虑一个的选项,缺省为DISTINCT
ALL:如果多个一样都考虑一个的选项
N:数字变量
例子:
PL/SQL
Var1:= AVG (1, 2, 3);
SQL
SELECT AVG (DAILY_SALES)“Average Daily Sales” FROM SALES;
Average Daily Sales
--------------
655.12
BFILENAME
参见:
LOBs CREATE DIRECTORY
语法:
BFILENAME (‘directory’,‘file’)
变量:
directory:服务器中的物理目录名,最长只能30个字符
file:服务中物理文件名
例子:
SQL
INSERT INTO lob_tableVALUES (BFILENAME (‘lob_directory’,
‘file1.bmp’));
BLOCK
参见:
LABEL
语法:
[<<label>>]
DECLARE
object_declaration_1,...,object_declaration_n
[subprogram_declaration_1
[,subprogram_declaration_2,...,subprogram_declaration_n]]
BEGIN
statement_1,...,statement_n
[EXCEPTIONexception_handler_1 [, exception_handler_2,...,
exception_handler_3]]
END [label];
变量:
label:可选,不需声明的PL/SQL 块
object_declaration_1,...,object_declaration_n:声明的对象,可以是:
constant_declaration
variable_declaration
cursor_declaration
cursor_variable_declaration
record_declaration
exception_declaration
plsql_table_declaration
subprogram_declaration_1,...,subprogram_declaration_n:子程序,可以是:
procedure_declaration
function_declaration
statement_1,...,statement_n:一系列语句
exception_handler_1,...,exception_handler_n:例外处理语句
例子:
PL/SQL
DECLARE
checking_acct_balanceNUMBER;
BEGIN
IF checking_acct_balance =0 THEN
INSERT INTO checking_acctVALUES (500);
DELETE FROM savings_acctVALUES (500);
END IF;
COMMIT;
END;
DECLARE
total_sales NUMBER;
bonus NUMBER;
salary NUMBER;
emp_id NUMBER;
BEGIN
IF total_sales < 1000THEN
bonus := salary /total_sales;
ELSE
bonus := total_sales ;
END IF;
INSERT INTO employee VALUES(bonus);
COMMIT;
EXCEPTION
WHEN ZERO_DIVIDE THEN
INSERT INTO employee VALUES(0);
COMMIT;
WHEN OTHERS THEN
ROLLBACK;
END;.
BTITLE
参见:
TTITLE, REPHEADER, REPFOOTER
语法:
BTI[TLE] [ printspec [ text| variable] ] | [ OFF | ON]
变量:
printspec:输出说明,包括:
COL n
S[KIP] [n]
TAB n
LE[FT]
CE[NTER]
R[IGHT]
BOLD
FORMAT text
Text:标题的字符
Variable:包括下列值的变量:
SQL.LNO (当前行号)
SQL.PNO (当前页号)
SQL.RELEASE (当前Oracle版本号)
SQL.SQLCODE ( 当前错误代码号)
SQL.USER (当前用户)
例子:
SQL
BTITLE RIGHT ‘MARKETINGREPORT’
BTITLE OFF
BTITLE ON.
CEIL
参见:
FLOOR
语法:
CEIL (n)
变量:
n:数字变量
例子:
PL/SQL
Var1:= CEIL (147.2754);
SQL
SELECT CEIL (147.2754)“Ceiling” FROM DUAL;
Ceiling
---------
148
CHANGE
参见:
EDIT
语法:
C[HANGE]separating_character old [separating_character [new
[separating_character]]]
变量:
separating_character:可分开的非字母数字字符串
old: SQL缓冲区中的旧字符串
new: 新的字符串
例子:
SQL
SQL>l
SELECT MARKETING,NAME fromemp
SQL>C /MARKETING/SALES
CHARTOROWID
参见:
ROWIDTOCHAR
语法:
CHARTOROWID (‘x’)
变量:
x:单引号引起的字符 (CHAR或 VARCHAR2 数据类型)
例子:
PL/SQL
Var1 := CHARTOROWID(‘ORACLE’)
SQL
SELECT CHARTOROWID(‘0000000D.1111.0022’) “ROWID_TYPE” FROM
DUAL;
ROWID_TYPE
------------------
0000000D.1111.0022
CHR
参见:
ASCII
语法:
CHR (n)
变量:
n: 数字变量
例子:
PL/SQL
Var1 := CHR (120);
SQL
SELECT CHR (74) || CHR (65)|| CHR (86) || CHR (65) “Hot”
FROM DUAL;
Hot
----
JAVA
CLEAR
参见:
Group By BREAK, COLUMN, COMPUTE
语法:
CLEAR option
变量:
option:可以是下列之一
BUF[FER]
COL[UMNS]
COMP[UTES]
SCR[EEN]
SQL
TIM[ING]
例子:
SQL
CLEAR BREAKS
CLEAR BUFFER
CLEAR SCREEN
CLEAR SQL
CLOSE
参见:
OPEN, OPEN-FOR, FETCH
语法:
CLOSE {cursor_name |cursor_variable_name | :host_cursor_variable_name};
变量:
cursor_name:希望关闭的光标名
cursor_variable_name:希望关闭的与光标有关变量名
host_cursor_variable_name:PL/SQL中缩主变量名
例子:
PL/SQL
LOOP
FETCH loan_cur INTOloan_rec;
EXIT WHENloan_cur%NOTFOUND;
END LOOP;
CLOSE loan_cur;
COLUMN
参见:
Titles 和Headings
语法:
COL[UMN] [{column|expr}[option_1 ... option_n]]
变量:
column:希望设置的列名
expr:有效的SQL表达式
option_1 ... option_N:下列选件之一 :
ALI[AS] alias
CLE[AR]
FOLD_A[FTER]
FOLD_B[EFORE]
FOR[MAT] format
HEA[DING] text
JUS[TIFY]{L[EFT]|C[ENTER]|C[ENTRE]|R[IGHT]}
LIKE {expr|alias}
NEWL[INE]
NEW_V[ALUE] variable
NOPRI[NT]|PRI[NT]
NUL[L] text
OLD_V[ALUE] variable
ON|OFF
WRA[PPED]|WOR[D_WRAPPED]|TRU[NCATED]
例子:
SQL
COLUMN SALARY FORMAT$9,999,999.99
COLUMN LAST_NAME FORMATA35.
COMMENT
参见:
REMARK
语法:
COMMENT ON [ TABLE table |COLUMN table.column ] IS comment
变量:
table:希望加注释的表名
column:希望加注释的列名
comment:注释内容
例子:
SQL
COMMENT ON TABLE customerCOLUMN cust_name IS this column
is ‘the name of thecustomer’;
COMMIT
参见:
SQL *Plus ROLLBACK, SAVEPOINT, SET
TRANSACTION, LOCK TABLE
语法:
COMMIT [WORK ] [COMMENT‘comment_text’]
变量:
WORK:可选关键字.
COMMENT:可以允许加50个字符的注释
例子:
SQL
COMMIT;
COMMIT WORK;
COMMIT WORK ‘Saving loantransaction’
COMPUTE
参见:
Group By BREAK
语法:
COMP[UTE] [function [LABEL]text OF expression|column|alias ON
expression | column | alias| REPORT | ROW]
变量:
function:下列函数之一:
AVG, COU[NT], MAX[IMUM],MIN[IMUM], NUM[BER], STD, SUM,
VAR[IANCE]
Text:the text you will specify for the computed value
Expression:the expression you would like to use to compute the value
Column:the column(s) you would like to use to compute the value
Alias:the alias for the column(s) you would like to use to compute thevalue
例子:
SQL
COMPUTE MAX LABEL ‘MAXIMUMSALES’ OF SALES ON PRODUCTS
COMPUTE MIN LABEL ‘MINIMUMSALES’ OF SALES ON PRODUCTS
COMPUTER SUM LABEL ‘TOTAL’OF SALES ON PRODUCTS
CONCAT
参见:
CONCATENATE
语法:
CONCAT (‘x’, ‘y’)
变量:
x: 字符串变量
y: 字符串变量
例子:
PL/SQL
Var1 := CONCAT (‘Oracle 8‘, ‘is now released’) ;
SQL
SELECT CONCAT (‘Oracle 8 ‘,‘is now released’) “Press Release”
FROM DUAL;
Press Release
------------------------
Oracle 8 is now released.
CONCATENATE
参见:
CONCAT
语法:
||
例子:
PL/SQL
Var1 := ‘Oracle 8 ‘ || ‘isnow released’;
SQL
SELECT ‘Oracle 8 ‘ || ‘isnow released’ “Press Release” FROM
DUAL;
Press Release
------------------------
Oracle 8 is now released.
CONNECT BY
语法:
SELECT sql_expn FROM[user.]table WHERE where_condition
CONNECT BY [PRIOR] expn =[PRIOR] expn
START WITH expn = expn
ORDER BY expn
变量:
sql_expn:有效的SQL表达式
user:表的主人
table: SELECT的表
where_condition:SELECT的WHERE条件
expn:任意有效的表达式
例子:
SQL
SELECT employee_name,department_name FROM employee
CONNECT BY emp_no = PRIORdepartment_no
ORDER BY department_no;.
CONVERT
参见:
DECODE
语法:
CONVERT (‘x’,’desc_set’ [,‘source_set’])
变量:
x:单引号印起的字符型
desc_set:单引号印起的字符集名
source_set:单引号印起的原来字符集名,可以选择下面字符集名:
通用字符集名 编码字符集名(为了查阅,不翻译)
US7ASCII US 7-bit ASCII character set
WE8DEC DEC West European 8-bit character set
WE8HP HP West European Laserjet 8-bitcharacter set
F7DEC DEC French 7-bit character set
WE8EBCDIC500 IBM West European EBCDIC CodePage 500
WE8PC850 IBM PC Code Page 850
WE8ISO8859P1 ISO 8859-1 West European 8-bitcharacter set
例子:
SQL
SELECT CONVERT (‘strutz’, ‘WE8HP’, ‘ F7DEC ‘) “Conversion”
FROM DUAL;
Conversion
------
Strutz.
COPY
参见:
CREATE DATBASE LINK
语法:
COPY FROMusername[/password] @database_specification TO
username[/password]@database_specification
{APPEND|CREATE|INSERT|REPLACE}destination_table
[(column_1,..., column_N)]USING query
变量:
username:用户名
password:口令
database specification: 数据库
destination_table:目的数据库
column_1,...,column_N:目标表的字段
query:查询语句
例子:
SQL
COPY FROM brokerage/sql TOsales/sql APPEND customers (id,
name, address) USING selectid, name, address from
brokerage.customers;
COS
参见:
COSH, SIN, SINH, TAN, TANH
语法:
COS (n)
变量:
n:数字变量
例子:
PL/SQL
Var1:= COS (90);
SQL
SELECT COS (90) “Cosine of90 degrees” FROM DUAL;
Cosine of 90 degrees
--------------------
-.4480736
COSH
参见:
COS, SIN, SINH, TAN, TANH
语法:
COSH (n)
变量:
n:数字变量
例子:
PL/SQL
Var1:= COSH (20);
SQL
SELECT COSH (20)“Hyperbolic Cosine of 20 deg” FROM DUAL;
Hyperbolic Cosine of 20 deg
---------------------------
242582598
COUNT
参见:
Group by SUM
语法:
COUNT ( * | [DISTINCT |ALL] expn)
变量:
*:所有列(包括重复)的个数
DISTINCT:所有不同列(不重复)的个数
ALL:所有列(包括重复)的个数
Expn:表达式
例子:
SQL
SELECT COUNT (*) “Total # of Sales” FROM SALES;
Total # of Sales
--------------
12
CREATE CLUSTER
参见:
ALTER CLUSTER, DROP CLUSTER
语法:
CREATE CLUSTER[user.]cluster
( column1 datatype [,column2 datatype, ....] )
[INITRANS n]
[MAXTRANS n]
[PCTFREE n]
[PCTUSED n]
[SIZE n [K | M] ]
[STORAGE n]
[TABLESPACE tablespace]
[INDEX | [HASH IS column]HASHKEYS n]
变量:
user:建立cluster的用户
cluster:Cluster 名
n:任何正整数
例子:
SQL
CREATE CLUSTER customerPCTFREE 12.
CREATE CONTROLFILE
参见:
CREATE DATABASE
语法:
CREATE CONTROLFILE [REUSE][SET] DATABASE database
LOGFILE [GROUP n] file[,[GROUP n] file] ( RESETLOG |
NORESETLOG)
DATAFILE file [,file]
[ MAXLOGFILES n]
[ MAXLOGMEMBERS n]
[ MAXLOGHISTORY n]
[ MAXDATAFILES n]
[ MAXINSTANCE n]
[ ARCHIVELOG | NOARCHIVELOG]
变量:
n:任何正整数
file:物理文件名
例子:
SQL
CREATE CONTROLFILE REUSESET DATABASE ORACLE
LOGFILE‘D:\ORAWIN95\DATABASE\LOG1ORCL.ORA’ SIZE
200K,
DATAFILE‘D:\ORAWIN95\DATABASE\SYS1ORCL.ORA’ SIZE
20M;.
CREATE DATABASE
参见:
ALTER DATABASE,
CREATE CONTROLFILE
语法:
CREATE DATABASE [database][CONTROLFILE REUSE] LOGFILE
[GROUP n] file [,[GROUP n]file]
( RESETLOG | NORESETLOG)
DATAFILE file [,file]
[ MAXLOGFILES n]
[ MAXLOGMEMBERS n]
[ MAXLOGHISTORY n]
[DATAFILE file_definition[,file_definition]]
[ MAXDATAFILES n]
[ MAXINSTANCE n]
[ ARCHIVELOG | NOARCHIVELOG]
[EXCLUSIVE]
( CHARACTER SET charset)
变量:
n:任何正整数
file:物理文件名
charset:字符集
例子:
SQL
CREATE DATABASE ORACLE
CONTROLFILE REUSE
LOGFILE‘D:\ORAWIN95\DATABASE\LOG1ORCL.ORA’ SIZE 200K
REUSE,‘D:\ORAWIN95\DATABASE\LOG2ORCL.ORA’ SIZE 200K
REUSE
DATAFILE‘D:\ORAWIN95\DATABASE\SYS1ORCL.ORA’ SIZE 20M
REUSE AUTOEXTEND ON
NEXT 10M MAXSIZE 200M
CHARACTER SETWE8ISO8859P1;.
CREATE DATABASE LINK
参见:
分布应用的CREATE SYNONYM
语法:
CREATE [PUBLIC] DATABASELINK link
CONNECT TO user IDENTIFIED
BY password USING‘connect_string’
变量:
link:数据库链名
user:数据库用户
password:有效的口令
connect_string:被访问的远程数据库字串
例子:
SQL
CREATE DATABASE LINKinternational_customers connect to INTL_DB
identified by intl using‘D:INTERNATIONAL’;
SELECT CUSTOMER_NAME FROM CUSTOMER@INTL_DB;.
CREATE PROFILE
参见:
User Profiles ALTER PROFILE, ALTER
RESOURCE COST, DROP
PROFILE
语法:
CREATE PROFILE profileLIMIT
[ { SESSIONS_PER_USER |
CPU_PER_SESSION |
CPU_PER_CALL |
CONNECT_TIME |
IDLE_TIME |
LOGICAL_READS_PER_SESSION |
LOGICAL_READS_PER_CALL |
COMPOSITE_LIMIT } { n |UNLIMITED | DEFAULT } |
PRIVATE_SGA { n {K | M} |UNLIMITED | DEFAULT } ]
变量:
profile:希望修改的profile文件名
n:任何正整数
例子:
SQL
CREATE PROFILE customerLIMIT SESSION_PER_USER 12 ;
CREATE PROFILE customerLIMIT CONNECT_TIME 600 ;.
CREATE DIRECTORY
参见:
LOBs DROP DIRECTORY, BFILENAME
语法:
CREATE [OR REPLACE]DIRECTORY directory AS pathname;
变量:
directory:建立对象的目录
pathname:单引号引起的完整路径
例子:
SQL
CREATE OR REPLACE DIRECTORYbfile_directory AS ‘/bfile’;
CREATE FUNCTION
参见:
Functions ALTER FUNCTION,
DROP FUNCTION
语法:
CREATE [OR REPLACE]FUNCTION [user.]function
[ (parameter [IN] datatype[,parameter [IN] datatype] ... ) ]
RETURN datatype (IS | AS)block
变量:
user:建立函数的用户名
function:数据库函数名
parameter:传给函数的参数
datatype:变量的数据类型
block: PL/SQL 程序块
例子:
SQL
CREATE FUNCTIONloan_calculation (loan_amount NUMBER,
no_of_years NUMBER) RETURNNUMBER AS
Total_loan NUMBER;
BEGIN
Total_loan := loan_amount *no_of_years;
RETURN total_loan ;
END loan_calculation ;
CREATE INDEX
参见:
ALTER INDEX, DROP INDEX
语法:
CREATE INDEX [user.]index
ON [user.]table (column[ASC | DESC] [,column
[ASC | DESC] ] ... )
[CLUSTER [user.]cluster]
[INITRANS n]
[MAXTRANS n]
[PCTFREE n]
[STORAGE storage]
[TABLESPACE tablespace]
[NO SORT]
变量:
user:建立索引的用户名
index:索引名
table:要建索引的表名
cluster:要建索引的CLUSTER名
n:任何正整数
tablespace:存放索引的表空间
例子:
SQL
CREATE INDEXloan_application_indx
TABLESPACE temp;
CREATE INDEX customer_indx
ON loan (cust_id ASC,loan_id ASC)
TABLESPACE temp;.
CREATELIBRARY
参见:
CREATE FUNCTION, CREATE
PROCEDURE, DROP LIBRARY
语法:
CREATE [OR REPLACE] LIBRARYlibrary_name [IS | AS] filename;
变量:
library_name:SQL和PL/SQL要调用的外部(3GL)函数及存储过程
filename:外部物理文件名
例子:
SQL
CREATE LIBRARY ext_lib IS‘/lib/file1.sql’;
CREATE OR REPLACE LIBRARYext_lib2 AS ‘/lib/file2.sql’;
CREATE PACKAGE
参见:
CREATE PACKAGE BODY,
ALTER PACKAGE,
DROP PACKAGE
语法:
CREATE [OR REPLACE] PACKAGE[user.]package { IS | AS}
{ variable_declaration |
cursor_specification |
exception_declaration |
record_declaration |
plsql_table_declaration |
procedure_specification |
function_specification } ;
[{ variable_declaration |
cursor_specification |
exception_declaration |
record_declaration |
plsql_table_declaration |
procedure_specification |
function_specification } ;] ...
END [package]
变量:
user:建立 Package的用户名
package:建立Package 的名
例子:
SQL
CREATE OR REPLACE PACKAGEloan_approval AS
Type LoanRecTyp IS RECORD(customer_id INTEGER, loan_amount
REAL);
CURSOR customer_history(customer_id NUMBER) RETURN LoanRecTyp;
PROCEDURE approve_loan
( customer_id CHAR,
loan_type CHAR,
loan_amount CHAR);
PROCEDURE cumulative_loan(loan_amount REAL);
END loan_approval ;
CREATEPACKAGE BODY
参见:
CREATE PACKAGE,
ALTER PACKAGE BODY,
DROP PACKAGE BODY
语法:
CREATE [OR REPLACE] PACKAGEBODY [user.]package { IS |
AS}
{ variable_declaration |
cursor_body |
exception_declaration |
record_declaration |
plsql_table_declaration |
procedure_body |
function_body } ;
[{ variable_declaration |
cursor_body |
exception_declaration |
record_declaration |
plsql_table_declaration |
procedure_body |
function_body } ; ] ...
END [package]
变量:
user:建立Package的用户名
package:在数据库中的Package的名字
例子:
SQL
CREATE OR REPLACE PACKAGEBODY loan_approval AS
CURSOR customer_history(customer_id NUMBER) RETURN LoanRecTyp
IS
SELECT customer_id FROMcustomer ;
PROCEDURE approve_loan
( customer_id CHAR,
loan_type CHAR,
loan_amount REAL) IS
BEGIN
IF loan_amount < 10000THEN
UPDATE customer
SET loan_type = ‘A’
WHERE cust_id =customer_id;
END IF;
END approve_loan;
PROCEDURE cumulative_loan(loan_amount REAL) IS
BEGIN
UPDATE total_outstandingSET loan_amt = loan_amt +
loan_amount;
END cumulative_loan;
END approve_loan ;.
CREATE PROCEDURE
参见:
ALTER PROCEDURE,
DROP PROCEDURE
语法:
CREATE [OR REPLACE][user.]procedure
[ (parameter [IN] datatype[,parameter [IN] datatype]
... ) ]
(IS | AS) block
变量:
user:建立Procedure的用户名
procedure:Procedure的名字
parameter:传参数给 Procedure的变量名
datatype:变量的数据类型
block:PL/SQL程序块
例子:
SQL
CREATE PROCEDUREloan_calculation (loan_amount NUMBER,
no_of_years NUMBER) AS
Total_loan NUMBER;
BEGIN
Total_loan:= loan_amount *no_of_years;
END loan_calculation ;
CREATE OR REPLACE PROCEDUREincrement (emp_id INTEGER,
increment REAL) IS
current_pay REAL;
missing_pay EXCEPTION;
BEGIN
SELECT salary INTOcurrent_pay FROM employee WHERE
empno = emp_id;
IF current_pay IS NULL THEN
RAISE missing_pay;
ELSE
UPDATE emp SET salary =salary + increment
WHERE empno = emp_id;
END IF;
EXCEPTION
WHEN missing_pay THEN
INSERT INTO emp_auditVALUES (emp_id,
‘Incomplete Details’);
END increment ;
CREATE ROLE
参见:
ALTER ROLE,
DROP ROLE, GRANT
语法:
CREATE ROLE role
[ NOT IDENTIFIED |IDENTIFIED
[ BY PASSWORD | EXTERNALLY] ]
变量:
role:角色名
例子:
SQL
CREATE ROLE custom_user ;
CREATE ROLLBACK SEGMENT
参见:
ALTER ROLLBACK SEGMENT,
DROP ROLLBACK SEGMENT
语法:
CREATE [PUBLIC] ROLLBACKSEGMENT rollback_segment
[TABLESPACE tablespace]
[STORAGE storage]
变量:
rollback_segment回:滚段名
tablespace:表空间名
例子:
SQL
CREATE ROLLBACK SEGMENTRB_HUMANRESOURCE STORAGE (INITIAL 50K
NEXT 100K OPTIMAL 150K);
CREATE SCHEMA
参见:
CREATE TABLE, DROP SCHEMA
语法:
CREATE SCHEMA AUTHORIZATIONschema
[ CREATE TABLE command |
CREATE VIEW command |
GRANT command ]
变量:
schema:希望建立的模式名
例子:
SQL
CREATE SCHEMA AUTHORIZATIONcust_schema
CREATE TABLE customer
(CUSTOMER_ID NUMBER(4) NOTNULL,
CUSTOMER_NAME VARCHAR2(50)NOT NULL,
STREET_ADDRESS VARCHAR2(50)NOT NULL,
CITY VARCHAR2(50) NOT NULL,
STATE VARCHAR2(2) NOT NULLCHECK (‘FL’, ‘TX’, ‘MD’)
CUST_TYPE VARCHAR2(1) NOTNULL
LOAN_AMOUNT NUMBER(6) ) |
CREATE VIEW loan.view AS
SELECT customer_id,customer_name, loan_amount FROM customer
WHERE
customer_type = ‘L’ ;
CREATE SEQUENCE
参见:
ALTER SEQUENCE,
DROP SEQUENCE
语法:
CREATE SEQUENCE[user.]sequence
[ INCREMENT BY n]
[ START WITH n]
[ MAXVALUE n | NOMAXVALUE]
[ MINVALUE n | NOMINVALUE]
[ CYCLE | NO CYCLE]
[CACHE n | NO CACHE]
[ORDER | NO ORDER]
变量:
user:建立sequence的用户名
sequence:sequence名
n:任何正整数
例子:
SQL
CREATE SEQUENCEcustomer_code_seq
INCREMENT BY 1
START WITH 1;
CREATE SEQUENCEloan_application_seq
START WITH 100
INCREMENT WITH 100
NOMAXVALUE ;.
CREATE SNAPSHOT
参见:
ALTER SNAPSHOT,
DROP SNAPSHOT
语法:
CREATE SNAPSHOT [user.]snapshot
[PCTFREE n |
PCTUSED n |
INITRANS n |
MAXTRANS n |
STORAGE n |
TABLESPACE tablespace ]
[CLUSER cluster (column1[,column2] ...) ]
[ REFRESH { FAST | COMPLETE| FORCE }
[START WITH start_date][NEXT
next_date] ]
AS query
变量:
user:建立snapshot的用户名
snapshot:snapshot的名字
n:任何正整数
tablespace:表空间名
start_date:开始处理日期
next_date:刷新snapshot的日期
query:从 SELECT snapshot取数据的语句
例子:
SQL
CREATE SNAPSHOTcustomer.inactive_cust_snapshot AS
SELECT customer_id,customer_name,
customer_address
FROM customer@remote;.
CREATE SNAPSHOT LOG
CREATE SNAPSHOT,
ALTER SNAPSHOT LOG,
DROP SNAPSHOT LOG
语法:
CREATE SNAPSHOT LOG ON[user.] table
[PCTFREE n |
PCTUSED n |
INITRANS n |
MAXTRANS n |
STORAGE n |
TABLESPACE tablespace ]
变量:
user:建立snapshot log的用户名
table:希望snapshot log的表名
n:any positive integer value
tablespace:the Tablespace on which you would like to create the snapshot
例子:
SQL
CREATE SNAPSHOT LOG
ON customer.customer@remote
TABLESPACE temp ;
CREATE SYNONYM
参见:
CREATE DATABASE LINK,
CREATE TABLE, CREATE VIEW
语法:
CREATE [PUBLIC] SYNONYM[user.] synonym
FOR [user.] table[@database_link]
变量:
user:建立同义词的用户
table:建立同义词的表
database_link:远程数据库连接名
例子:
SQL
CREATE SYNONYM r_cust FOR CUSTOMER@REMOTE_SITE;
CREATE TABLE
参见:
ALTER TABLE,
DROP TABLE
语法:
CREATE TABLE[schema.]table
[ ( { column datatype[DEFAULT expr] [WITH ROWID]
[SCOPE IS[user.]scope_table_name][column_constrint]...
| table_constraint | REF(ref_column_name)WITH ROWID
|SCOPE FOR (ref_column_name) IS [user.]scope_table_name }
[ ,{ column datatype[DEFAULT expr] [WITH ROWID]
[SCOPE IS[user.]scope_table_name][column_constrint]...
| table_constraint | REF(ref_column_name)WITH ROWID
|SCOPE FOR (ref_column_name) ISuser.]scope_table_name}]...)]
[{[ORGANIZATION{HELP|INDEX}
|PCTTHRESHOLD|INCLUDEINGcolumn_name]
[OVERFLOW[physical_sttrbutes_clause|TABLESPACE tablespace]...]
|hpysical_attributes_clause|TABLESPACE tablespace
|LOB(lob_item[,lob_item...])STORE AS
[lob_segname]
[(TABLESPACE tablespace|STORAGE storage
|CHUNK integer |PCTVERSION integer
|CACHE
| NOCACHELOGGING|NOCACHE NOLOGGING
|INDEX[lob_index_name]
[(TABLESPACEtablespace
|STORAGEstorage
|INITITRANSinteger
|MAXTRANSinteger )...])]
|NESTED TABLE nested_itemSTORE AS storage_table
|{LOGGING|NOLOGGING}]...
|CLUSTERcluster(column[,column]...)}]
[PARALLEL parallel_clause ]
[PARTITION BY RANGE (column_list)
(PARTITION [partition_name]VALUE LESS THAN (value_list) [physical_attributes-clause
|TABLESPACE tablespace
|{LOGGING|NOLOGGING}]...
[ENABLE enable_clause|DISABLEdisable_clause]...
[AS subquery]
[CACHE|NOCACHE]
Physical_attributes_clause::=
[PCTFREE integer | PCTUSED integer
|INITRANS integer | MAXTRANS integer
|STORAGE storage_clause]
变量:
user:建立表的用户名
table:表名
column:列(字段)名
expn: DEFAULT 值
column_constraint:列的完整性限制
column_constraint 可以是:
NULL 表示列可以空
NOT NULL表示列不能空
UNIQUE 列指定为唯一键
PRIMARY KEY 列指定为主键
FOREIGN KEY 列指定为外部键
REFERENCES 表示被引用的是主键
ON DELETE CASCASE 当主键表被删除时外部表自动被删除
CHECK 指定每行的条件
Cluster: 希望建立表的cluster名
N: 任意正整数
Query: SELECT 子查询
例子:
SQL
CREATE TABLE customer
(CUSTOMER_ID NUMBER(4) NOTNULL,
CUSTOMER_NAME VARCHAR2(50)NOT NULL,
STREET_ADDRESS VARCHAR2(50)NOT NULL,
CITY VARCHAR2(50) NOT NULL,
STATE VARCHAR2(2) NOT NULLCHECK (‘FL’, ‘TX’, ‘MD’) ;.
CREATE TABLESPACE
参见:
ALTER TABLESPACE
语法:
CREATE TABLESPACEtablespace
DATAFILE file_definition[,file_definition] |
[DEFAULT STORAGE storage ]
[ ONLINE | OFFLINE ]
变量:
tablespace:表空间名
file_definition:文件大小的定义(以 K 或 M)
例子:
SQL
CREATE TABLESPACE customer
(DATAFILE‘customer02.dat’);
CREATE TRIGGER
参见:
ALTERTRIGGER, DROP TRIGGER
语法:
CREATE TRIGGER[user.]trigger
{BEFORE | AFTER }
{DELETE | INSERT | UPDATE [OF column1 [, column2] ...}
[ OR {DELETE | INSERT |UPDATE [ OF column1 [, column2]
...} ...] ON [user.]table
[ REFERENCING { OLD [AS]old | NEW [AS] new } ]
[ FOR EACH ROW]
[WHEN (when_condition) ]
block
变量:
user:建立触发器的用户名
trigger:触发器名
column:触发器的列名
table:定义触发器的表名
old:引用表中的旧值
new:引用表中的新值
when_condition:触发器条件
block:要执行的 PL/SQL块
例子:
SQL
CREATE OR REPLACE TRIGGERcumulative_loan_update
AFTER INSERT OR UPDATE OFloan_amount
ON customer
FOR EACH ROW
WHEN (new. loan_type = ‘A’)
BEGIN
UPDATE total_outstandingSET loan_amt = loan_amt +
new.loan_amount;
END;.
CREATE TYPE
参见:
ALTER TYPE, CREATE
TYPE BODY, DROP TYPE
语法:
CREATE [OR REPLACE] TYPE[schema.]type_name { AS | AS TABLE |
AS OBJECT }
{ VARRAY (size) | VARYINGARRAY (size) }
{ OF datatype}
{ REF object_type_name}
{ MAP | ORDER MEMBER function_specification }
{ PRAGMARESTRICT_REFERENCES function_specification
restriction}
变量:
schema:建立对象类型的模式名
type_name:所建立的类型名
size:VARRAY大小限制
datatype:任意类型,如 CHAR, DATE, NUMBER
object_type_name:类型名
function_specification:函数名
restriction:限制的类型,如WNDS, WNPS, RNDS, RNPS
例子:
SQL
CREATE TYPE customer_obj ASOBJECT
(name CHAR(20),
address CHAR(50),
age NUMBER(2));
CREATE TYPE name_type ASVARRAY(100) OF CHAR(20);
CREATE TYPE loan_obj ASOBJECT
(customer_name CHAR(20),
loan_amount NUMBER(5),
MEMBER FUNCTION get_amountRETURN NUMBER,
pragma RESTRICT_REFERENCES(get_amount, WNDS));.
CREATE TYPE BODY
参见:
CREATE TYPE,
DROP TYPE BODY
语法:
CREATE [OR REPLACE] TYPEBODY [schema.]type_name { IS | AS }
{ MEMBERprocedure_declaration |
function_declaration
{ MAP | ORDER MEMBERfunction_declaration }
END;
变量:
schema:建立类型体的模式名
type_name:类型名( CREATE TYPE)
procedure_declaration: PL/SQL 过程
function_declaration: PL/SQL 函数
例子:
SQL
CREATE OR REPLACE TYPE BODYloan_obj IS
MAP MEMBER FUNCTIONget_amount RETURN NUMBER IS
BEGIN
RETURN loan_amount ;
END;
END;
CREATE USER
参见:
ALTER USER, DROP USER
语法:
CREATE USER user [IDENTIFIED[BY password |
EXTERNALLY]]
[ DEFAULT TABLESPACEtablespace]
[TEMPORARY TABLESPACEtablespace]
[QUOTA {n [K | M] |UNLIMITED} ON tablespace]
[PROFILE profile]
变量:
user:用户名
tablespace:表空间名
n:任意正整数
profile:用户所挂接的profile名
例子:
SQL
CREATE USER supervisoridentified by master
DEFAULT tablespacecust_tablespace
TEMPORARY tablespace temp;
CREATE VIEW
参见:
ALTER VIEW, DROP VIEW
语法:
CREATE [OR REPLACE][FORCE/NO FORCE] VIEW [user.]view
[column_name1,column_name2] AS query
[WITH CHECK OPTION[CONSTRAINT constraint];
变量:
user:建立视图所在的用户名
view:视图名
query:SQL SELECT语句
constraint:约束名
例子:
SQL
CREATE OR REPLACE VIEWcustomer.loan.view AS
SELECT customer_id,loan_id, loan_amount
FROM customer
WHERE customer_type = ‘L’;
CREATE OR REPLACE VIEWemployee.salary_view
(EMPNO, ENAME, SALARY) AS
SELECT emp_id, emp_name,grade.grade_range
FROM employee, grade
WHERE employee.grade_id =grade.grade_id;
CREATE OR REPLACE VIEWladies_only AS
SELLECT customer_id, name,address, city, state, zip
FROM sex_type = ‘F’ WITHCHECK OPTION ;
CURRVAL
参见:
CREATE SEQUENCE, NEXTVAL,
PSEUDOCOLUMN
语法:
CURRVAL
变量:
user:建立sequence 所在的用户名
sequence:sequence名
例子:
SQL*Plus
SELECT loan_seq.CURRVALFROM DUAL;
CURSOR_ALREADY_OPEN
参见:
INVALID_CURSOR
语法:
EXCEPTION
WHEN CURSOR_ALREADY_OPENTHEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n:一系列语句
例子:
PL/SQL
BEGIN
OPEN loan_cur;
LOOP
FETCH loan_cur INTOloan_rec;
EXIT WHENloan_cur%NOTFOUND;
END LOOP;
EXCEPTION
WHEN CURSOR_ALREADY_OPENTHEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR =‘CURSOR_ALREADY_OPEN’ ;
WHEN OTHERS THEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR = ‘OTHER ERROR’;
END;
DATATYPE
参见:
CREATE TABLE, ALTER TABLE
BFILE 外部大数据类型,最大GB.
BLOB 大数据类型,最大GB.
CHAR(n) n=1到 255
CLOB大数据类型,最大4GB.
DATE 日期型 从公元前4712 1月1日 到 公元后4712 12月 31日(占7字节),
还包括时分秒
FLOAT(n) p=1到 126 二进制数
LONG 大数据类型,最大2GB
LONG RAW 大二进制数据类型,最大2GB
MLSLABEL标识二进制格式(Trusted Oracle版本)
NCHAR(n) n=1到 2,000
NCLOB大数据类型,最大4GB
NUMBER(p,s) p=1到38,s=-84 到 127
NVARCHAR2(n) n=1 到 4,000,见 VARCHAR 和 NVARCHAR2
RAW(n) n=1到 255 原始二进制数
ROWID 伪列,十六进制格式
VARCHAR(n) n=1到 4,000
VARCHAR2(n) n=1到 4,000
DATATYPE —CHAR
参见:
VARCHAR2, LONG
语法:
CHAR (n)
变量:
n:类型大小,缺省为1,最大为 2000。
DATATYPE —DATE
参见:
MONTHS_BETWEEN,ADD_MONTHS,
LAST_DAY, NEXT_DAY
语法:
DATE
变量:
无
DATATYPE —FLOAT
参见:
NUMBER
语法:
FLOAT (n)
变量:
n:数字,最小为1,最大为126位。
DATATYPE —LONG
参见:
CHAR, VARCHAR2
语法:
LONG
变量:
无
DATATYPE —LONGRAW
参见:
RAW
语法:
LONG RAW
变量:
无
DATATYPE —MLSLABEL
参见:
Table 命名
语法:
MLSLABEL
变量:
无
DATATYPE —NUMBER
参见:
FLOAT
语法:
NUMBER (p, s)
变量:
DATATYPE —RAW
参见:
LONGRAW
语法:
RAW (n)
变量:
n:1到255
DATATYPE —ROWID
参见:
DELETE,UPDATE, SELECT, SELECT INTO
语法:
ROWID
变量:
无
DATATYPE —VARCHAR
参见:
VARCHAR2
语法:
VARCHAR (n)
变量:
n: 1 到 4000
DATATYPE —VARCHAR2
参见:
CHAR, LONG
语法:
VARCHAR2 (n)
变量:
n::1 到 4000
DECLARE
参见:
DECLARE CURSOR, DECLARE,
DATABASE, DECLARE TABLE,
CREATE PACKAGE
语法:
DECLARE
object_declaration_1,...,object_declaration_n;
变量:
object_declaration_1,...,object_declaration_n:对象及类型声明
例子:
PL/SQL
DECLARE
total_sales NUMBER;
total_products NUMBER;
unit_price CONSTANTNUMBER:= 1.0;
DECODE
参见:
TRANSLATE
语法:
DECODE (n, if1, then1, if2,then2 ...., else)
变量:
n:编码值或列值
if1:条件1
then1:当条件成立后希望代替的值
else:如果条件都满足就用else 来代替
例子:
PL/SQL
Var1:= DECODE (Var2, ‘Sybase’, ‘Oracle’, );
SQL
SELECT DECODE (‘Microsoft’, ‘Microsoft’, ‘Macrosoft’) “Example
“ FROM DUAL;
Example
---------
Macrosoft.
DEFINE
参见:
SET
语法:
DE[FINE][variable]|[variable = text]
变量:
variable:希望定义的变量名
text:1分配给变量的文本
例子:
SQL
DEFINE last_name = ‘Jordan’
DEFINE department_number =160
DEL
参见:
APPEND, CHANGE, EDIT
语法:
DEL [n|n m|n *|n LAST|*|*n|* LAST|LAST]
变量:
n: 希望删除的缓冲区的行数
n m: 希望删除的缓冲区的n行到m行
n *: 希望删除的缓冲区的n行到当前行
n LAST:希望删除的缓冲区的n行到最末行
*: 希望删除的缓冲区的当前行
* n: 希望删除的缓冲区的当前行到n行
* LAST:希望删除的缓冲区的当前行到最末行
LAST: 希望删除的缓冲区的最末行
例子:
SQL
D 1
DEL 5 LAST.
DELETE
参见:
INSERT, UPDATE, SELECT,
SELECT INTO
语法:
DELETE [FROM] {table |(sub_query)} [alias]
[WHERE {search_condition |CURRENT OF cursor_name}];
变量:
table:用户有权删除的表名
sub_query:一组子查询
alias:别名
WHERE search_condition:有效的 WHERE 条件句
CURRENT OF cursor_name:与光标有关的当前行
例子:
SQL
DELETE FROM employees whereemployee_id = ‘124561123’;
DELETE FROM employees whereemployee_id in
(SELECT employee_id
FROM department_head
WHEREdepartment_active_flag = ‘N’);.
DEREF
参见:
REFTOHEX, MAKE_REF
语法:
DEREF (expn)
变量:
expn:引用对象的任意表达式
例子:
SQL
CREATE TABLE customer_tab(customer_no NUMBER, loan REF
loan_tab);
SELECT DEREF (loan) FROMcustomer_tab;
DESCRIBE
参见:
CREATE TABLE
语法:
DESC[RIBE][user.]table[@database_link_name] [column]|
[user.]object[.subobject]
变量:
user:表所有者的用户名
table:希望列出的表名
database_link_name:指定的表或视图的名字
column:列名
object:描述的对象
subobject: 描述的子对象
例子:
SQL
DESCRIBE loan
DESC acct
DROP CLUSTER
参见:
CREATE CLUSTER
语法:
DROP CLUSTER [user.]cluster [INCLUDING TABLES] [CASCADE
CONSTRAINTS]
变量:
user:已经建立的cluster的用户名
cluster:希望从数据库中删除的cluster 名
例子:
SQL
DROP CLUSTER marketing;
DROP CLUSTERtelecommunications INCLUDING TABLES;
DROP CLUSTER branchINCLUDING TABLES CASCADE CONSTRAINTS;
DROP DATABASE LINK
参见:
CREATE DATABASE LINK
语法:
DROP [PUBLIC] DATABASE LINKdatabase_link
变量:
PUBLIC:从数据库中删除的PUBLIC
database_link:从数据库中删除的公共数据库链
例子:
SQL
DROP PUBLIC DATABASE LINKcorporation;
DROP DATABASE LINKtracking;
DROP DIRECTORY
参见:
CREATE DIRECTORY,
BFILENAME
语法:
DROP DIRECTORY directory;
变量:
directory:希望删除的目录名
例子:
SQL
DROP DIRECTORYbfile_directory;
DROP FUNCTION
参见:
CREATE FUNCTION,
ALTER FUNCTION
语法:
DROP FUNCTION[user.]function
变量:
user:建立函数的用户名
function:希望删除的函数
例子:
SQL
DROP FUNCTION acctrpt
DROP FUNCTION loan.calint
DROP INDEX
参见:
CREATE INDEX, ALTER INDEX
语法:
DROP INDEX [user.]index
变量:
user:建立索引的用户名
index:希望删除的索引名
例子:
SQL
DROP INDEX sales;
DROP INDEX resources;
DROP LIBRARY
参见:
CREATE LIBRARY
语法:
DROP LIBRARY library_name;
变量:
library_name: 要删除的库名字
database.
例子:
SQL
DROP LIBRARY ext_procs;
DROP PACKAGE
参见:
CREATE PACKAGE, ALTER PACKAGE
语法:
DROP PACKAGE [body.][user.] package
变量:
body:包体名字
user:建立包的用户名
package:希望删除的包的名字
例子:
SQL
DROP PACKAGE loan;
DROP PACKAGE BODYcustomers.newacct;
DROP PROCEDURE
参见:
CREATE PROCEDURE,
ALTER PROCEDURE
语法:
DROP PROCEDURE [user.]procedure
变量:
user:建立存储过程的用户名
procedure:数据库中存储过程的用户名
例子:
SQL
DROP PROCEDURE loanrpt;
DROP PROCEDUREacct.acctrpt;
DROP PROFILE
参见:
CREATE PROFILE,
ALTER PROFILE
语法:
DROP PROFILE [user.]profile
变量:
user:建立Profile的用户名
profile:Profile名字
例子:
SQL
DROP PROFILE accountant;
DROP PROFILE my_profile;
DROP ROLE
参见:
CREATE ROLE, SET ROLE, ALTER ROLE
语法:
DROP ROLE [user.] role
变量:
user:建立角色的用户名
role:希望删除的角色名
例子:
SQL
DROP ROLE manager;
DROP ROLE officer;
DROP ROLLBACK SEGMENT
参见:
CREATE ROLLBACK SEGMENT,
ALTER ROLLBACK SEGMENT
语法:
DROP ROLLBACK SEGMENTrollback_segment
变量:
rollback_segment: 希望删除的回滚段名字
例子:
SQL
DROP ROLLBACK SEGMENThumanresources;
DROP ROLLBACK SEGMENTinsurance;
DROP SEQUENCE
参见:
CREATE SEQUENCE, ALTER SEQUENCE
语法:
DROP SEQUENCE[user.]sequence
变量:
user:建立序列的用户名
sequence:希望删除的序列名
例子:
SQL
DROP SEQUENCE customer.new_cust_seq;
DROP SEQUENCE seq1;
DROP SNAPSHOT
参见:
CREATE SNAPSHOT,
ALTER SNAPSHOT
语法:
DROP SNAPSHOT [user.]snapshot
变量:
user:建立快照的用户名
snapshot:希望从数据库删除的快照
例子:
SQL
DROP SNAPSHOTcustomer.inactive_cust_snapshot;
DROP SNAPSHOT snp1;
DROP SNAPSHOT LOG
参见:
CREATE SNAPSHOT LOG,
ALTER SNAPSHOT LOG
语法:
DROP SNAPSHOT LOG ON [user.] table
变量:
user:建立快照日志的用户名
table:希望删除的与快照日志有关的主表
例子:
SQL
DROP SNAPSHOT LOG ONinventory
DROP SYNONYM
参见:
CREATE SYNONYM
语法:
DROP [PUBLIC] SYNONYM[user.]synonym
变量:
user:建立同义词的用户名
synonym:希望删除的同义词的用户名
例子:
SQL
DROP SYNONYM PUBLIC SYNONYMcomputer;
DROP SYNONYM advertisement;
DROP TABLE
参见:
CREATE TABLE, ALTER TABLE
语法:
DROP TABLE [user.] table[CASCADE CONSTRAINTS]
变量:
user:建立表的用户名
table:希望从数据库中删除的表名
例子:
SQL
DROP TABLE loan;
DROP TABLE acct CASCADECONSTRAINTS;
DROP TABLESPACE
参见:
CREATE TABLESPACE,
ALTER TABLESPACE
语法:
DROP TABLESPACE tablespace[INCLUDING CONTENTS]
[CASCADE CONSTRAINTS]
变量:
tablespace:希望删除的表空间名字
例子:
SQL
DROP TABLESPACE accounting;
DROP TABLESPACE airlinesINCLUDING CONTENTS;
DROP TABLESPACEconstruction CASCADE CONSTRAINTS;
DROP TRIGGER
参见:
CREATE TRIGGER,
ALTER TRIGGER
语法:
DROP TRIGGER [user.]trigger
变量:
user: 建立触发器的用户名
trigger: 希望删除的数据库触发器名字
例子:
SQL*Plus
DROP TRIGGERaccounting.acct_trigger_1;
DROP TRIGGER loan_trigger_2;
DROP TYPE
参见:
CREATE TYPE, ALTER TYPE,
DROP TYPE BODY
语法:
DROP TYPE [schema.] type_name [FORCE]
变量:
schema: 建立类型的模式名
type_name: 对象类型名字
例子:
SQL
DROP TYPE customer_obj;
DROP TYPE loan_obj FORCE;
DROP TYPE BODY
参见:
CREATE TYPE,
ALTER TYPE,
DROP TYPE BODY
语法:
DROP TYPE BODY [schema.]type_name
变量:
schema: 建立类型的模式名
type_name: 希望删除的对象类型名字
例子:
SQL
DROP TYPE BODYcustomer_obj;
DROP TYPE BODY loan_obj;
DROP USER
参见:
CREATE USER
语法:
DROP USER user [CASCADE]
变量:
user:希望删除的用户名字
例子:
SQL
DROP USER john;
DROP USER robert CASCADE;
DROP VIEW
参见:
CREATE VIEW, ALTER VIEW
语法:
DROP VIEW [user.]view
变量:
user:建立视图的用户名字
view:希望删除的视图的名字
例子:
SQL
DROP VIEWmarketing.marketview;
DROP VIEW loanview;
DUMP
参见:
RAWTOHEX, HEXTORAW
语法:
DUMP ( s [, fmt [, start [,length] ] ] )
变量:
s:CHAR或VARCHAR的字符类型.
Fmt:引用的格式,缺省为 ASCII 或 EBCDIC
Start:S的开始位置
Length:长度
例子:
SQL
SELECT DUMP (SALES_AGENT)“DUMP (Sales Agent, 10, 1, 5)” FROM
SALES;
DUMP (Sales Agent)
--------------
Typ=1 Len=14:20,4e,20,46,52.
DUP_VAL_ON_INDEX
参见:
TOO_MANY_ROWS,
VALUE_ERROR
语法:
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n:一序列语句
例子:
PL/SQL
BEGIN
UPDATE STATISTICS
SET ROW_COUNT = ROW_COUNT +1;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO STATISTICSVALUES (1);
END IF;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
UPDATE STATISTICS
SET ROW_COUNT = 0;
END;
EDIT
参见:
GET, SAVE, SET
语法:
ED[IT] [file_name[.ext]]
变量:
file_name:希望打开的文件名
ext:扩展类型,缺省为.SQL
例子:
SQL
ED LOAN_RPT
EDIT ACCT_RPT
EDIT LOAN.QRY
EMPTY_BLOB
参见:
BFILENAME, EMPTY_CLOB
语法:
EMPTY_BLOB ( )
变量:
无
例子:
PL/SQL
Var1:= EMPTY_BLOB ();
SQL
INSERT INTO lob_tableVALUES (‘BLOB DEMO’, EMPTY_BLOB());
UPDATE lob
SET blob_column =EMPTY_BLOB()
WHERE name = ‘BLOB DEMO’;
EMPTY_CLOB
参见:
BFILENAME, EMPTY_BLOB
语法:
EMPTY_CLOB ( )
变量:
无
例子:
PL/SQL
Var1:= EMPTY_CLOB ();
SQL
INSERT INTO lob_tableVALUES (‘CLOB DEMO’, EMPTY_CLOB());
UPDATE lob
SET clob_column =EMPTY_CLOB()
WHERE name = ‘CLOB DEMO’;
EXCEPTION INIT Pragma
DECLARE EXCEPTION,
EXCEPTION, SQLERRM
语法:
PRAGMA EXCEPTION_INIT (n);
变量:
n: 例外整数值
例子:
PL/SQL
DECLARE
exception_error_occurredexception;
pragma exception_init
(exception_error_occurred-786);
BEGIN
......
EXCEPTION
WHENexception_error_occurred THEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR = EXCEPTIONERROR’ ;
END;
EXECUTE
参见:
PREPARE
语法:
EXEC[UTE] statement
变量:
statement:a PL/SQL statement
例子:
SQL
EXECUTE sales_calculation;
EXEC sales_input_trigger;
EXISTS
参见:
NOT IN, ANY,
ALL, BETWEEN
语法:
WHERE EXISTS (subquery)
变量:
subquery: 一个 SQL SELECT 子句
例子:
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE EXISTS (SELECT * FROMMONTHLY_SALES WHERE
DAILY_SALES.SALES_AMOUNT =MONTHLY_SALES.SALES_AMOUNT);
SALES_AMOUNT
----------
12.23
EXIT
参见:
LOOP, WHILE-LOOP, FOR-LOOP,
EXIT-WHEN
语法:
LOOP
statement_1,…statement_n;
IF condition_1 is true THEN
EXIT;
END IF;
END LOOP
变量:
statement_1,...,statement_n:一序列 PL/SQL 语句
condition_1:适当的 PL/SQL条件句
例子:
PL/SQL
LOOP
Total_Salary:= Base_Salary+ Commission + Bonus;
IF Total_Salary = 0 THEN
EXIT;
END IF;
UPDATE salary SETemployee_salary = Total_Salary WHERE
employee_number:= emp_id;
END LOOP;
EXIT
参见:
COMMIT, DISCONNECT, QUIT
语法:
EXIT|QUIT[SUCCESS|FAILURE|WARNING|n|variable]
[COMMIT|ROLLBACK]
变量:
n:返回代码正整数
variable:用户定义的变量或系统变量
例子:
SQL
EXIT
EXIT SQL.SQLCODE
QUIT.
EXIT-WHEN
参见:
LOOP, WHILE-LOOP,
FOR-LOOP, EXIT-WHEN
语法:
LOOP
statement_1,...statement_n;
EXIT WHEN condition_1 istrue;
END LOOP
变量:
statement_1,...,statement_n:可多次执行的 PL/SQL 语句
condition_1:适当PL/SQL 条件句
例子:
PL/SQL
LOOP
Total_Salary:= Base_Salary+ Commission + Bonus;
EXIT WHEN Total_Salary = 0;
UPDATE salary SETemployee_salary = Total_Salary WHERE
employee_number:= emp_id;
END LOOP ;
EXP
参见:
SQRT, POWER
语法:
EXP (n)
变量:
n:数值变量
例子:
PL/SQL
Var1:= EXP (12);
SQL
SELECT EXP (12)“Exponential Value of 12” FROM DUAL;
Exponential Value of 12
-----------------------
162754.79
EXPLAIN PLAN
参见:
ANALYZE
语法:
EXPLAIN PLAN
[SET STATEMENT ID =statement_name]
[ INTO [user.]table]
FOR query
变量:
statement_name:输出表中一个标识说明方案的名字。如果没有给定,将在输出表中存放一个空值。
User:建立表的用户名
Table:计划存储的表名
Query:计划解释的SQL 语句
例子:
SQL
EXPLAIN PLAN
SET STATEMENT_ID =‘exp_plan_customer
INTO all_plan
FOR
SELECT CUSTOMER_ID,CUSTOMER_NAME FROM CUSTOMER WHERE
CUSTOMER_ID IN (SELECTCUSTOMER_ID FROM LOAN_APPROVAL );.
FETCH
参见:
CLOSE, OPEN, OPEN-FOR
语法:
FETCH {cursor_name |cursor_variable_name
|:host_cursor_variable_name}
INTO {variable_1[,variable_n]... | record_name};
变量:
cursor_name: 光标名
cursor_variable_name: 光标变量名
host_cursor_variable_name: 在PL/SQL宿主变量中声明的光标变量
variable_1,...,variable_n: 一序列变量名
record_name: 用户定义的, 或 %ROWTYPE 取数据的名字
例子:
PL/SQL
OPEN loan_cur ;
LOOP
FETCH loan_cur INTOloan_rec;
EXIT WHENloan_cur%NOTFOUND;
END LOOP;
CLOSE loan_cur;
OPEN emp_cur ;
LOOP
FETCH emp_cur INTO emp_id,emp_name, emp_title,
emp_salary;
EXIT WHEN emp_cur%NOTFOUND;
END LOOP;
CLOSE emp_cur;.
FLOOR
参见:
CEIL
语法:
FLOOR (n)
变量:
n:一个数值变量
例子:
PL/SQL
Var1:= FLOOR (147.2754);
SQL
SELECT FLOOR (147.2754)“Floor” FROM DUAL;
Floor
---------
147
FOR-LOOP
参见:
LOOP, EXIT, WHILE-LOOP,
EXIT-WHEN
语法:
FOR i in 1..10 LOOP
statement_1,...,statement_n;
END LOOP
变量:
i:循环指示变量
statement_1,...,statement_n:一序列PL/SQL语句
例子:
PL/SQL
FOR i in 1..total_employeesLOOP
Total_Salary:= Base_Salary+ Commission + Bonus;
UPDATE salary SETemployee_salary = Total_Salary WHERE
employee_number:= emp_id;
END LOOP;
FORMAT
说明格式的保留字
FORMAT —DATE
日期格式代码表
日期代码 | 格式说明 | 例子 |
AD 或 BC | AD=Anno Domini公元,BC=Before Christ公元前。不带点的公元或公元前 | ‘YYYY AD’=1999 AD |
A.D. 或B.C. | 带点的公元或公元前 | ‘YYYY A.D.’=1999 A.D. |
AM或PM | AM= ante meridiem 上午,PM=post meridiem下午。不带点的上午或下午 | ‘HH12AM’=09AM |
A.M.或P.M. | 带点的上午或下午 | ‘HH12A.M.’=09A.M. |
DY | 星期几的缩写 | Mon,Tue,... |
DAY | 星期几的全拼 | Monday,Tuesday,... |
D | 一周的星期几,星期天=1,星期六=7 | 1,2,3,4,5,6,7 |
DD | 一月的第几天,1à31 | 1,2,... 31 |
DDD | 一年的第几天,1à366 | 1,2,3,...366 |
J | 公元前的第几天(从公元前4712起 ?) | 2451514,2451515,... |
W | 一个月的第几周,1à 5 | 1,2,3,4,5 |
WW,IW | 一年的第几周,一年的ISO的第几周 | 1,2,3,4,... 52 |
MM | 两为数的月 | 01,02,03,...12 |
MON | 月份的缩写 | Jan,Feb,Mar ,...Dec |
MONTH | 月份的全拼 | January,February,... |
RM | 罗马数字的月份,I à XII | I,II,III,IV,...XII |
YYYY,YYY,YY,Y | 四位数的年,三位数的年 | 1999,999,99,9 |
YEAR | 年的全拼 | Nineteen Ninety-nine |
SYYYY | 如果是公元前(BC),年份前负号 | -1250 |
RR | 当前年份的后两位数字 | 01代表2001年 |
HH,HH12 | 12小时制,1à12 | 1,2,3,...12 |
HH24 | 24小时制,0à23 | 0,1,2,3,...23 |
MI | 一小时中的第几分,0à59 | 0,1,2,3...59 |
SS | 一分中的第几秒,0à59 | 0,1,2,3,...59 |
SSSSS | 一天中的第几秒,0à86399 | 0,1,2,3,...86399 |
../-;: | 标点符号表示法 | 文字显示 |
‘text’ | 引号表示法 | 文字显示 |
FORMAT —NUMBER
9 指定的数字.如果为正,前用空格; 如果为负,前带 – 号
0 以 0 来填在前面
$ 美圆号在前
B 浮点数整数部分用空格
MI 带有负号的负数
S 具有给定位置的数值. 正号用+;负号用 -
PR 在<angle brackets>内的负号
D 小数后有指定位数的十进制
G 在指定位置放组分隔符
C 在指定位置的ISO货币符号
L 在指定位置的本地货币符号
, 在指定位置的豆号
. 在指定位置的小数点
V 一个被10的n次方乘的数值,n是v后面的位数
EEEE 科学记数法表示
RN 以罗马数字表示的数字
Rn 以罗马小写数字表示的数字
FM 一个不带前导和后空的数字.
GET
参见:
GET, SAVE, SET, EDIT
语法:
GET filename [.ext][LIS[T]|NOL[IST]]
变量:
filename: 希望加载到SQL缓冲区的文件名
ext: 文件的扩展名,缺省为 SQL.
例子:
SQL
GET LOAN_RPT
GET LOAN.QRY
GLB
参见:
LUB
语法:
GLB ( [DISTINCT | ALL] mls)
变量:
DISTINCT:不同的保留字,可选
ALL:包括相同的所有
Mls:MLSLABEL.类型的变量
GOTO
参见:
LOOP, WHILE-LOOP,
FOR-LOOP
语法:
BEGIN
...
GOTO<<calculate_loan>;
...
<<calculate_loan>
SELECT loan_amount FROMloan;
END
变量:
calculate_loan:标记符
例子:
PL/SQL
BEGIN
...
GOTO<<calculate_total_salary>>;
...
<<calculate_total_salary>>
Total_Salary:= Base_Salary+ Commission + Bonus;
END
GRANT
参见:
REVOKE
语法:
GRANT system_privilege |role TO user | role | PUBLIC
[WITH ADMIN OPTION]
GRANT object_privilege |ALL column ON schema.object
FROM user | role | PUBLICWITH GRANT OPTION
变量:
system_privilege:授与用户或角色的系统权限名
role:授个用户的角色名
user:用户或角色名
object_privilege:对象的权限名字,可以是:
F ALTER
F DELETE
F EXECUTE
F INDEX
F INSERT
F REFERENCES
F SELECT
F UPDATE
Column:列名
Schema:模式名
Object:对象名字
例子:
SQL
GRANT CREATE TABLE TOgavaskar;
GRANT team_leader TOcrystal;
GRANT INSERT, UPDATE ONsales TO larry WITH GRANT
OPTION;
GRANT ALL TO PUBLIC;.
GREATEST
参见:
GREATEST_LB, LEAST,
LEAST_LB
语法:
GREATEST (expn1 [, expn2]...)
变量:
expn:表达式或列名
例子:
SQL
SELECT GREATEST(‘JOHN’,‘JONNY’, ‘JANARTHAN’) “GREATEST” FROM
DUAL;
GREAT
-------------
JONNY
GREATEST_LB
参见:
GREATEST, LEAST, LEAST_LB
语法:
GREATEST_LB (label1 [, label2] ...)
变量:
label:MLSLABEL 或 RAW MLSLABEL.类型变量
HEXTORAW
参见:
RAWTOHEX
语法:
HEXTORAW (‘x’)
变量:
x:带引号的十六进制
例子:
SQL
SELECT HEXTORAW(animation)“Animation” FROM animation;
Animation
----------------------------------------
3D
HOST
参见:
@, START
语法:
HO[ST] [command]
变量:
command:操作系统命令
例子:
SQL
HOST ls -d
HOST ls *.rpt
IF-THEN
参见:
IF-THEN-ELSE, IF-THEN-ELSEIF
语法:
IF condition_1 is true THEN
statement_1,...,statement_n
END IF
变量:
condition_1:条件1
statement_1,...,statement_n:如果条件1是真要执行的一序列PL/SQL 语句
例子:
PL/SQL
IF sales > quota THEN
Total_Salary:= Base_Salary+ bonus;
END IF;
IF-THEN-ELSE
参见:
IF-THEN, IF-THEN-ELSEIF
语法:
IF condition_1 is true THEN
statement_1,...,statement_n
ELSE
statement_11,...,statement_n1
END IF
变量:
condition_1:适当的条件
statement_1,....,statement_n:如果条件1是真要执行的一序列PL/SQL 语句
statement_11,...,statement_n1:如果条件1是假要执行的一序列PL/SQL 语句
例子:
PL/SQL
IF sales > quota THEN
Total_Salary:= Base_Salary+ Commission +
Bonus;
ELSE
Total_Salary:= Base_Salary+ Commission;
END IF;
IF-THEN-ELSEIF
参见:
IF-THEN-ELSE, IF-THEN-ELSEIF
语法:
IF condition_1 is true THEN
statement_1,...,statement_n
ELSEIF condition_2i,...,true THEN
statement_11,...,statement_n1
ELSE
statement_12,...,statement_n2
END IF
变量:
condition_1:第一个被判断的确定的条件
condition_2:下一个被判断的确定的条件
statement_1,...,statement_n:如果判断condition_1为真才执行的一系列PL/SQL语句
statement_11,...,statement_n1:如果判断condition_2为真才执行的一系列PL/SQL语句
statement_12,...,statement_n2:如果判断condition_1和condition_2都为假才执行的一系列PL/SQL语句
例子:
PL/SQL
IF sales > quota THEN
Total_Salary:= Base_Salary+ Commission +
Bonus;
ELSEIF sales = quota THEN
Total_Salary:= Base_Salary+ Commission;
ELSE
Total_Salary:= Base_Salary;
END IF;.
INITCAP
参见:
LOWER, UPPER
语法:
INITCAP (‘x’)
变量:
x:一个由圆括号所界定的字符变量
例子:
PL/SQL
Var1:= INITCAP (‘oracle isa good database’);
SQL
SELECT INITCAP(‘oracle is agood database.’) “Sentence” FROM
DUAL;
Sentence
--------------------------
Oracle Is A Good Database.
INPUT
参见:
ACCEPT
语法:
I[NPUT] text
变量:
text:你想添加到SQL缓冲区里的文字
例子:
SQL
I WHERE state = ‘FL’
INPUT ORDER BY last_name
INSERT
参见:
DELETE, UPDATE, SELECT,
SELECT INTO
语法:
INSERT INTO {table |(sub_query)}
[(column_1[,column_2,...column_n)]
{VALUES
(sql_expression_1[,sql_expression_2,...,sql_expression_n])
| sub_query);
变量:
table:数据库中用户正在插入数据的表,用户要有INSERT权限
column_1,...,column_n:指定数据库表中将要被插入数据的列
sql_expression_1,...,sql_expression_n:一串有效的SQL表达式序列
例子:
SQL*Plus
INSERT INTO employeesVALUES (‘John Doe’, ‘124561123’,
‘Manager’);
INSERT INTO employees
(employee_name,employee_id)
VALUES
(‘John Doe’, ‘124561123’);
INSERT INTO employees
(SELECT * FROMdepartment_head) ;.
INSTR
参见:
函数INSTRB
语法:
INSTR (‘w’,’x’ [,’y’,’z’])
变量:
w:一个由单括弧界定的字符类型或 VARCHAR2 类型的变量,你将替换掉这个变量里的某些字符。
X:一个由单括弧界定的字符类型或VARCHAR2类型的变量,它是你想查找的变量。
Y:一个表示查找起始位置的数字变量。缺省值为1。这个值是可选的。
Z:一个正的数字变量,它表示查找第几位的字符。缺省为1。这个值是可选的。
例子:
PL/SQL
Var1:= INSTR (‘OracleTraining’, ‘ra’, 1, 2);
SQL
SELECT INSTR (‘OracleTraining’, ‘ra’, 1, 2) “Instring” FROM
DUAL;
Instring
---------
9.
INSTRB
参见:
INSTR
语法:
INSTRB (‘w’,’x’ [,’y’,’z’])
变量:
w:一个由单括弧界定的字符类型或 VARCHAR2 类型的变量,你将替换掉这个变量里的某些字符。.
x:一个由单括弧界定的字符类型或VARCHAR2类型的变量,它是你想查找的变量。
Y:一个表示查找起始位置的数字变量。缺省值为1。这个值是可选的。
Z:一个正的数字变量,它表示查找第几位的字符。缺省为1。这个值是可选的。
例子:
PL/SQL
Var1:= INSTRB (‘OracleTraining’, ‘ra’, 1, 2);
SQL
SELECT INSTRB (‘OracleTraining’, ‘ra’, 1, 2) “Instring Bytes”
FROM DUAL;
Instring Bytes
--------------
9.
INSTRB 函数与INSTR函数几乎一样,除了INSTR 函数返回一个字符类型的值而INSTRB返回一个字节值。INSTRB函数在W里查找字符串x中从第y位开始的第z位字符。
如果y 是负数,oracle就会从w的末尾向前查找。如果没有找到,Oracle返回0。
INTERSECT
参见:
UNION, UNION ALL, MINUS
语法:
b1 INTERSECT b2
变量:
b1 和 b2 是选择语句
例子:
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES WHERE SALES_DATE =
‘09-FEB-96’
INTERSECT
SELECT SALES_AMOUNT FROMDAILY_SALES WHERE SALES_DATE =
‘09-FEB-97’ ;
SHIPPING_T
----------
299.95
INVALID_CURSOR
参见:
CURSOR_ALREADY_OPEN
语法:
EXCEPTION
WHEN INVALID_CURSOR THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n:一个语句序列
例子:
PL/SQL
BEGIN
OPEN loan_cur;
LOOP
FETCH loan_cur INTOloan_rec;
EXIT WHENloan_cur%NOTFOUND;
END LOOP;
EXCEPTION
WHEN INVALID_CURSOR THEN
UPDATE APPLICATION_ERROR_TABLE
SET ERROR =‘INVALID_CURSOR’ ;
END;
INVALID_NUMBER
参见:
VALUE_ERROR, ZERO_DIVIDE
语法:
EXCEPTION
WHEN INVALID_NUMBER THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n:一个语句序列
例子:
PL/SQL
DECLARE
total_salary NUMBER;
BEGIN
total_salary:= TO_NUMBER(‘ABC’);
EXCEPTION
WHEN INVALID_NUMER THEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR = ‘INVALID NUMBER
CONVERSION’ ;
END;
KEYWORDS
Oracle 有一列没有保留但仍在Oracle 语法中使用的单词表。这些单词就是关键字。 你可
以使用这些关键字作为变量名或对象名,不过系统强烈建议你不要这样做,因为这样会使
你的代码难于阅读和理解。
下面就是一个关键字表。
Table A-16 关键字列表
关键字 关键字 关键字 关键字
ADMIN AFTER ALLOCATE ANALYZE
ARCHIVE ARCHIVELOG AUTHORIZATION AVG
BACKUP BEGIN BECOME BEFORE
BLOCK BODY
CACHE CANCEL CASCADE CHANGE
CHARACTER CHECKPOINT CLOSE COBOL
COMMIT COMPILE CONSTRAINT CONSTRAINTS
CONTENTS CONTINUE CONTROLFILE COUNT
CURSOR CYCLE
DATABASE DATAFILE DBA DEC
DECLARE DISABLE DISMOUNT DOUBLE
DUMP
EACH ENABLE END ESCAPE
EVENTS EXCEPT EXCEPTIONS EXEC
EXPLAIN EXECUTE EXTENT EXTERNALLY
FETCH FLUSH FREELIST FREELISTS
FORCE FOREIGN FORTRAN FOUND
FUNCTION
GO GOTO GROUPS
INCLUDING INDICATOR INITRANS INSTANCE
INT
KEY
Keyword Keyword Keyword Keyword
LANGUAGE LAYER LINK LISTS
LOGFILE
MANAGE MANUAL MAX MAXDATAFILES
MAXINISTANCES MAXLOGFILES MAXLOGHISTORY MAXLOGMEMBERS
MAXTRANS MAXVALUE MIN MINEXTENTS
MINVALUE MODULE MOUNT
NEXT NEW NOARCHIVELOG NOCACHE
NOCYCLE NOMAXVALUE NOMINVALUE NONE
NOORDER NORESETLOGS NORMAL NOSORT
NUMERIC
OFF OLD ONLY OPTIMAL
OPEN OWN
PACKAGE PARALLEL PASCAL PCTINCREASE
PCTUSED PLAN PLI PRECISION
PRIMARY PRIVATE PROCEDURE PROFILE
QUOTA
READ REAL RECOVER REFERENCES
REFERENCING RESETLOGS RESTRICTED REUSE
ROLE ROLES ROLLBACK
SAVEPOINT SCHEMA SCN SECTION
SEGMENT SEQUENCE SHARED SNAPSHOT
SOME SORT SQLCODE SQLERROR
STATEMENT_ID STATISTICS STOP STORAGE
SUM SWITCH SYSTEM
TABLES TABLESPACE TEMPORARY THREAD
TIME TRACING TRANSACTION TRIGGERS
TRUNCATE
UNDER UNLIMITED UNTIL USE
USING
WHEN WRITE WORK.
LABELS
参见:
BLOCK
语法:
<<my_label>>
LOOP
statement_1,...,statement_n
END LOOP<<my_label>>
变量:
my_label:一个未声明的标识符
statement_1,...,statement_n:一个语句序列
例子:
PL/SQL
<<calculate_salary>>
LOOP
new_salary:= base_salary +bonus + commission;
END LOOP calculate_salary;
LAST_DAY
参见:
NEXT_DAY, SYSDATE,
ADD_MONTHS
语法:
LAST_DAY (d)
变量:
d:一个有效的日期型变量
例子:
PL/SQL
Days_Left:=LAST_DAY(SYSDATE) - SYSDATE;
SQL
SELECT LAST_DAY(SYSDATE)“Last Day” FROM DUAL;
Last Day
---------
30-NOV-97
LEAST
参见:
GREATEST, GREATEST_LB,
LEAST_LB
语法:
LEAST (expn1 [, expn2] ...)
变量:
expn:任何一个有效的表达式或列。
例子:
SQL
SELECT LEAST(‘JOHN’,‘JONNY’, ‘JANARTHAN’) “LEAST” FROM DUAL;
LEAST
---------
JANARTHAN
LEAST_LB
参见:
GREATEST, GREATEST_LB, LEAST
语法:
LEAST_LB (label1 [, label2]...)
变量:
label:一个 MLSLABEL 类型或 RAW MLSLABEL.类型的变量
LENGTH
参见:
LENGTHB
语法:
LENGTH (‘x’)
变量:
x:一个由单括弧界定的字符类型或 varchar2类型的变量。
例子:
PL/SQL
Var1:= LENGTH (‘Oracle’);
SQL
SELECT LENGTH (‘Oracle’)“Length” FROM DUAL;
Length
---------
6
LENGTHB
参见:
LENGTH
语法:
LENGTHB (‘x’)
变量:
x:一个由单括弧界定的CHAR 类型或VARCHAR2 类型的变量。
例子:
PL/SQL
Var1:= LENGTHB (‘Oracle’);
SQL
SELECT LENGTHB (‘Oracle’)“Length in bytes” FROM DUAL;
Length in bytes
---------------
6
LIKE
参见:
AND, OR, NOT
语法:
WHERE column1 LIKE‘valuepattern’
变量:
column1:一个你要查找的CHAR 或 VARCHAR2类型的列。
Value:那些与你所要查找的模式相似的值。
Pattern:有下面3种模式:
表A-17 LIKE 查找模式示例
模式 例子 查找对象
% ‘abc%’ 以abc开头的值
_ ‘__a’ 在第三位上的是a的值
% ‘%a%a%’ 含有两个a的值
%标号是通配符。
_标志是一个位置标志。
Oracle 中匹配模式对所给条件是相当敏感的。
例子:
SQL
SELECT NAME_OF_AGENT “AGENTNAME” FROM SALES WHERE
NAME_OF_AGENT LIKE‘MICHAEL%’;
“AGENT NAME”
-------------------
MICHAEL GEORGE
MICHAEL JORDAN
MICHAEL MAGNUM
SELECT NAME_OF_AGENT FROMSALES WHERE NAME_OF_AGENT LIKE ‘__C’;
“AGENT NAME”
-------------------
MICKY
ARCHIE
MICHAEL
LIST
参见:
PAUSE
语法:
L[IST] [n|n m|n *|nLAST|*|* n|* LAST|LAST]
变量:
n:SQL*Plus 列出的SQL缓冲区里的第n行。
n m:SQL*Plus 列出的SQL缓冲区里从m行开始的n行。
n *:SQL*Plus 列出的SQL缓冲区里从当前行开始的n行。
n LAST:SQL*Plus 缓冲区里从最后一行开始向前的n行。SQL*Plus 将列出这n行。
*:SQL*Plus将列出的SQL缓冲区里的当前行。
* n:SQL*Plus 缓冲区里从当前行开始向后的n行,SQL*Plus将列出这n行。
* LAST:SQL*Plus 将列出的SQL缓冲区里从当前行开始到最后一行结束的所有行。
LAST:SQL*Plus 将列出的SQL缓冲区里的最后一行。
例子:
SQL
L 2
LN
参见:
LOG
语法:
LN (n)
变量:
n:一个大于0的数字变量。
例子:
PL/SQL
Var1:= LN (12);
SQL
SELECT LN (12) “Logarithmof 12” FROM DUAL;
Logarithm of 12
---------------
2.4849066
LOCK TABLE
参见:
COMMIT, ROLLBACK,
SAVEPOINT, SET TRANSACTION
语法:
LOCK TABLE table_1[,table_2, ..., table_n] IN lock_mode MODE
NOWAIT
变量:
table_1,...,table_n: 一系列你想通过使用LOCK TABLE语句锁住的数据库表。
lock_mode: 对于某一数据库表你要设定的锁定模式。你可以从如下的锁定模式中任选一个。
F EXCLUSIVE
F SHARE ROW EXCLUSIVE
F SHARE
F SHARE UPDATE
F ROW SHARE
F ROW EXCLUSIVE
NOWAIT: Oracle will not wait to lock the given Table(s), if theTable(s) is(are) not
available
例子:
SQL
LOCK TABLE loan IN SHAREMODE ;
LOCK TABLE region INEXCLUSIVE MODE NOWAIT;
LOCK TABLE acct IN SHAREUPDATE MODE;
LOCK TABLE bank IN ROWEXCLUSIVE MODE NOWAIT;
LOCK TABLE user IN SHAREROW EXCLUSIVE MODE;
LOCK TABLE branch IN ROWSHARE MODE NOWAIT;.
LOG
参见:
LN
语法:
LOG (m, n)
变量:
m:任何一个不是0 或 1的正的数字变量。.
n:任何一个正的数字变量。.
例子:
PL/SQL
Var1:= LOG (12, 2);
SQL
SELECT LOG (12, 2) “Logbase 2 of 12” FROM DUAL;
Log base 2 of 12
----------------
.27894295
LOGIN_DENIED
参见:
NOT_LOGGED_ON
语法:
EXCEPTION
WHEN LOGIN_DENIED THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n:一系列的语句
例子:
PL/SQL
BEGIN
OPEN loan_cur;
LOOP
FETCH loan_cur INTOloan_rec;
EXIT WHENloan_cur%NOTFOUND;
END LOOP;
EXCEPTION
WHEN LOGIN_DENIED THEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR = ‘ACCESS DENIED’;
END;
LOOP
参见:
EXIT, WHILE-LOOP,
FOR-LOOP, EXIT-WHEN
语法:
LOOP
statement_1,...,statement_n
END LOOP
变量:
statement_1,...,statement_n:一系列PL/SQL 将重复执行的语句
例子:
PL/SQL
LOOP
Total_Salary:= Base_Salary+ Commission + Bonus;
UPDATE salary SETemployee_salary = Total_Salary WHERE
employee_number:= emp_id;
END LOOP;
LOWER
参见:
INITCAP, UPPER
语法:
LOWER (‘x’)
变量:
x:一个由单括弧界定的字符类型或VARCHAR2类型的变量。
例子:
PL/SQL
Var1:= LOWER (‘ORACLE’);
SQL
SELECT LOWER (‘ORACLE IS AGOOD DATABASE’) “LowerCase Sentence”
FROM DUAL;
LowerCase Sentence
-------------------------
oracle is a good database
LPAD
参见:
RPAD, REPLACE
语法:
LPAD (‘x’, n [,’y’])
变量:
x:一个由单括弧界定的字符类型或VARCHAR2类型的变量。
N:一个正的数字变量。
Y;一个由单括弧界定的字符类型或VARCHAR2l类型的变量。这个变量是可选的。如果不指定,缺省是空字符。.
例子:
PL/SQL
Var1:= LPAD (‘Oracle’, 1);
SQL
SELECT LPAD (‘ is a gooddatabase’, 25, ‘Oracle’) “Example of
Left Padding” FROM DUAL;
Example of Left Padding
-------------------------
Oracle is a good database
LTRIM
参见:
RTRIM
语法:
LTRIM (‘x’ [,’y’])
变量:
x:一个由单括弧界定的字符类型或VARCHAR2类型的变量。
Y:一个由单括弧界定的字符类型或VARCHAR2l类型的变量。这个变量是可选的。如果不指定,缺省是空字符。.
例子:
PL/SQL
Var1:= LTRIM (‘Oracle’,‘Or’);
SQL
SELECT LTRIM (‘The Theresaof all mothers’, ‘The’) “Example of
LTrim” FROM DUAL;
Example of LTrim
-----------------------
Theresa of all mothers
LUB
参见:
GLB
语法:
LUB ( [DISTINCT | ALL] mls)
变量:
DISTINCT:这个选项使函数只考虑那些参数中有不同值的情况,此项是可选的。
ALL:这个选项使函数考虑所有的情况,包括参数中有拷贝值。此项是可选的。
Mls:这是一个 MLSLABEL.类型的变量。
MAKE_REF
参见:
DEREF, REFTOHEX
语法:
MAKE_REF(view, key[,key...])
变量:
view:你想为其中的行创建引用的对象视图。
Key:命令中作为主键的键。
例子:
SQL
CREATE TYPE loan_obj ASOBJECT
(loan_amount NUMBER,interest_rate NUMBER);
CREATE TABLE loan_table
(loan_amount NUMBER,interest_rate NUMBER);
CREATE VIEW loan_view
OF loan_table
WITH OBJECT loan_view_obj(loan_amount,
interest_rate) AS
SELECT * from loan_table;
SELECTMAKE_REF(loan_view_obj, 1, 3) FROM DUAL;
MAX
参见:
MIN, SUM
语法:
MAX ( [DISTINCT | ALL]expn)
变量:
DISTINCT:这个选项使函数只考虑那些参数中有不同值的情况,这项是可选的。
ALL:这个选项使函数考虑所有的情况包括参数中有相同值,这项是可选的。
Expn:这可以是一列SQL查询也可以是一列数学表达式。.
例子:
PL/SQL
Var1:= MAX (1, 2, 3);
SQL
SELECT MAX (DAILY_SALES)“Maximum Sales” FROM SALES;
Maximum Sales
--------------
133
MIN
参见:
MAX, SUM
语法:
MIN ( [DISTINCT | ALL]expn)
变量:
DISTINCT:这个选项使函数只考虑那些参数中有不同值的情况,这项是可选的。
ALL:这个选项使函数考虑所有的情况包括参数中有相同值,这项是可选的。
Expn:这可以是一列SQL查询也可以是一列数学表达式。.
例子:
PL/SQL
Var1:= MIN (1, 2, 3);
SQL
SELECT MIN (DAILY_SALES)“Minimum Sales” FROM SALES;
Minimum Sales
--------------
12
MINUS
参见:
UNION, UNION ALL, INTERSECT
语法:
b1 MINUS b2
变量:
b1 and b2 是SELECT语句。
例子:
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES WHERE SALES_DATE =
‘09-FEB-96’
MINUS
SELECT SALES_AMOUNT FROMDAILY_SALES WHERE SALES_DATE =
‘09-FEB-97’ ;
SHIPPING_T
----------
199.95
MOD
语法:
MOD (x , y)
变量:
x:数字变量
y:数字变量
例子:
PL/SQL
Var1:= MOD (12, 5);
SQL
SELECT MOD (12, 5) “ModulusValue of 12/5” FROM DUAL;
Modulus Value of 12/5
---------------------
2
MONTHS_BETWEEN
参见:
ADD_MONTHS
语法:
MONTHS_BETWEEN (d1, d2)
变量:
d1 & d2:有效的日期型变量
例子:
PL/SQL
MONTHS_INBETWEEN:= SYSDATE- SALES_DATE;
SQL
SELECT MONTHS_BETWEEN(SYSDATE, ‘26-JAN-98’) “MONTHS_INBETWEEN”
FROM DUAL;
MONTHS_INBETWEEN
----------------
-2.53143
NEW_TIME
参见:
SYSDATE
语法:
NEW_TIME (d, ‘tz1’, ‘tz2’)
变量:
d::一个有效的日期型变量
tz1 & tz2::下表中的任一时区
表A-18NEW_TIME的时区说明
时区1 时区2 说明
AST ADT 大西洋标准时间
BST BDT 白令海标准时间
CST CDT 中部标准时间
EST EDT 东部标准时间
GMT 格林尼治标准时间
HST HDT 阿拉斯加—夏威夷标准时间
MST MDT 山区标准时间
NST 纽芬兰标准时间
PST PDT 太平洋标准时间
YST YDT YUKON标准时间
例子:
PL/SQL
LONDON_TIME:= NEW_TIME (SYSDATE,‘EST’, ‘GMT’)
SQL
SELECT NEW_TIME (SYSDATE,‘EST’, ‘GMT’) “LONDON_TIME” FROM
DUAL;
LONDON_TI
---------
09-NOV-97.
NEXT_DAY
参见:
LAST_DAY, SYSDATE
语法:
NEXT_DAY (d, ‘x’)
变量:
d::一个有效的日期型变量
x: 一个有括号界定的字符变量,它代表星期中的某一天
(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, andSUNDAY)
例子:
PL/SQL
DATE_FOR_NEXT_WEEK:=NEXT_DAY(‘26-JAN-47’,’TUESDAY’);
SQL
SELECTNEXT_DAY(‘26-JAN-47’,’TUESDAY’) “DATE_AFTER_TUESDAY_26-
JAN-47” FROM DUAL;
DATE_AFTER
---------
28-JAN-47
NEXTVAL
参见:
CREATE SEQUENCE,
CURRVAL, PSEUDOCOLUMN
语法:
[user.]sequence.NEXTVAL
变量:
user: 创建序列的用户
sequence: 你要从中取出下一个值的序列
例子:
SQL*Plus
SELECT loan_seq.NEXTVALFROM DUAL;
NLS_CHARSET_DECL_LEN
参见:
DELETE, UPDATE, SELECT, SELECT INTO
语法:
NLS_CHARSET_DECL_LEN (len,char_id)
变量:
len: 列的长度
char_id::列的ID字符集合
例子:
SQL
SELECT NLS_CHARSET_DECL_LEN(130, nls_charset_id (‘char_cs’))
FROM DUAL;
NLS_CHARSET_ID
参见:
NLS_CHARSET_DECL_LEN,
NLS_CHARSET_NAME
语法:
NLS_CHARSET_ID (‘x’)
变量:
x: 一个由括号界定的字符串变量
例子:
SQL
SELECT NLS_CHARSET_ID(‘char_cs’)) FROM DUAL;
SELECT NLS_CHARSET_ID(‘nchar_cs’)) FROM DUAL;
NLS_CHARSET_NAME
参见:
NLS_CHARSET_DECL_LEN,
NLS_CHARSET_ID
语法:
NLS_CHARSET_NAME (n)
变量:
n::一个有效的数值变量
例子:
SQL
SELECT NLS_CHARSET_NAME (2)FROM DUAL;
SELECT NLS_CHARSET_NAME(1001) FROM DUAL;
NLS_INITCAP
参见:
NLS_LOWER, NLS_UPPER
语法:
NLS_INITCAP (‘x’[,’nlsparm’])
变量:
x::一个由单括弧界定的字符变量
nlsparm:这个变量确定了排序规则。它可以是语言的排序顺序也可以是 BINARY排序顺序,语言上的排序顺序主要是针对那些由于事例转换而引起的特殊语言的要求。这个参数是可选的,如果没有给定它,函数就会在此会话期间使用缺省的排序顺序。‘nlsparams’ 值可以有下述形式:
‘NLS_SORT = sort’
例子:
SQL
SELECT NLS_INITCAP(‘strutz’,‘NLS_SORT = XGerman’) “Sentence”
FROM DUAL;
Senten
------
Strutz
NLS_LOWER
参见:
NLS_INITCAP, NLS_UPPER
语法:
NLS_LOWER (‘x’[,’nlsparm’])
变量:
x::一个由单括弧界定的字符变量
nlsparm:这个变量确定了排序规则。它可以是语言的排序顺序也可以是 BINARY排序顺序,语言上的排序顺序主要是针对那些由于事例转换而引起的特殊语言的要求。这个参数是可选的,如果没有给定它,函数就会在此会话期间使用缺省的排序顺序。‘nlsparams’ 值可以有下述形式:
‘NLS_SORT = sort’
例子:
SQL
SELECT NLS_LOWER (‘strutz’,‘NLS_SORT = XGerman’) “LowerCase
Sentence” FROM DUAL;
LowerC
------
strutz
NLS_UPPER
参见:
NLS_INITCAP, NLS_LOWER
语法:
NLS_UPPER (‘x’[,’nlsparm’])
变量:
x::一个由单括弧界定的字符变量
nlsparm:这个变量确定了排序规则。它可以是语言的排序顺序也可以是 BINARY排序顺序,语言上的排序顺序主要是针对那些由于事例转换而引起的特殊语言的要求。这个参数是可选的,如果没有给定它,函数就会在此会话期间使用缺省的排序顺序。‘nlsparams’ 值可以有下述形式:
‘NLS_SORT = sort’
例子:
SQL
SELECT NLS_UPPER (‘strutz’,‘NLS_SORT = XGerman’) “UpperCase
Sentence” FROM DUAL;
UpperC
------
STRUTZ
NO_DATA_FOUND
参见:
TOO_MANY_ROWS
语法:
EXCEPTION
WHEN NO_DATA_FOUND THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n::一系列的语句
例子:
PL/SQL
BEGIN
OPEN loan_cur;
LOOP
FETCH loan_cur INTOloan_rec;
EXIT WHEN loan_cur%NOTFOUND;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20001,
‘No Data Found’);
END;
NOAUDIT
参见:
AUDIT
语法:
NOAUDIT statement |system_privilege BY user [WITH
GRANT OPTION] [WHENEVER[NOT] SUCCESSFUL]
NOAUDIT object_operating ONschema.object [WHENEVER
[NOT] SUCCESSFUL]
变量:
statement::你想停止检查的语句
system_privilege::你想停止检查的系统权限
user::你想停止检查的用户
例子:
PL/SQL
NOAUDIT SELECT TABLE BYjohn;
NOAUDIT CREATE TABLE BYmartha;
NOAUDIT SELECT ONloanschema.loan WHENEVER SUCCESSFUL;.
NOT_LOGGED_ON
参见:
LOGIN_DENIED
语法:
EXCEPTION
WHEN NOT_LOGGED_ON THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n::一系列的语句
例子:
PL/SQL
BEGIN
OPEN loan_cur;
LOOP
FETCH loan_cur INTOloan_rec;
EXIT WHENloan_cur%NOTFOUND;
END LOOP;
EXCEPTION
WHEN NOT_LOGGED_ON THEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR = ‘NOT LOGEED ON’;
END;
NULL
参见:
ASSIGNMENT, DECLARE
语法:
IF condition_1 is true THEN
statement_1,...,statement_n
ELSE
NULL
END IF
变量:
condition_1:一个PL/SQL 将判断的确定的条件
statement_1,...,statement_n: 一系列如果condition_1 为真 PL/SQL 就迭代执行的语句。
例子:
PL/SQL
IF sales > quota THEN
Total_Salary:= Base_Salary+ Commission +
Bonus;
ELSE
NULL;
END IF;
NVL
参见:
函数NULL
语法:
NVL (value, substitute)
变量:
value::任何一种类型的变量,包括 CHAR, VARCHAR2, NUMBER, DATE, 和其它。
substitute: 与 value相同类型的变量,如果substitute 的类型与value不一样此函数就会首先对它进行类型转换。
例子:
SQL
SELECT NVL(SALES_AGENT,‘AGENT NAME NOT AVAILABLE’) “NAMES OF
SALES AGENT” FROM SALES;
NAMES OF SALES AGENT
-----------------------------------
JOHN HOPKINS
MICHAEL JORDAN
AGENT NAME NOT AVAILABLE
OPEN
参见:
CLOSE, OPEN-FOR, FETCH
语法:
OPEN cursor_name[(parameter_1 [,parameter_2,...,
parameter_n]...)]
变量:
cursor_name: 你要打开的指定游标的名字。
parameter_1,...,parameter_n: 你要传递给游标的参数。
例子:
PL/SQL
OPEN loan_cur;
OPEN loan_cur (1, ‘BankHoldings, Inc.’, 500000)
OPEN-FOR
参见:
CLOSE, OPEN, FETCH
语法:
OPEN {cursor_name|:host_cursor_variable_name} FOR
select_statement;
变量:
cursor_name:你想打开的游标名字。
host_cursor_variable_name: 在一个 PL/SQL 主环境中声明的游标变量的名字。
host environment
select_statement: 一个有效的 SELECT 语句,它返回了传送给游标的值。
例子:
PL/SQL
OPEN loan_cur FOR SELECT *FROM loan;
OPEN:emp_cur FOR SELECT *FROM employee;
OPEN salary_cur FOR SELECT* FROM salary;
运算符
0 + - PRIOR
1 * /
2 =
3 > < !=
4 NOT
5 AND
6 OR.
运算符— < >
参见:
=, <, <=, >, >=
语法:
b1 <> b2
变量:
b1, b2: 数字变量。
例子:
PL/SQL
IF DAILY_SALES <>(MONTHLY_SALES/30)
THEN
TARGET: = -1
END IF;
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE <>‘09-FEB-96’;
DISCOUNT_T
----------
1222.12
运算符—>
参见:
=, !=,<=, <, >=
语法:
b1 > b2
变量:
b1, b2::数字变量。
例子:
PL/SQL
IF DAILY_SALES >(MONTHLY_SALES/30) THEN
TARGET: = -1
END IF;
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE >‘09-FEB-96’;
DISCOUNT_T
----------
168.5
运算符—> =
参见:
=, !=, <=, <, >
语法:
b1 >= b2
变量:
b1, b2: 数字变量。
例子:
PL/SQL
IF DAILY_SALES >=(MONTHLY_SALES/30) THEN
TARGET: = -1
END IF;
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE >=‘09-FEB-96’;
DISCOUNT_T
----------
2268.5
运算符—! =
参见:
=, <, <=, < >, >, >=
语法:
b1 != b2
变量:
b1, b2:数字变量。
例子:
PL/SQL
IF DAILY_SALES !=(MONTHLY_SALES/30) THEN
TARGET: = -1
END IF;
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE !=‘09-FEB-96’;
DISCOUNT_T
----------
1222.12
运算符—*
参见:
+, -, /
语法:
b1 * b2
变量:
b1, b2:数字变量。
例子:
PL/SQL
AnnualSalary:=MonthlySalary * 12;
DiscountAmount:=ProductValue *
DiscountPercent;
SQL*Plus
SELECT SALES_AMOUNT * 10DISCOUNT_TOTAL
FROM DAILY_SALES
WHERE SALES_DATE =‘09-FEB-96’;
DISCOUNT_T
----------
6850
运算符—+
参见:
-, *, /
语法:
b1 + b2 + ... + bn
变量:
b1, b2,..., bn::数字变量。
例子:
PL/SQL
NewSalary:= CurrentSalary +Bonus + Commission + Raise;
TotalPayment:= Mortgage +Interest + RealEstateTaxes +
Insurance;
SQL
SELECT SALES_AMOUNT + 4.95SHIPPING_TOTAL
FROM DAILY_SALES
WHERE SALES_DATE =‘09-FEB-96’;
SHIPPING_T
----------
699.95
运算符—-*
参见:
-, *, /
语法:
b1 - b2
变量:
b1, b2:数字变量。
例子:
PL/SQL
NetProfit:= Revenue - Expenses;
NetPay:= GrossIncome -Taxes;
SQL
SELECT SALES_AMOUNT - 10.00DISCOUNT_TOTAL
FROM DAILY_SALES
WHERE SALES_DATE =‘09-FEB-96’;
DISCOUNT_T
----------
685
运算符—/
参见:
+, -, *
语法:
b1 / b2
变量:
b1, b2:数字变量。
例子:
PL/SQL
MonthlySalary:=AnnualSalary / 12;
SQL
SELECT SALES_AMOUNT / 10DISCOUNT_TOTAL
FROM DAILY_SALES
WHERE SALES_DATE =‘09-FEB-96’;
DISCOUNT_T
----------
68.5
运算符—<=
参见:
=, !=,<, >, >=
语法:
b1 <= b2
变量:
b1, b2::数字变量。
例子:
PL/SQL
IF DAILY_SALES <=(MONTHLY_SALES/30) THEN
TARGET: = -1
END IF;
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE <=‘09-FEB-96’;
DISCOUNT_T
----------
168.5
运算符—=
参见:
!=, <,<=, < >, >, >=
语法:
b1 = b2
变量:
b1, b2:数字变量。
例子:
PL/SQL
IF DAILY_SALES =(MONTHLY_SALES/30)
THEN
TARGET: = 1
END IF;
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE =‘09-FEB-96’;
DISCOUNT_T
----------
68.5
运算符—AND
参见:
OR, NOT
语法:
b1 AND b2
变量:
b1, b2::任何变量或结果。
例子:
PL/SQL
IF (DAILY_SALES =MONTHLY_SALES/30) AND
(ANNUAL_SALES > 0 )THEN
BONUS: = 100
END IF;
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE <=‘09-FEB-96’ AND SALES_DATE >= ‘09-MAR-96’;
DISCOUNT_T
----------
368.5
运算符—BETWEEN
参见:
IN, NOT IN, ANY,ALL, BETWEEN
语法:
WHERE column BETWEEN n ANDm
变量:
n and m:任何相同的数据类型的变量。
例子:
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE BETWEEN‘09-FEB-96’ AND ‘10-FEB-96’;
DISCOUNT_T
----------
68.5
12.5
运算符—IN
参见:
NOT IN, ANY, ALL, BETWEEN
语法:
WHERE column IN
例子:
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE IN(‘09-FEB-96’, ‘10-FEB-96’);
DISCOUNT_T
----------
68.5
12.5
运算符—IS NOT NULL
参见:
IS NULL
语法:
WHERE column IS NOT NULL
例子:
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE IS NOTNULL;
DISCOUNT_T
----------
12.2
15.3
运算符—IS NULL
参见:
IS NOT NULL
语法:
WHERE column IS NULL
例子:
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE IS NULL;
DISCOUNT_T
----------
10.2
运算符—NOT
参见:
AND, OR
语法:
b1 NOT b2
变量:
b1, b2:结果。
例子:
PL/SQL
IF NOT (DAILY_SALES !=MONTHLY_SALES/30) THEN
BONUS: = 100
END IF;
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE NOT (SALES_DATE =‘09-FEB-96’);
DISCOUNT_T
----------
4468.5
运算符—NOT BETWEEN
参见:
其中运算符,如 IN, NOT IN, ANY, ALL,
BETWEEN
语法:
WHERE column NOT BETWEEN nAND m
变量:
n and m:任何相同数据类型的变量。
例子:
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE NOTBETWEEN ‘09-FEB-96’ AND ‘10-FEB-96’;
DISCOUNT_T
----------
13
144.12
运算符—NOT IN
参见:
其中运算符,如 IN, ANY, ALL, BETWEEN
语法:
WHERE column NOT IN
例子:
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE NOT IN(‘09-FEB-96’, ‘10-FEB-96’);
DISCOUNT_T
----------
100.12
62.5
运算符—OR
参见:
AND, NOT
语法:
b1 OR b2
变量:
b1, b2:任何变量或结果。
例子:
PL/SQL
IF (DAILY_SALES =MONTHLY_SALES/30) OR (ANNUAL_SALES >
0 )THEN
BONUS: = 100
END IF;
SQL
SELECT SALES_AMOUNT FROMDAILY_SALES
WHERE SALES_DATE <=‘09-FEB-96’ OR SALES_DATE >= ‘09-MAR-96’;
DISCOUNT_T
----------
3368.5
PRIOR
参见:
CONNECT BY
语法:
SELECT sql_expn FROM[user.]table WHERE where_condition
CONNECT BY [PRIOR] expn =[PRIOR] expn
START WITH expn = expn
ORDER BY expn
变量:
sql_expn::一个有效的 SQL 表达式。
User:表的所有者。
Table: SQL 将再其上执行SELECT的表。
where_condition:SQL SELECT 的WHERE条件。
Expn: 任何有效的表达式。
例子:
SQL
SELECT employee_name,department_name FROM employee
CONNECT BY emp_no = PRIORdepartment_no
ORDER BY department_no;
PROGRAM_ERROR
参见:
NO_DATA_FOUND,
TOO_MANY_ROWS,
STORAGE_ERROR
语法:
EXCEPTION
WHEN PROGRAM_ERROR THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n:一系列的语句。
例子:
PL/SQL
BEGIN
OPEN loan_cur;
LOOP
FETCH loan_cur INTOloan_rec;
EXIT WHENloan_cur%NOTFOUND;
END LOOP;
EXCEPTION
WHEN PROGRAM_ERROR THEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR = ‘INTERNALPROGRAM ERROR’ ;
END;
PROMPT
参见:
语法:
PROMPT [text]
变量:
text:你要SQL*Plus 显示的一行文本。
例子:
SQL
PROMPT
PROMPT The system will beshut down within 10 minutes.
PSEUDOCOLUMN
参见:
CURRVAL, LEVEL, NEXTVAL,
NULL, ROWID, ROWNUM,
SYSDATE, UID, USER
例子:
See sections on specific pseudocolumns for 例子:.
RAISE
参见:
EXCEPTIONS
语法:
RAISE exception_name
变量:
exception_name:一个的或用户定义的异常。
例子:
PL/SQL
IF past_due > 90 THEN
RAISE past_due_exception;
END IF;
RAWTOHEX
参见:
HEXTORAW
语法:
RAWTOHEX (r)
变量:
r:一个 RAW.类型的变量。
例子:
SQL
SELECT RAWTOHEX(animation)“Animation” FROM animation;
Animation
----------------------------------------
3D
RECORD
参见:
TABLE
语法:
TYPE type_name IS RECORD OF
( field_name1 {field_type |variable%TYPE |
table.column%TYPE |table%ROWTYPE }
[NOT NULL],
field_name2 {field_type |variable%TYPE |
table.column%TYPE |table%ROWTYPE }
[NOT NULL] )
变量:
type_name:一个在记录的后继声明中使用的类型标识符。
field_type:任何包括RECORD和TABLE的数据类型。
例子:
PL/SQL
DECLARE
TYPE LoanRecTyp IS RECORD
( cname CHAR(50),
loan_amount NUMBER (5));
loan_rec LoanRecTyp;
BEGIN
SELECT cust_name, loan_amtINTO loan_rec FROM
LOAN;
END;
DECLARE
TYPE TimeTyp IS RECORD
( minute SMALLINT,
hour SMALLINT);
TYPE MeetingTyp IS RECORD
( day DATE,
time TimeTyp,
place CHAR(20));
meeting MeetingTyp;
BEGIN
meeting.day := ‘26-JAN-51’;
meeting.time.minute := 45;
meeting.time.hour := 12;
END;.
REFTOHEX
参见:
DEREF, MAKE_REF
语法:
REFTOHEX (expn)
变量:
expn:任何一个返回对象引用的表达式。
例子:
SQL
CREATE TABLE customer_tab(customer_no NUMBER,
loan REF loan_tab);
SELECT REFTOHEX (loan) FROMcustomer_tab;
REMARK
参见:
--, /* */
语法:
REM[ARK] [comment]
变量:
comment:你要包括在命令文件中的注释。
例子:
SQL
REM
REM Loan accountinformation
RENAME
参见:
COPY
语法:
RENAME old TO new
变量:
old::你要命名的表、视图、序列或私有同义词。
New:表、视图、序列或私有同义词的新名字。
例子:
SQL
RENAME accounting TOloanaccts
RENAME marketing TO sales
REPFOOTER
参见:
BTITLE, TTITLE, REPHEADER
语法:
REPF[OOTER] [PAGE][printspec [text | variable]] | [OFF|ON]
变量:
printspec:printspec包括下面这些从句,SQL*Plus用它们来放置和格式化报表脚注的文本。
F COL n
F S[KIP] [n]
F TAB n
F LE[FT]
F CE[NTER]
F R[IGHT]
F BOLD
F FORMAT text
Text:报表脚注的文本。
Variable:含有下列系统保留值的文本。
Values:
F SQL.LNO (current line number)
F SQL.PNO (current page number)
F SQL.RELEASE (current Oracle release number)
F SQL.SQLCODE (current error code)
F SQL.USER (current Username)
例子:
SQL
REPFOOTER PAGE RIGHT ‘ENDOF REPORT’
REPFOOTER OFF
REPFOOTER ON.
REPHEADER
参见:
BTITILE, TTITLE, REPFOOTER
语法:
REPH[EADER] [PAGE][printspec [text|variable] ] | [OFF|ON]
变量:
printspec:包括下列从句, SQL*Plus用它们来放置和格式化报表表头的文本。
F COL n
F S[KIP] [n]
F TAB n
F LE[FT]
F CE[NTER]
F R[IGHT]
F BOLD
F FORMAT text
Text:报表表头文本。
Variable:含有下列系统保留值的变量。
Values:
F SQL.LNO (current line number)
F SQL.PNO (current page number)
F SQL.RELEASE (current Oracle release number)
F SQL.SQLCODE (current error code)
F SQL.USER (current Username)
例子:
SQL
REPHEADER PAGE CENTER‘TOTAL SALES BY REGION’
REPHEADER PAGE BOLD ‘TOTALUNITS SOLD’
REPHEADER OFF
REPHEADER ON.
REPLACE
参见:
TRANSLATE
语法:
REPLACE (‘x’ [,’y’,’z’])
变量:
x:应该在单引号中指定的字符型或varchar2 型变量,你想在这个变量中替换字符。
Y:应该在单引号中指定的字符型或varchar2 型变量,它指向你想替换的串。它是可选的。
Z:应该在单引号中指定的字符型或varchar2 型变量,它指向你想用来替换的串。它是可选的。
例子:
PL/SQL
Var1:= REPLACE (‘Oracle’, ‘Or’,‘Mir’,);
SQL
SELECT REPLACE (‘Oracle’,‘Or’, ‘Mir’) “Example “ FROM DUAL;
Example
-------
Miracle
REPLACE
参见:
TRANSLATE
语法:
REPLACE (‘x’ [,’y’,’z’])
变量:
x:应该在单引号中指定的字符型或varchar2 型变量,你想在这个变量中替换字符。
Y:应该在单引号中指定的字符型或varchar2 型变量,它指向你想替换的串。它是可选的。
Z:应该在单引号中指定的字符型或varchar2 型变量,它指向你想用来替换的串。它是可选的。
例子:
PL/SQL
Var1:= REPLACE (‘Oracle’,‘Or’, ‘Mir’,);
SQL
SELECT REPLACE (‘Oracle’,‘Or’, ‘Mir’,); ‘’Example of replacing
strings’’ FROM DUAL;
Example of replacingstrings
----------------------------
Miracle
RETURN
参见:
Functions, Procedures, RAISE
语法:
RETURN [expression]
变量:
expression:一个有效表达式。有效表达式包括变量,操作符,文字,函数调用或常量。
例子:
PL/SQL
RETURN loan_bal;
RETURN 5;
REVOKE
参见:
GRANT
语法:
REVOKE system_privilege |role FROM user | role | PUBLIC
REVOKE system_privilege |role FROM user | role | PUBLIC
REVOKE object_privilege |ALL ON schema.object FROM user
| role | PUBLIC CASCADECONSTRAINTS
变量:
system_privilege:从用户或角色收回的系统权限。
object_privilege:从用户或角色收回的系统权限。对象权限可为如下之一:
F ALTER
F DELETE
F EXECUTE
F INDEX
F INSERT
F REFERENCES
F SELECT
F UPDATE
Role:从用户收回的角色。
例子:
SQL
REVOKE ALTER TABLESPACEFROM john;
REVOKE GRANT ANY ROLE FROMtodd;
REVOKE manager FROM imran;
REVOKE INSERT ON sales FROMjaved;
REVOKE ALL ON marketingFROM terry;.
ROLLBACK
参见:
COMMIT, SAVEPOINT,
SET TRANSACTION, LOCK TABLE
语法:
ROLLBACK [WORK ] [TO[SAVEPOINT] savepoint_work]
变量:
WORK:可选关键字。使用这个关键字没有实际影响。它仅仅使这条语句看起来更完整一些。.
SAVEPOINT:可选关键字。使用这个关键字没有实际影响。它仅仅使这条语句看起来更完整一些。
savepoint_work:在当前事务中的在此之前的改变可以由 ROLLBACK语句取消。
例子:
SQL
ROLLBACK;
ROLLBACK WORK;
ROLLBACK TO loan_changes;
ROLLBACK TO SAVEPOINTloan_changes;
ROLLBACK WORK TO SAVEPOINTloan_changes;
ROUND
参见:
TRUNC
语法:
ROUND (d [, ‘fmt’])
变量:
d:有效的日期型变量。
Fmt:在引号中指定的字符变量、表示返回日期的方式。.
表A-20ROUND 命令的变量
方式 结果
CC, SCC 世纪
SYYYY, YYYY, YEAR, SYEAR, YYY, YY Y 年 (对6月1日进行取整)
IYYY, IY, IY I ISO 年
Q 季度 (对季度的第二个月份的16号进行取整)
MONTH, MON, MM, RM 月份(对月份的16号进行取整)
WW 将某周的某一天作为一年的第一天
WN 将某周的某一天作为一年的第一天
IW 将某周的某一天作为ISO年的第一天
W 将某周的某一天作为某月的第一天
DDD, DD, J 按天向上取整
DAY, DY, D 按周的第一天取整
HH, HH12, HH24 按小时取整
MI 按分钟取整
例子:
PL/SQL
FISCAL_YEAR:= ROUND(SYSDATE,’YEAR’);
SQL
SELECT ROUND(SYSDATE,’YEAR’) “CURRENT_FISCAL_YEAR” FROM DUAL;
CURRENT_F
---------
01-JAN-98
PL/SQL
Var1:= ROUND (124.1666, 2);
SQL
SELECT ROUND (124.16666,-2) “Rounded Value” FROM DUAL;
Rounded Value
-------------
100
ROWIDTOCHAR
参见:
CHARTOROWID
语法:
ROWIDTOCHAR (r)
变量:
r:ROWID类型的变量。.
例子:
SQL
SELECT ROWIDTOCHAR (ROWID)“VARCHAR2 TYPE” FROM EMPLOYEE;
VARCHAR2 TYPE
------------------
00000545.0001.0005
00000546.0000.0005
ROWTYPE_MISMATCH
参见:
NO_DATA_FOUND,
TOO_MANY_ROWS
语法:
EXCEPTION
WHEN ROWTYPE_MISMATCH THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n:语句序列。
例子:
PL/SQL
BEGIN
OPEN loan_cur;
LOOP
FETCH loan_cur INTOemp_rec;
EXIT WHENloan_cur%NOTFOUND;
END LOOP;
EXCEPTION
WHEN ROWTYPE_MISMATCH THEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR = ‘ROWTYPE_MISMATCH IN
CURSOR’ ;
END;
RPAD
参见:
LPAD, REPLACE
语法:
RPAD (‘x’, n [,’y’])
变量:
x:应该在单引号中指定的字符或varchar2 型变量。
n:正的数字变量。
y:应该在单引号中指定的字符或varchar2 型变量。该变量为可选的。若未指定,则缺省值为空字符。
例子:
PL/SQL
Var1:= RPAD (‘Oracle’, 1);
SQL
SELECT RPAD (‘Oracle is agood ‘, 25, ‘database’) “Example of
Right Padding” FROM DUAL;
Example of Right Padding
-----------------------
Oracle is a good database
RTRIM
参见:
LTRIM
语法:
RTRIM (‘x’ [,’y’])
变量:
x:应该在单引号中指定的字符型或varchar2 型变量。
Y:应该在单引号中指定的字符型或varchar2 型变量。.
这个变量是可选的。若未指定,则缺省值为空字符。
例子:
PL/SQL
Var1:= RTRIM (‘Oracle’,‘Or’);
SQL
SELECT RTRIM (‘MotherTheresa, The’, ‘The’) “Example of Right
Trimming” FROM DUAL;
Example of Right
----------------
Mother Theresa,
SAVE
参见:
SET, EDIT, GET
语法:
SAV[E] filename[.ext][CRE[ATE]|REP[LACE]|APP[END]]
变量:
filename:你将把缓冲区中的内容存入的文件。
ext:若使用文件后缀,缺省的文件后缀为SQL。
例子:
SQL
SAVE LOANRPT
SAVE ACCT.NEW
SAVEPOINT
参见:
COMMIT, ROLLBACK, SET
TRANSACTION, LOCK TABLE
语法:
SAVEPOINT savepoint_work
变量:
savepoint_work:在当前事务中用于存储当前的未声明的标识符。
例子:
SQL
SAVEPOINT loan_changes;
SAVEPOINT update_loan;
SELECT
参见:
基本查询语句 SELECT INTO, DECLARE
CURSOR, FETCH
语法:
SELECT [DISTINCT | ALL] {*| column1[, column2]...}
FROM {table_1 | (subquery)}[alias]
[, {table_2 | (subquery)}[alias]]...
[WHERE condition]
[CONNECT BY condition[START WITH condition]
[GROUP BY expn] [HAVINGexpn]
[{ UNION [ALL] | INTERSECT| MINUS } SELECT . . . ]
[ ORDER BY [expn ] [ ASC |DESC]
[ FOR UPDATE [OF[user.]table | view] column ]
[NOWAIT]
变量:
item_1,...,item_n:SELECT 语句选择的列。
table_1,...,table_n:用于选择数据的数据库表。
sub_query:向SELECT 语句提供值和值集的 SELECT语句。
Alias:一般是指表的短名字, 或者是在 SELECT语句中引用的视阈。
column_1,...,column_n:给定数据和修改表中的指定列。
where_condition: 有效的 WHERE 子句。
例子:
PL/SQL
SELECT employee_id,employee_name, employee_title
FROM employees
WHERE employee_id = emp_id
ORDER BY employee_id;.
SELECT INTO
参见:
SELECT, DECLARE
CURSOR, FETCH
语法:
SELECT [DISTINCT | ALL] {*| item_1[, item_2]...}
INTO {variable_1[,variable_2]... | record_1}
FROM {table_1 | (subquery)}[alias]
[, {table_2 | (subquery)}[alias]]...
rest_of_select_statement;
变量:
item_1,...,item_n:SELECT INTO 语句选择并存入给定变量中的项。
variable_1,...,variable_n:将保存数据的变量。
record_1:用户定义的记录或 %ROWTYPE记录使用 SELECT INTO 语句将行的值提取出并置放在该记录中。
table_1,...,table_n:从其中选择数据的数据库表。
sub_query:向SELECT INTO 语句提供值或侄集的SELECT语句。
Alias:一般是SELECT语句中引用的表,或视图的短名字。
column_1,...,column_n:指定将要被修改表的列。
sql_expression:有效的 SQL 表达式。
WHERE search_condition:有效的WHERE 子句,用于指定s将要被修改的行。
CURRENT OF cursor_name:与cursor_name指定的游标相关的 FETCH语句处理的当前行。
rest_of_select_statement:在SELECT 语句中FROM子句之后的合法语句。
例子:
PL/SQL
SELECT employee_name,employee_id,
employee_title
INTO e_name, e_id, e_title
FROM employees
WHERE employee_id =emp_id;.
SET
参见:
EDIT, GET, SAVE
语法:
SET variable value
变量:
value:系统变量的值。
Variable:可以用 SQL*Plus 设置值的系统变量。该变量可以取下列的系统变量。
表A_21 SQL*Plus SET 命令中的变量
变量 语法
APPI[NFO] {ON|OFF|text}
ARRAY[SIZE] {20|n}
AUTO[COMMIT] {OFF|ON|IMM[EDIATE]|n}
AUTOP[RINT] {OFF|ON}
AUTOT[RACE] {OFF|ON|TRACE[ONLY]}
EXP[LAIN] [STAT[ISTICS]]
BLO[CKTERMINATOR] {.|c}
CLOSECUR[SOR] {OFF|ON}
CMDS[EP] {;|c|OFF|ON}
COLSEP {_|text}
COM[PATIBILITY] {V6|V7|NATIVE}
CON[CAT] {.|c|OFF|ON}
COPYC[OMMIT] {0|n}
COPYTYPECHECK {OFF|ON}
CRT crt
DEF[INE] {‘&’|c|OFF|ON}
ECHO {OFF|ON}
EDITF[ILE] file_name[.ext]
EMBEDDED {OFF|ON}
ESC[APE] {\|c|OFF|ON}
FEED[BACK] {6|n|OFF|ON}
FLAGGER {OFF|ENTRY|INTERMED[IATE]|FULL}
FLU[SH] {OFF|ON}
HEA[DING] {OFF|ON}
HEADS[EP] {||c|OFF|ON}
LIN[ESIZE] {80|n}
LONG {80|n}
LONGC[HUNKSIZE] {80|n}
MAXD[ATA] n
NEWP[AGE] {1|n}
NULL text
NUMF[ORMAT] format
NUM[WIDTH] {10|n}
PAGES[IZE] {24|n}
PAU[SE] {OFF|ON|text}
RECSEP {WR[APPED]|EA[CH]|OFF}
RECSEPCHAR {_|c}
SERVEROUT[PUT] {OFF|ON} [SIZE n] [FOR[MAT]{WRA[PPED]|
WOR[D_WRAPPED]|TRU[NCATED]}].
SHOW[MODE] {OFF|ON}
SQLC[ASE] {MIX[ED]|LO[WER]|UP[PER]}
SQLCO[NTINUE] {> |text}
SQLN[UMBER] {OFF|ON}
SQLPRE[FIX] {#|c}
SQLP[ROMPT] {SQL>|text}
SQLT[ERMINATOR] {;|c|OFF|ON}
SUF[FIX] {SQL|text}
TAB {OFF|ON}
TERM[OUT] {OFF|ON}
TI[ME] {OFF|ON}
TIMI[NG] {OFF|ON}
TRIM[OUT] {OFF|ON}
TRIMS[POOL] {ON|OFF}
UND[ERLINE] {-|c|ON|OFF}
VER[IFY] {OFF|ON}
WRA[P] {OFF|ON}
例子:
SQL
SET AUTOPRINT ON
SET AUTOCOMMIT OFF
SET APPINFO ON
SET PAUSE ‘Enter the lastname of the customer’
SET TIMING OFF.
SET ROLE
参见:
ALTER SESSION, ALTER SYSTEM
语法:
SET ROLE role_nameIDENTIFIED BY password
SET ROLE ALL
SET ROLE ALL EXCEPTrole_name
SET ROLE NONE
变量:
role_name:你想在当前会话中设置的角色。
password: 你要设置的角色的口令。
例子:
SQL
SET ROLE manager IDENTIFIEDBY magic;
SET ROLE ALL;
SET ROLE ALL EXCEPTmanager;
SET ROLE NONE;
SET TRANSACTION
参见:
COMMIT, ROLLBACK,
SAVEPOINT, LOCK TABLE
语法:
SET TRANSACTION
{ READ ONLY | READ WRITE |ISOLATION LEVEL {SERIALIZABLE
| READ COMMITTED}
| USE ROLLBACK SEGMENTrollback_segment_name}
变量:
rollback_segment_name:回滚段的名字。
例子:
SQL
SET TRANSACTION READ ONLY;
SET TRANSACTION READ WRITE;
SHOW
参见:
SET
语法:
SHO[W] option
变量:
option:该选项可以为下列子句之一:
F ALL
F BTI[TLE]
F ERR[ORS]
F [{FUNCTION|PROCEDURE|PACKAGE|PACKAGE
BODY|TRIGGER|VIEW}schema.]name]
F LABEL
F LNO
F PNO
F REL[EASE]
F REPF[OOTER]
F REPH[EADER]
F SPOO[L]
F SQLCODE
F TTI[TLE]
F USER
例子:
SQL
SHOW USER
SHOW LNO
SHOW BTITLE
SHOW LABEL
SHOW LINESIZE
SHOW ALL.
SIGN
参见:
ABS
语法:
SIGN (x)
变量:
x: 数字型变量。描述SIGN函数返回值的符号。下表表示三种可能情况下的结果值。
表A_22 SIGN函数在数的范围上的结果
x的值 结果
< 0 -1
0 0
> 0 1
例子:
PL/SQL
Var1:= SIGN (3);
SQL
SELECT SIGN (-5) “SIGN OF-5” FROM DUAL;
SIGN OF -5
----------
-1.
SIN
参见:
COS, COSH, SINH,
TAN, TANH
语法:
SIN (n)
变量:
n:数字型变量。
例子:
PL/SQL
Var1:= SIN (20);
SQL
SELECT SIN (90) “Sine of 90degrees” FROM DUAL;
Sine of 90 degrees
------------------
.89399666
SINH
参见:
COS, COSH, SIN,
TAN, TANH
语法:
SINH (n)
变量:
n:数字型变量。
例子:
PL/SQL
Var1:= SINH (20);
SQL
SELECT SINH (20)“Hyperbolic Sine of 20 deg” FROM DUAL;
Hyperbolic Sine of 20 deg
-------------------------
242582598
SOUNDEX
语法:
SOUNDEX (‘x’)
变量:
x:应该在单引号中指定字符或Varchar2型变量。
例子:
SQL
SELECT DPL_NAME FROMDENIED_PARTIES_LIST WHERE
SOUNDEX(DPL_NAME) =SOUNDEX(‘Saddam Hussain’) ;
DPL_NAME
----------------------------------------------
Al Husseni
Sadda Al Sada.
SPOOL
EDIT, SAVE
语法:
SPO[OL] [filename[.ext] |OFF | OUT]
变量:
filename:你想输出(spool)的文件名。
ext:文件的后缀。缺省的后缀是LST(或LIS)。
例子:
SQL
SPOOL REPORT
SPOOL REPORT OUT
SPOOL REPORT OFF
SQLERRM
参见:
DECLARE EXCEPTION,
EXCEPTION, EXCEPTION
_INIT PRAGMA
语法:
SQLERRM [n]
变量:
n:表示 SQLCODE 取整数值。这是可选项。.
例子:
PL/SQL
error_message := SQLERRM(1401);
SQLPLUS
语法:
SQLPLUS [[-S[ILENT]][logon] [start]]|
变量:
logon:用户标识,口令和数据库信息的格式如下:
username[/password][@database_specification]|/|/NOLOG
start:在 SQL*Plus 中执行的命令文件。
例子:
SQL
SQLPLUS john/cage
SQLPLUS -S john/cage
SQLPLUS -S john/cage@LOAN
SQLPLUS john/cage @LOAN_RPT
SQRT
参见:
EXP, POWER
语法:
SQRT (n)
变量:
n:正的数字变量。
例子:
PL/SQL
Var1:= SQRT (ABS(Var2));
SQL
SELECT SQRT (4) “SquareRoot of 4” FROM DUAL;
Square Root of 4
----------------
2
START
参见:
@, DEFINE
语法:
STA[RT] filename[.ext][arg_1,...,arg_N]
变量:
filename:在 SQL*Plus 中执行的命令文件。
Ext:命令文件的后缀,缺省的后缀是 SQL。
arg_1,...,arg_N:如果参数可用,则为命令文件参数。
例子:
SQL
START CALCSALARY 1
START LOAN_RPT
START ACCT.QRY
STDDEV
参见:
SUM, VARIANCE
语法:
STDDEV ( [DISTINCT | ALL]expn)
变量:
DISTINCT:该选项使函数只考虑参数的不同值。是可选项。.
ALL:这个选项使函数考虑参数中包括所有重复值。该项是可选项。.
Expn:可以是SQL查询的一列或是几列的数学表达式。
例子:
PL/SQL
Var1:= STDDEV (1, 2, 3);
SQL
SELECT STDDEV (DAILY_SALES)“Standard Deviation” FROM SALES;
Standard Deviation
------------------
3416.842
STORAGE
参见:
CREATE CLUSTER, CREATE
INDEX, CREATE ROLLBACK
SEGMENT, CREATE SNAPSHOT,
CREATE SNAPSHOT LOG,
CREATE TABLE,
CREATE TABLESPACE
语法:
STORAGE ( [INITIAL n [K |M] )
[NEXT n [K | M] ]
[PCTINCREASE n]
[MINEXTENTS n]
[MAXEXTENTS n]
[OPTIMAL n [K | M] | NULL ]
[FREELISTS n ]
[FREELIST GROUP n ]
变量:
n:任何有效的正整数变量。
STORAGE_ERROR
参见:
PROGRAM_ERROR, NO_DATA_
FOUND, TOO_MANY_ROWS
语法:
EXCEPTION
WHEN STORAGE_ERROR THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n:一系列的语句。
例子:
PL/SQL
BEGIN
OPEN loan_cur;
LOOP
FETCH loan_cur INTOloan_rec;
EXIT WHENloan_cur%NOTFOUND;
END LOOP;
EXCEPTION
WHEN STORAGE_ERROR THEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR = ‘STORAGE ERROR’;
END;
STORE
参见:
SAVE, GET
语法:
STORE {SET} filename[.ext][CRE[ATE]|REP[LACE]| APP[END]]
变量:
filename:你要在其中存储SQL*Plus环境属性的文件名。
Ext:若后缀可用,则为文件后缀。
例子:
SQL
STORE SET MYENV
STORE SET MYENV.NEW APPEND
STORE SET MYENV CREATE
STORE SET MYENV.OLD REPLACE
SUBSTR
参见:
SUBSTRB
语法:
SUBSTR (‘x’ [,y, z])
变量:
x:在单引号中指定的字符或Varchar2型的变量。.
y:指出替换串起市始位置的数字型变量。是可选项。如果这个项指定为0,则作为1处理。
Z:指出需要被替换的字符总数的数字型变量。是可选项。
例子:
PL/SQL
Var1:= SUBSTR (‘Oracle’, 1,3);
SQL
SELECT SUBSTR (‘Oracle’, 1,3) “Substr” FROM DUAL;
Sub
---
Ora
SUBSTRB
参见:
SUBSTR
语法:
SUBSTRB (‘x’ [,y, z])
变量:
x:在单引号中指定的字符或Varchar2型的变量。.
y:指出替换串起市始位置的数字型变量。是可选项。如果这个项指定为0,则作为1处理。
Z:指出需要被替换的字符总数的数字型变量。是可选项。
例子:
PL/SQL
Var1:= SUBSTRB (‘Oracle’,1, 3);
SQL
SELECT SUBSTRB (‘Oracle’,1, 3) “Substr “ FROM DUAL;
Sub
---
Ora.
SUM
参见:
Group by AVG, MAX, MIN,
STDDEV, VARIANCE
语法:
SUM ( [DISTINCT | ALL]expn)
变量:
DISTINCT:该选项使函数只考虑参数的不同值。是可选项。.
ALL:这个选项使函数考虑重复值的所有参数值。该项是可选项。.
Expn:可以是SQL查询的一列或是几列的数学表达式。
例子:
PL/SQL
Var1:= SUM (1, 2, 3);
SQL
SELECT SUM (DAILY_SALES)“SUM” FROM SALES;
SUM
--------------
1212.1221
SYSDATE
参见:
PSEUDOCOLUMN
语法:
SYSDATE
例子:
SQL
SELECT SYSDATE “THE DATETODAY” FROM DUAL;
THE DATE
---------
09-NOV-97
TABLE
参见:
RECORD
语法:
TYPE type_name IS TABLE OF
{ column_type |variable%TYPE | table.column%TYPE } [NOT
NULL]
INDEX BY BINARY_INTEGER ;
变量:
type_name:在接下来的PL/SQL 表中声明的类型标识。
column_type:诸如CHAR,DATE,或 NUMBER的数据类型。
例子:
PL/SQL
DECLARE
TYPE CnameTabTyp IS TABLEOF CHAR(10)
INDEX BY BINARY_INTEGER;
Cname_tab CnameTabType;
BEGIN
Cname_tab (1) := ‘LARRY’;
END;
TAN
参见:
COS, COSH, SIN,
SINH, TANH
语法:
TAN (n)
变量:
n:数字型变量。
例子:
PL/SQL
Var1:= TAN (20);
SQL
SELECT TAN (20) “Tangent of20 degrees” FROM DUAL;
Tangent of 20 degrees
---------------------
2.2371609
TANH
参见:
COS, COSH, SIN,
SINH, TAN
语法:
TANH (n)
变量:
n:数字型变量。
例子:
PL/SQL
Var1:= TANH (20);
SQL
SELECT TANH (20)“Hyperbolic Tangent of 20 deg” FROM DUAL;
Hyperbolic Tangent of 20deg
----------------------------
1
TIMEOUT_ON_RESOURCE
参见:
NO_DATA_FOUND,
TOO_ MANY_ROWS
语法:
EXCEPTION
WHEN TIMEOUT_ON_RESOURCETHEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n:一系列语句。
例子:
PL/SQL
BEGIN
OPEN loan_cur;
LOOP
FETCH loan_cur INTOloan_rec;
EXIT WHENloan_cur%NOTFOUND;
END LOOP;
EXCEPTION
WHEN TIMEOUT_ON_RESOURCETHEN
UPDATE APPLICATION_ERROR_TABLE
SET ERROR = ‘ TIMEOUT ONRESOURCE ‘ ;
END;
TIMING
参见:
CLEAR, SET
语法:
TIMI[NG] [STARTtext|SHOW|STOP]
变量:
text: 计时器的名字。
例子:
SQL
TIMING START new_timer
TIMING SHOW
TIMING STOP
TO_CHAR (date)
参见:
TO_DATE, FORMAT —Date
语法:
TO_CHAR (d [, ‘x’,‘nls_parm’])
变量:
d:有效的日期变量。
X:在单引号中指定的字符型变量,它表示返回的日期类型。是可选项。
nls_parm:指定返回的年、月和日的语言。该参数是可选的。缺省的,它将采用缺省的日期语言。这个参数可以指定为:‘NLS_DATE_LANGUAGE = language’。
例子:
PL/SQL
FISCAL_YEAR:= TO_CHAR(SYSDATE,’YEAR’);
SQL
SELECT TO_CHAR(SYSDATE,’YEAR’) “CURRENT_FISCAL_YEAR” FROM
DUAL;
CURRENT_FISCAL_YEAR
-----------------------
NINETEEN NINETY-SEVEN.
TO_CHAR (label)
参见:
TO_LABEL
语法:
TO_CHAR (mls [, ‘x’])
变量:
mls:有效的MLSLABEL 变量。
X:在单引号中指定的字符变量,它表示返回的字符串的形式。该项是可选的。
TO_CHAR (number)
参见:
TO_NUMBER,
FORMAT —NUMBER
语法:
TO_CHAR (n [, ‘x’, ‘nls_parm’])
变量:
n:有效的数字变量。
X:在引号中指定的字符变量,它表示返回的数字形式。是可选项。
nls_parm:指定返回数字的语言。该项是可选项。这个参数可以如下指定 ‘NLS_NUMERIC_CHARACTERS = ‘’,.’’’
‘NLS_CURRENCY = ‘’$’’’
例子:
PL/SQL
FISCAL_YEAR:= TO_CHAR(786);
SQL
SELECTTO_CHAR(786,’L99G999D99MI’,’NLS_NUMERIC_CHARACTERS =
‘’,.’’ NLS_CURRENCY = ‘’$’’‘) “Net Payable” FROM DUAL;
Net Payable
--------------------
$786,00.
TO_DATE (char)
参见:
TO_CHAR, TO_NUMBER,
FORMAT —DATE
语法:
TO_DATE (‘s’ [, ‘x’,‘nls_parm’])
变量:
s:在引号中指定的CHAR 或 VARCHAR2型变量。
X:在引号中指定的字符变量,它表示返回的日期形式。该项是可选的。
Nls_parm:指定返回的年、月、日的语言。该参数是可选的。缺省的,它将采用缺省的日期语言。
这个参数可以指定为‘NLS_DATE_LANGUAGE = language’。
例子:
PL/SQL
FISCAL_YEAR:= TO_DATE(‘26-JAN-47’);
SQL
SELECT TO_DATE (‘01/26/47’,‘mm/dd/yy’) “REPUBLIC DAY” FROM
DUAL;
REPUBLIC
---------
26-JAN-47.
TO_LABEL (char)
参见:
TO_CHAR
语法:
TO_LABEL (‘s’ [, ‘x’])
变量:
s:在引号中指定的有效的CHAR 或 VARCHAR2型变量。
X:在引号中指定的字符型变量,它表示返回的标号方式。该项为可选项。.
TO_MULTI_BYTE (char)
参见:
TO_SINGLE_BYTE
语法:
TO_MULTI_BYTE (‘s’)
变量:
s:在引号中指定的有效的 CHAR或 VARCHAR2 型变量。
TO_NUMBER (char)
参见:
TO_CHAR, TO_DATE,
FORMAT —NUMBER
语法:
TO_NUMBER (‘s’ [, ‘x’,‘nls_parm’])
变量:
s:在引号中指定的 CHAR 或 VARCHAR2 型变量。
X:在引号中指定的字符变量,表示返回的数字的形式,该项是可选项。
Nls_parm:指定需要返回值的字符。该参数是可选的。缺省的,它将采用缺省的数字形式。
这个参数如下指定:
‘NLS_NUMERIC_CHARACTERS = ‘’,.’’’
‘NLS_CURRENCY = ‘’$’’’
例子:
PL/SQL
FISCAL_YEAR:= TO_NUMBER(‘1947’);
SQL
SELECT TO_NUMBER (‘1947’)“FISCAL_YEAR” FROM DUAL;
FISCAL_YEAR
-----------
1947.
TO_SINGLE_BYTE (char)
参见:
TO_MULTI_BYTE
语法:
TO_SINGLE_BYTE (‘s’)
变量:
s:在引号中指定有效的 CHAR或 VARCHAR2 型变量。
TOO_MANY_ROWS
参见:
NO_DATA_FOUND
语法:
EXCEPTION
WHEN TOO_MANY_ROWS THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n:一系列语句。
例子:
PL/SQL
DECLARE
total_salary NUMBER;
BEGIN
SELECT salary INTOtotal_salary FROM employee;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR =
‘ MORE THAN 1 ROW FOR THE
INTO VARIABLE’ ;
END;
TRANSLATE
参见:
REPLACE
语法:
TRANSLATE (‘x’ [,’y’,’z’])
变量:
x:在单引号中指定的字符或varchar2型变量,在该变量中替换字符。
Y:在单引号中指定的字符或varchar2型变量,它指向你想替换的字符。
Z:在单引号中指定的字符或varchar2型变量,它指向你想替换的字符。.
例子:
PL/SQL
Var1:= TRANSLATE (‘Oracle’,‘Or’, ‘Mir’,);
SQL
SELECT TRANSLATE (‘Oracle’,‘Or’, ‘Mir’,); ‘’Example of
translating strings’’ FROMDUAL;
Example of translatingstrings
----------------------------
Miracle.
TRUNC (date)
参见:
ROUND
TRUNC (d [, ‘x’])
变量:
d:有效的日期变量。
X:在引号中指定的字符变量,它表示返回的日期方式。
表A_24 TRUNC (date) 命令的方式
方式 结果
CC, SCC 世纪
SYYYY, YYYY, YEAR, SYEAR,YYY, YY, Y 年(对6月1日舍入)
IYYY, IY, IY, I ISO 年
Q 季节 (对季节的第二月份的16号舍入)
MONTH, MON, Month,MM,RM 月(对16号舍入)
WW 将某周的某一天作为某年的第一天
IW 将某周的某一天作为ISO年的第一天
W 将某周的某一天作为月份的第一天
DDD, DD, J 按日期取整
DAY, DY, D 按周的第一天取整
HH, HH12, HH24 按小时取整
MI 按分钟取整
例子:
PL/SQL
FISCAL_YEAR:= TRUNC(SYSDATE,’YEAR’);
SQL
SELECT TRUNC(SYSDATE,’YEAR’) “CURRENT_FISCAL_YEAR” FROM DUAL;
CURRENT_F
---------
01- JAN-97.
TRUNC (number)
参见:
ROUND
语法:
TRUNC (x [, y])
变量:
x: 数字型变量
y: 数字型变量(可选)
例子:
PL/SQL
Var1:= TRUNC (124.1666, 2);
SQL
SELECT TRUNC (124.16666,-2) “Rounded Value” FROM DUAL;
Rounded Value
-------------
100
SELECT TRUNC (124.16666, 2)“Rounded Value” FROM DUAL;
Rounded Value
-------------
124.16
TRUNCATERUNCATE
DELETE, DROP TABLE
语法:
TRUNCATE [TABLE | CLUSTER]
schema.[table][cluster][DROP | REUSE STORAGE]
变量:
table:希望删除行的表名
cluster:希望删除行的CLUSTER名
schema:表或 cluster的schema名
例子:
SQL
TRUNCATE TABLE inventory;
TRUNCATE CLUSTER marketingDROP STORAGE;
TRUNCATE TABLE loan REUSESTORAGE;
TTITLE
参见:
BTITLE,REPHEADER, REPFOOTER
语法:
TTI[TLE] [printspec[text|variable] ...]|[OFF|ON]
变量:
printspec:printspec 包含 SQL*Plus 将用来作为题目的如下语句:
F COL n
F S[KIP] [n]
F TAB n
F LE[FT]
F CE[NTER]
F R[IGHT]
F BOLD
F FORMAT text
text::题目的内容。
Variable:包含如下系统维护的变量:
F SQL.LNO (当前行数)
F SQL.PNO(当前页数)
F SQL.RELEASE (当前 Oracle 版本号)
F SQL.SQLCODE (当前错误代码)
F SQL.USER (当前用户名)
例子:
SQL
TTITLE LEFT ‘ADVENTUREREPORT’
TTITLE OFF
TTITLE ON.
UID
参见:
USER
语法:
UID
变量:
无
例子:
SQL
SELECT UID FROM DUAL;
UID
-----
33
UNDEFINE
参见:
DEFINE, START
语法:
UNDEF[INE] variable_1 ...variable_n
变量:
variable_1 ... variable_n:希望删除的变量
例子:
SQL
UNDEFINE myVar1
UNDEFINE loan_bal acct_bal
UNDEFINE myVar1 myVar2myVar3
UNION
参见:
UNION ALL, INTERSECT, MINUS
语法:
b1 UNION b2
变量:
b1 和 b2 是 SELECT语句
例子:
SQL
SELECT SALES_AMOUNT
FROM DAILY_SALES
WHERE SALES_DATE =‘09-FEB-96’
UNION
SELECT SALES_AMOUNT
FROM DAILY_SALES
WHERE SALES_DATE =‘09-FEB-97’ ;
SHIPPING_T
----------
699.95
122.95
UNION ALL
参见:
ALL UNION, INTERSECT,
MINUS, SELECT
语法:
b1 UNION ALLb2
变量:
b1 和 b2 是 SELECT 语句
例子:
SQL
SELECT SALES_AMOUNT
FROM DAILY_SALES
WHERE SALES_DATE =‘09-FEB-96’
UNION ALL
SELECT SALES_AMOUNT
FROM DAILY_SALES
WHERE SALES_DATE =‘09-FEB-97’ ;
SHIPPING_T
----------
699.12
122.12
111.12
112.33
UPDATE
参见:
INSERT, DELETE, SELECT,
SELECT INTO
语法:
UPDATE {table |(sub_query)} [alias]
SET { column_name ={sql_expression | (sub_query)}
| (column_name[,column_name]...) = (subquery)}
[, { column_name ={sql_expression | (subquery)}
| (column_name[,column_name]...) = (subquery)}]...
[WHERE {search_condition |CURRENT OF cursor_name}];
变量:
table:希望更新的表名
sub_query:一个 SELECT 子查询
alias:别名
column_1,...,column_n:列名
sql_expression:有效的 SQL表达式
WHERE search_condition:有效的 WHERE子句
例子:
SQL
UPDATE salary
SET employee_salary =Total_Salary
WHERE employee_number:=emp_id;
UPDATE employees
SET employee_active_flag =‘N’
WHERE employee_id in
(SELECT employee_id
FROM department_head
WHEREdepartment_active_flag = ‘N’);.
UPPER
参见:
INITCAP, LOWER
语法:
UPPER (‘x’)
变量:
x:单引号中指定的字符型或Varchar2型变量。
例子:
PL/SQL
Var1:= UPPER (‘oracle’);
SQL
SELECT UPPER (‘oracle is agood database’) “UpperCase Sentence”
FROM DUAL;
UpperCase Sentence
-------------------------
ORACLE IS A GOOD DATABASE
USER
参见:
UID, PSEUDOCOLUMN
语法:
USER
变量:
无
例子:
SQL
SELECT USER FROM DUAL;
USER
-----
FED_REG
USERENV
参见:
UID, USER
语法:
USERENV (‘type’)
变量:
type:可以采用如下的选项并且必须在引号中指定:
表A_25 USERENV 命令的选项设置
选项 描述
ENTRYID 返回当前用户会话输入的 ID
SESSIONID 返回当前用户会话ID
TERMINAL 返回当前系统会话的操作系统标识
OSDBA 若当前用户会话有OSDBA 角色则返回TRUE
LABEL 返回当前用户会话标号.
LANGUAGE 返回当前用户会话的语言和区域
CLIENT_INFO 为当前用户会话返回client_info域的值。这个值由
dbms_application_info.set_client_info过程设置
LANG 返回以ISO省略形式的3字符表示的当前用户会话使用的语言
例子:
SQL
SELECT USERENV(‘SESSIONID’)“SESSION ID” FROM DUAL;
SESSION ID
---------------
21323.
VALUE_ERROR
参见:
NO_DATA_FOUND,
TOO_MANY_ROWS
语法:
EXCEPTION
WHEN VALUE_ERROR THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n: 一序列语句
例子:
PL/SQL
DECLARE
name VARCHAR2(10);
total_salary NUMBER;
BEGIN
total_salary:= total_salary* name;
EXCEPTION
WHEN VALUE_ERROR THEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR = ‘VALUE_ERROR -
INVALID MULTIPLICATION’ ;
END;
VARIABLE
参见:
ACCEPT, DEFINE
语法:
VAR[IABLE]
[variable [NUMBER|CHAR|CHAR(n)|VARCHAR2 (n)| REFCURSOR]]
变量:
variable: 声明的二进制变量名
例子:
SQL
VARIABLE quota NUMBER
VARIABLE last_name CHAR(35)
VARIABLE r REFCURSOR
VARIABLE ASSIGNMENT
参见:
DECLARE, NULL
语法:
a:= 5
变量:
a: 用于赋值变量
例子:
PL/SQL
Total_Salary:= Base_Salary+ Commission + Bonus;
VARIANCE
参见:
SUM, STDDEV, VARIANCE
语法:
VARIANCE ( [DISTINCT | ALL]expn)
变量:
DISTINCT: 变量的目标值
ALL: 包括重复的在内的所有值
expn: 可以是查询的列名或表达式
例子:
PL/SQL
Var1:= VARIANCE (1, 2, 3);
SQL
SELECT VARIANCE(DAILY_SALES) “ VARIANCE “ FROM SALES;
VARIANCE
--------------
34.12
VSIZE
参见:
LENGTH
语法:
VSIZE (v)
变量:
v:任何正整数
例子:
SQL
SELECT VSIZE (‘ORACLE’)“VSIZE” FROM DUAL;
VSIZE
--------
6
SELECT VSIZE (21.543)“VSIZE” FROM DUAL;
VSIZE
--------
4
RESERVED WORDS
ACCESS ADD ALL ALTER
AND ANY AS ASC
AUDIT
BETWEEN BY
CHAR CHECK CLUSTER COLUMN
COMMENT COMPRESS CONNECTCREATE
CURRENT
DATE DECIMAL DEFAULT DELETE
DESC DISTINCT DROP
ELSE EXCLUSIVE EXISTS
FILE FLOAT FOR FROM
GRANT GROUP
HAVING
IDENTIFIED IMMEDIATE ININCREMENT
INDEX INITIAL INSERTINTEGER
INTERSECT INTO IS
LEVEL LIKE LOCK LONG
MAXEXTENTS MINUS MODEMODIFY
NOAUDIT NOCOMPRESS NOTNOWAIT
NULL NUMBER
OF OFFLINE ON ONLINE
OPTION OR ORDER
PCTFREE PRIOR PRIVILEGESPUBLIC
RAW RENAME RESOURCE REVOKE
ROW ROWID ROWLABEL ROWNUM
ROWS
SELECT SESSION SET SHARE
SIZE SMALLINT STARTSUCCESSFUL
SYNONYM SYSDATE
TABLE THEN TO TRIGGER
UID UNION UNIQUE UPDATE
USER
VALIDATE VALUES VARCHARVARCHAR2
VIEW
WHENEVER WHERE WITH.
WHENEVER OSERROR
参见:
WHENEVER SQLERROR
语法:
WHENEVER OSERROR {EXIT[SUCCESS|FAILURE|n|variable]
[COMMIT|ROLLBACK]|CONTINUE[COMMIT|ROLLBACK|NONE]}
变量:
n:SQL*Plus返回的成功或失败的代码
variable:SQL*Plus返回的成功或失败的变量
例子:
SQL
WHENEVER OSERROR EXITCOMMIT
WHENEVER OSERROR EXITretcode ROLLBACK.
WHENEVER SQLERROR
参见:
WHENEVER OSERROR
语法:
WHENEVER SQLERROR {EXIT[SUCCESS|FAILURE|n|variable]
[COMMIT|ROLLBACK]|CONTINUE[COMMIT|ROLLBACK|NONE]}
变量:
n:SQL*Plus返回的成功或失败的代码
variable:SQL*Plus返回的成功或失败的变量
例子:
SQL
WHENEVER SQLERROR EXITCOMMIT
WHENEVER SQLERROR EXITFAILURE retcode ROLLBACK.
WHILE-LOOP
参见:
LOOP, EXIT, FOR-LOOP,
EXIT-WHEN
语法:
WHILE condition1 is trueLOOP
statement_1,...,statement_n;
END LOOP
变量:
condition_1:PL/SQL 条件
statement_1,...,statement_n:一序列PL/SQL 执行语句
例子:
PL/SQL
WHILE Total_Salary > 0LOOP
Total_Salary:= Base_Salary+ Commission + Bonus;
UPDATE salary SETemployee_salary = Total_Salary WHERE
employee_number:= emp_id;
END LOOP;
ZERO_DIVIDE
参见:
INVALID_NUMBER
语法:
EXCEPTION
WHEN ZERO_DIVIDE THEN
statement_1,...,statement_n
变量:
statement_1,...,statement_n:a sequence of statements
说明:
如果用0 除, PL/SQL 将隐含地启动ZERO_DIVIDE 例外。 相关的错误代码是ORA-01476
例子:
PL/SQL
DECLARE
total_salary NUMBER;
BEGIN
total_salary:= total_salary/ 0;
EXCEPTION
WHEN ZERO_DIVIDE THEN
UPDATEAPPLICATION_ERROR_TABLE
SET ERROR = ‘DIVISION BYZERO
INVALID’ ;
END;