作为 Oracle 所特有的一种语言,PL/SQL 是对结构化查询语言(SQL)的一种扩展与补 充。它结合了数据库语言与程式化编程语言,并基于一个基本单位,称为块(block)。 通过编译与存储可执行的块,Oracle 可以轻松快捷地处理 PL/SQL。在本次的技术手册中, 我们为您提供了 PL/SQL 的基础知识以及专家指导,包括了 PL/SQL 中的数据类型简介、 PL/SQL 函数与触发器以及 PL/SQL 中的存储过程等,相信您无论是高手还是菜鸟都可以获 得有帮助的信息。
|
扩展语言是一个特征集,它以一定方式强化现有的语言。有人说 PL/SQL 是 SQL 的一 个特殊版本,但是情况并非如此。PL/SQL 自身是一种编程语言,它有自己的语法、规则和 编译程序。在编写 PL/SQL 程序时,使用或不使用 SQL 语句都可以。
|
v PL/SQL 定义与基础(下) v Oracle 中的 PL/SQL 数据类型介绍
|
函数是 PL/SQL 存储程序单元,它为程序单元内部隐藏执行数据提供了一个方法。在 PL/SQL 中,函数可以当做右值并可以从 SQL 语句中调用,而过程则不可以。
|
v 如何在 PL/SQL 过程中打开 ref cursor v 导入/导出预包装的 PL/SQL 过程
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
v 如何显示 PL/SQL 过程的输出语句 v 返回多行时出现 ORA-01422 错误的解决方法 v 如何在 Oracle 中重建触发器
|
一个存储在数据库中以编译形式存在的 PL/SQL 程序可以通过存储过程来被调用。而 存储过程可以被许多程序共享。存储过程有利于控制对数据的访问、保存数据的完整性并 提高生产率。在本部分中,我们将介绍 PL/SQL 中的存储过程,包括了存储过程错误及示 例等。
|
v 使用 Oracle 存储过程返回多行 v 如何找到正在运行的存储过程 v 如何在 Oracle 服务器之间自动传输数据 v 如何在 SELECT 语句中调用一个存储过程 v 在 Oracle 中调用同一存储过程
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
什么是 PL/SQL?PL/SQL 是一个程序语言,它是结构化查询语言(SQL)的延伸。 PL/SQL 的作用是将数据库语言和程式化编程语言整合到一起。PL/SQL 允许你将 SQL 语句 融合到程序结构中,因此可以使用 PL/SQL 命令块和子程序将 SQL 语句组织起来,然后递 交给 Oracle 执行。没有 PL/SQL 的话,Oracle 必须一次一个地处理 SQL 语句,在一个网络 环境中,这将造成流量阻塞而延缓反应时间。
|
在 Bill Pribyl 和 Steven Feuerstein 两位专家所写的一本名叫《Learning Oracle PL/SQL》的书中,他们是这样定义 PL/SQL 的。“结构化查询语言是一种基于集合论的语 言,因此它的功能是操作数据集。SQL 主要由 SELECT, INSERT, CREATE 和 GRANT 等少量 的主要命令组成;事实上,这些命令所达成的功能,可能需要上百行程序代码来实现。这 也是基于 SQL 语言的数据库如此流行的原因之一。而“SQL”这个名称也对我们开了一个 巨大的玩笑,它其实并不是结构化的,也并不是只用于查询,甚至并不是一种真正意义上 的语言。尽管如此,它仍然是关系型数据库(Oracle,DB2,SQL Server)中最通用的语 言。”
|
“扩展语言是一个特征集,它以一定方式强化现有的语言。有人说 PL/SQL 是 SQL 的 一个特殊版本,但是情况并非如此。PL/SQL 自身是一种编程语言,它有自己的语法、规则 和编译程序。在编写 PL/SQL 程序时,使用或不使用 SQL 语句都可以。有些人说 PL/SQL 是 SQL 的扩展集,这并不正确,因为在 PL/SQL 程序中只能使用最普通的 SQL 语句。”
|
“PL/SQL 是一种和 SQL 关系密切的语言,它允许你编写语句规则整齐的程序。比较适 合程序员的 PL/SQL 定义是:PL/SQL 是支持特定程序单元和程序包的过程语言,它的大部 分语法是源自 Ada,并从 Oracle SQL 中获得数据类型空间和许多内部函数。”
|
(作者:SearchOracle.com 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
过程性编程语言: 允许程序员定义一系列步骤来生成一个结果。 匿名块程序:一个支持批处理脚本的匿名块 PL/SQL 程序只需要执行部分。 命名块程序:一个命名块程序就是一个 PL/SQL 程序,它交付可存储程序单元。 执行部分:始于 BEGIN 语句,终于程序的 END 语句或 EXCEPTION 块。
|
存储过程:一个 SQL 语句的存储过程集以编译的形式存放在数据库中,它可以被许多 程序分享。
|
触发器:PL/SQL 触发器是一个 PL/SQL 存储过程,当执行 INSERT, UPDATE 或 DELETE 命令时,它就启动。
|
控制语句:PL/SQL 控制语句不仅检查逻辑条件和分支程序执行,还要反复声明一个条 件直到收到退出指令。
|
条件语句:PL/SQL 条件语句负责检查一个值在执行之前是否满足条件。在 PL/SQL 中 有两种条件语句,IF 语句和 CASE 语句。
|
(作者:SearchOracle.com 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
PL/SQL 的数据类型包括数字、字符、大对象、布尔类型、时间与时间间隔。每个常量、 变量和参数都会有一个数据类型,它们会指定存储格式、限制和有效的值域。 PL/SQL 提 供了很多预置的数据类型,以下这些是其中最常用的数据类型:
|
BLOB:BLOB 是 LOB(大对象)数据类型中的一种,它允许你存储最多 4GB 的非结构化 的数据块(比如文本,图片,视频片段和声音波形等)。
|
根据 Whatis.com 对 BLOB(二进制大对象)的定义来看,它的读音和 BLAHB 相近,有 时候也可用小写的 blob 来代替。它指的是那些大的文件,通常是一个图片或声音文件。 在进行上传下载或存储到数据库时,BLOB 数据类型文件通常需要使用特殊的方式进行操作。
|
CLOB:CLOB(字符大对象)同样是 LOB 数据类型的一种,它允许你存储最多 4GB 的非 结构化的数据块(比如文本,图片,视频片段和声音波形等)。
|
CHAR:CHAR 是一种字符类型,它允许你存储字母数字数据、表示单词和文本,并可以 操控字符串。一般地,CHAR 数据类型用来存储固定长度的字符数据。
|
DATE:DATE 数据类型存储固定长度的日期时间,它以秒为计数单位,包含从午夜 0 点 开始一天的时间。日期部分默认为当月的第一天;时间部分默认为午夜 0 点。用户可以使 用日期函数 SYSDATE 返回当前的日期和时间。
|
LONG 与 LONG ROW:LONG 数据类型一般用来存储可变长度的字符串,LONG 值的最大长 度为 32760 字节。LONG RAW 数据类型用来存储二进制数据和字节串。LONG RAW 数据和 LONG 数据差不多,只是 LONG RAW 数据不用 PL/SQL 来编译。
|
VARCHAR2:在 Oracle 中,我建议使用 VARCHAR2 数据类型而不建议使用 VARCHAR1 。 VARCHAR2 用来存储可变长度字符数据。VARCHAR2 需要一个参数来指明最大长度为 32767 字节。小 VARCHAR2 变量可优化性能,大 VARCHAR2 变量可优化内存利用率。
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
(作者:SearchOracle.com 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
问:在面对存储过程与函数的选择问题时,何时该选择存储过程,何时该选择函数, 是否有既定的规则可以遵循?比如返回游标时,该选择哪一种方式?为什么?
|
答:我使用函数来返回计算机值。我可以在 SELECT 列表中调用函数(例如 SELECT my_function() FROM dual;),同样地在 PL/SQL 中也可以。
|
我使用存储过程来返回游标,或者做任何过程操作时也会使用,因为我不必担心返回 值的问题,而只在乎步骤的发生与否。
|
(作者:Karen Morton 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
如何在 PL/SQL 过程中打开 ref cursor
|
问:在 PL/SQL 过程中,我使用 EXECUTE IMMEDIATE Drop 一个已有表然后再重建它。 之后我想声明一个 ref cursor 来处理表中的数据。问题是过程运行正确,但执行后过程 变成无效了。如何解决这一问题?
|
答:这么说吧,从应用中 Drop 再重建一个表是十分不妥的,我可以举出好多原因来 说明。其中最直接的原因就是它是一个相对“昂贵”的操作。如果这是数据集市和数据仓 库的部分负载,那可能还好一些。
|
如果表的结构没有改变,你可以考虑用 truncate 表来代替。如果有外部关键字指向 这个表,那么你就必须临时弃用外部关键字了。
|
如果你为查询而打开了一个 ref cursor,drop 表然后试图提取 ref cursor,但结果 并不能保证。你可能会遇到不一致提取或无效游标的错误。在数据量较小的情况下,错误 发生的概率不大,而大的结果集中发生概率较大。发生这一现象的原因的 Oracle 将数据 块标记为“dirty”了。这是不要 drop 并重建表的另一个原因。
|
在对基础对象做出更改后,Oracle 往往会将程序单元设为无效。当下次引用过程时, Oracle 将自动重新编译它。你注意一下 last_ddl,我打赌它一定在持续更改和重新编译。
|
唯一的解决方法是动态地打开一个 ref cursor,一个表示查询的字符串: v_stmt := 'select col1, col2, col3 from some_table where key = :k'; v_key := 3;
|
open v_ref for v_stmt using v_key;
|
(作者:Dan Clamage 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
问:我们现在要将 Oracle 8.0.5 数据库升级到 Oracle 9i。其中有一些预包装好的 PL/SQL 过程,在将其导出 Oracle 8 数据库时,dump 文件可以正常地导入 Oracle 9i 数据 库。但随后导出 Oracle 9i 时,重新导入就遇到了问题。再导入存储过程时,主机显示内 存资源不足。我的 DBA 告诉我是因为预包装的问题,请问问题到底出在什么地方?
|
答:如果我遇到这样的问题,我首先会去查看创建预包装的源代码。希望你们还保存 了那些文档,你可以先将它们 Drop 掉,然后重建,进行预包装。
|
还可以这样,使用 SHOW=Y, FULL=Y 和 LOG=filename 来运行导入操作。这可以将 DLL 命令 dump 到你的日志文件中。打开这个日志文件,你可以看到 DLL 命令并创建你的存储 过程。以脚本文件的形式存储 DLL,然后手动运行 SQL*Plus,看看到底发生了什么。
|
(作者:Brian Peasland 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
问:我按照 Oracle 参考指南上给出的几个例子自己试了一下。其它的都还好,只是 当使用 DBMS_LOB 或者 DBMS_output 包运行 PL/SQL 过程时,我没有得到任何输出语句。之 后退出提示说 PL/SQL 过程已成功完成。我是否需要再打开什么东西,来看一下输出呢?
|
答:DBMS_OUTPUT.PUT_LINE 是依靠 SQL*Plus 命令 SET SERVEROUTPUT ON 来显示输出 的。默认的 SERVEROUTPUT 已经关闭,这就是为什么你看不到输出而系统显示过程已成功 完成。你只需要把它打开就可以了。顺便说一句,在每次使用 SQL*Plus 时,你都需要将 SERVEROUTPUT 打开,因为系统设置总是还原为默认值的。
|
(作者:Karen Morton 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
返回多行时出现 ORA-01422 错误的解决方法
|
问:我使用以下 Oracle 过程,根据输入 SQL 语句的 PO ID 可以返回一行或者更多行。 当返回多行时,我收到了 ORA-01422 错误提示。请问我该如何重新编写过程代码,以便一 对多地返回数据行?我有一个硬编码的 PO ID('0700185')以便测试用。过程应该可以返回 所有单据,有可能是一个也有可能是五个。
|
请您帮助解释一下,因为我不是 DBA,对 Oracle 来说我只是新手,但我有意愿学习。 谢谢!
|
CREATE OR REPLACE PROCEDURE TESTPNET(PO OUT VARCHAR, BATCH OUT VARCHAR2, VOU CHER OUT VARCHAR2, INVOICE OUT VARCHAR2) AS BEGIN SELECT PO_ID,GRP_AP_ID, VOUC HER_ID, INVOICE_ID INTO PO, BATCH, VOUCHER, INVOICE FROM PS_VOUCHER WHERE PO_I D = '0700185'; DBMS_OUTPUT.PUT_LINE(PO); DBMS_OUTPUT.PUT_LINE(BATCH); DBMS_OU TPUT.PUT_LINE(VOUCHER); DBMS_OUTPUT.PUT_LINE(INVOICE); END TESTPNET;
|
答:你所创建的过程使用了 SQL,而这只应该在希望返回一行时使用。如果你希望返 回多行数据,你应该使用游标。我添加了一个游标 C1(参见第二个过程)。第二个过程中使 用了循环来遍历选定的记录。
|
CREATE OR REPLACE PROCEDURE testpnet ( po OUT VARCHAR,
|
batch OUT VARCHAR2, voucher OUT VARCHAR2, invoice OUT VARCHAR2 )
|
SELECT po_id, grp_ap_id, voucher_id, invoice_id INTO po, batch, voucher, invoice
|
WHERE po_id = '0700185'; close;
|
DBMS_OUTPUT.put_line (po); DBMS_OUTPUT.put_line (batch); DBMS_OUTPUT.put_line (voucher); DBMS_OUTPUT.put_line (invoice); END testpnet;
|
CREATE OR REPLACE PROCEDURE testpnet (
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
po OUT VARCHAR, batch OUT VARCHAR2, voucher OUT VARCHAR2, invoice OUT VARCHAR2 )
|
select po_id, grp_ap_id, voucher_id, invoice_id from ps_voucher
|
where po_id = '0700185'; BEGIN
|
fetch c1 into po, batch, voucher, invoice; exit when c1%NOTFOUND;
|
DBMS_OUTPUT.put_line (po); DBMS_OUTPUT.put_line (batch); DBMS_OUTPUT.put_line (voucher); DBMS_OUTPUT.put_line (invoice); END testpnet;
|
(作者:Greg Williams 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
问:我公司使用的是 Oracle 数据库,现在有一些触发器从 schema 中删除了,请问我 可不可以从其它的 schema 中导入一个触发器呢?
|
答:如果你使用的是 Oracle 9i 或者 10g,那么你可以去源 schema 下反向构建一个 DLL 来重建触发器。只需要在 SQL*Plus 中使用以下的查询语句:
|
SELECT DBMS_METADATA.GET_DDL('TRIGGER','TRIG_NAME','OWNER') FROM dual;
|
你需要添加触发器的名称和所有者。这一命令的输出可以存到文本文件中,用它来重 建触发器。
|
(作者:Brian Peasland 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
问:请问能否在数据库内部的触发器中编写一个过程或函数,反过来是否也一样?
|
答:是的,你可以从数据库的触发器中的调用一个过程或函数,但是反向触发一个触 发器是不可以的。触发器的类型有三种:
|
1、DML——按照表中的 DML 语句触发; 2、Instead-of——按照视图而不是 DML 语句触发; 3、System——当系统事件发生时触发,比如关闭或启动系统。
|
(作者:Greg Williams 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
问:1、我想要一个可以返回多行的 Oracle 存储过程,代码该如何编写? 2、我还想通过 VB.NET 调用这个存储过程,并在平台上显示这条记录。 3、在 DBMS_SQL 包中,NATIVE 指的是什么?
|
答:1、有许多可以使用的存储过程,下面是一个 REF CURSOR 的例子:
|
create or replace procedure employee_sel(
|
cv_results in out sys_refcursor)
|
select first_name, last_name from employee;
|
where sys_refcursor is a type of REF CURSOR type
|
你可以在 VB.NET 中使用 SELECT 语句对存储过程进行调用。由于 VB.NET 并不是我的 特长,因此我只能这样告诉你。
|
Dbms_sql.native 是 DBMS_SQL.PARSE 的一个强制参数。Oracle 8i 中引入了本地动态 SQL,这是 DBMS_SQL 的替代品。使用本地动态 SQL 时,你可以直接将动态 SQL 语句添加到 PL/SQL 块中。大多数情况下,本地动态 SQL 可以代替 DBMS_SQL,而且更加好用。
|
(作者:Greg Williams 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
答:答案就是你需要找到会话中正在运行的 SQL 语句。存储过程就是以 SQL 语句的形 式出现的,想要完成这项工作,只需运行下面的查询:
|
FROM v$session s, v$sqlarea a WHERE s.sql_address = a.addr AND s.sql_hash_value = a.hash_value AND s.username='MYUSER';
|
在这个例子中,我使用 MYUSER 将它仅局限为一个查询。但是你也可以在 V$SESSION 视图中做其它的局限。
|
(作者:Brian Peasland 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
问:我想请问一下,如何让一个存储过程将更新的数据从远程服务器自动传输到中央 服务器中?
|
答:基本上,你需要建立一个数据库连接然后通过它让存储过程进行插入数据的操作。 《Oracle 10g 管理员指南》中的第 29 章讲到了这一内容。
|
在“架构对象名称解析”这一标题下,你可以看到类似的示例,它在 db link 中使用 了 INSERT 语句,而你的存储过程中也可以用相同的 INSERT 语句。
|
(作者:Brian Peasland 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
问:请问如何在 SELECT 语句中调用一个存储过程?
|
答:你不可以在 SELECT 语句中调用一个存储过程。但是可以再 SELECT 语句中调用函 数,而这个函数可以是一个打包好的存储过程。比如,假设我有一个 F00 存储过程,我们 可以将其打包成函数 BAR:
|
CREATE FUNCTION bar () RETURN number
|
现在我可以使用下面的函数赖在 SELECT 语句中执行存储过程了: SELECT bar() FROM dual;
|
(作者:Brian Peasland 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
问:我现在想在一个存储过程中调用相同的存储过程。比如:我现在有一个存储过程, 叫做 ConverSubPlans,里面有一些参数。我想在过程内部做一些验证,所以需要在 ConverSubPlans 中调用 ConverSubPlans,这样做可以吗?
|
答:你当然可以在存储过程内部再调用相同的存储过程。这样的操作通常称为“递 归”。一个过程以递归的方式不断地自我调用。在进行这样的操作时,你需要设置一个结 束点,否则自我调用的过程将会持续不止。一般的递归过程或函数是用来计算阶乘的,其 中 N(N!)的定义是 N*(N-1)*(N-2)*...*2*1,N! = N*(N-1)!。这样一个递归函数就生成了, 具体可以参考下面的代码:
|
CREATE FUNCTION fact (n NUMBER) RETURN NUMBER IS
|
IF (n = 1) THEN returnVal := 1; ELSE
|
returnVal := n*fact(n-1); END IF;
|
(作者:Brian Peasland 译者:孙瑞 来源:TT 中国)
|
TT 数据库技术专题之“Oracle PL/SQL 应用指南”
|
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理