欢迎进入Oracle社区论坛,与200万技术人员互动交流 >>进入 存储过程是实际位于 Oracle 中的程序。大多数存储过程都是用 PL/SQL 编写的,在 Oracle 数据库 10g 第 2 版和更高版本中,您可以用 Java、.NET 或其他语言将它们编写为外部过程。 存储过程通常将一
欢迎进入Oracle社区论坛,与200万技术人员互动交流 >>进入
存储过程是实际位于 Oracle 中的程序。大多数存储过程都是用 PL/SQL 编写的,在 Oracle 数据库 10g 第 2 版和更高版本中,您可以用 Java、.NET 或其他语言将它们编写为外部过程。 存储过程通常将一系列相关操作组成一个 API。存储过程执行的操作包括由 SQL 语句以及 PL/SQL 语句执行的操作,SQL 语句用于获取和修改数据,PL/SQL 语句将对这些数据进行相应操作,如执行某些数学运算、对值进行详细验证值以及处理错误条件。它们降低了调用程序与数据库之间的“往返”次数并简化了客户端 中的数据管理逻辑,从而有利于提高性能。 如果考虑一下管理表之间的多对多关系通常需要的代码,则会发现对现有数据执行更新通常涉及三个不同的查询。通过将该进程封装在单个存储过程中,将减少客户端与数据库之间的通信量,而通常需要在客户端代码分多个步骤执行的操作将减化为一个数据库调用。 PHP OCI8 扩展支持对存储过程的调用,您可以将参数绑定到过程语句(与将参数绑定到普通的 SQL 语句方法相同),并可以访问结果游标和 Oracle 集合。本方法文档中提供了存储过程的常见操作示例。 存储过程输入和输出 调用 Oracle 存储过程时,所有输入和输出数据均以参数形式传递给过程。如果您习惯于使用某些参数调用 PHP 函数并让它返回一个值的过程,那么起初您可能对此感到有些迷惑不解,但通过示例却可以一目了然。假设有以下存储过程签名:
sayHello (name IN VARCHAR2, greeting OUT VARCHAR2)
调用此过程时,第一个参数名将包含一个在调用时提供的输入值,而 greeting 将由该过程填充,作为一个“返回”值,在该过程完成后使用。 阅读规范 PL/SQL 编程不是本方法文档的范畴,但您需要对存储过程有一个大致的了解并能够阅读接口规范,但不必深究。 对于存储过程的源代码,开始都需要先定义接受的参数,例如:
PROCEDURE edit_entry(
status_out OUT NUMBER,
status_msg_out OUT VARCHAR2,
id_inout IN OUT INTEGER,
title_in IN VARCHAR2,
text_out OUT CLOB,
categories_in IN list_of_numbers
);
该过程名为 edit_entry。圆括号中定义了可以传递给该过程的各参数(由逗号分隔)。每个参数中,你会看到用于在该过程内部引用其值的名称(不需要在 PHP 脚本中使用同一名称)、参数的模式(如下所示)以及该参数的类型。 对于该示例中的第一个参数:
status_out OUT NUMBER,
内部名称为 status_out,模式为 OUT,类型为 NUMBER(它是一个原生的 Oracle 数据类型)。 后面有一个 id_inout 参数。
id_inout IN OUT INTEGER,
它的模式为 IN OUT,类型为 INTEGER。 最后是 categories_in 参数:
categories_in IN list_of_numbers
此处的类型是由用户定义的(稍后将对该类型进行详细介绍)。 参数模式 参数模式描述了数据从调用方到过程的“流”向:
IN ? 该模式的参数由调用方提供。
OUT ? 参数可以由过程分配值并返回至调用方。
IN OUT ? 参数可以在两个“方向”使用;即,调用方可以为该参数提供值,而过程也可以修改参数值。
参数项是必选项。从 PHP 调用过程时,必须将 PHP 变量绑定到它定义的所有参数。您不必向 PHP 变量分配值,即使它们是输入参数 ? 如果未向标量类型分配值,Oracle 将把它视为 NULL 值。 值得注意的是,存储过程可以在 Oracle 中“重载”。换言之,可以有两个名称相同但参数签名不同的过程。将依据 PHP 变量绑定到的参数的数目和类型来决定要调用哪个过程。 复杂类型 存储过程使用的参数并不只局限于 VARCHAR2 和 INTEGER 等标量类型。也可以传递并接收复杂的数据类型,如值列表或与从表中选择的行集相对应的结果游标。 一般说来,如果存在要迭代的数据行,则您将通常会收到从存储过程返回的游标,而如果您需要传入值列表,则通常将使用集合。以下示例通过 PHP 演示了这些复杂类型。 调用方与定义方权限。Oracle 对“调用方”(执行存储过程的用户)和定义方(以其身份执行 CREATE PROCEDURE 语句的用户)进行了区分。 默认情况下,存储过程是以定义方的权限执行的,即使调用方是不同的用户。这意味着表的所有访问权限(例如,在过程中的访问权限)将由定义方的权限控制,因此调用方只需要执行过程的权限而非它使用的表的权限。 可以在过程定义中用关键字 AUTHID CURRENT_USER 更改此模型。设置该指令后,执行存储过程时所需的权限将在运行时依据执行该过程的当前用户来决定。 该方法的一个用途是测试一个修改表数据但实际上不修改实时数据的过程。这种情况下,调用方在他们自己的模式中定义一个表(该表与从他们需要执行的过程中访问的表同名),而过程依据本地表而非提供给定义方的表执行。 从 PHP 中调用存储过程 对于要从 PHP 中执行以调用过程的 SQL 语句而言,您将通常在 Oracle BEGIN ...END; 块(称作匿名块)中嵌入调用。例如:
// etc.
$sql = 'BEGIN sayHello(:name, :message); END;';
然后,通过调用 oci_bind_by_name() 将参数绑定到 PHP 变量。 如果使用以下 DDL 语句定义了 sayHello
:
CREATE OR REPLACE PROCEDURE
sayHello (name IN VARCHAR2, greeting OUT VARCHAR2)
AS
BEGIN
greeting := 'Hello ' || name;
END;
/
注意,您可以使用 SQL*Plus 命令行运行上面的语句。将该语句保存到文件 (SAYHELLO.SQL)。接下来,使用 SQL*Plus 登录:
$ sqlplus username@SID
然后,使用 START 命令创建该过程:
SQL> START /home/username/SAYHELLO.SQL
以下 PHP 脚本调用该过程:
$conn = oci_connect('SCOTT','TIGER') or die;
$sql = 'BEGIN sayHello(:name, :message); END;';
$stmt = oci_parse($conn,$sql);
// Bind the input parameter
oci_bind_by_name($stmt,':name',$name,32);
// Bind the output parameter
oci_bind_by_name($stmt,':message',$message,32);
// Assign a value to the input
$name = 'Harry';
oci_execute($stmt);
// $message is now populated with the output value
print "$message\n";
?>
Blog 示例程序包。为演示调用存储过程方面的某些技巧,您将在此处使用以下名为 blog 的程序包,该程序包提供了一个 API,用于获取和修改假设的网志应用程序中的条目。程序包用于通过其自身的作用域将过程、函数和数据封装在其自身的命名空间内部,并使它们独立于全局数 据库命名空间中的其他过程。调用程序包中的过程时,将使用句号来分隔程序包名称与过程名称。 可以使用以下语句指定 blog 程序包:
CREATE OR REPLACE PACKAGE blog AS
TYPE cursorType IS REF CURSOR RETURN blogs%ROWTYPE;
/*
Fetch the latest num_entries_in from the blogs table, populating
entries_cursor_out with the result
*/
PROCEDURE latest(
num_entries_in IN NUMBER,
entries_cursor_out OUT cursorType
);
/*
Edit a blog entry.If id_inout is NULL, results in an INSERT, otherwise
attempts to UPDATE the existing blog entry. status_out will have the value
1 on success, otherwise a negative number on failure with status_msg_out
containing a description
categories_in is a collection where list_of_numbers is described by
TYPE list_of_numbers AS VARRAY(50) OF NUMBER;
*/
PROCEDURE edit_entry(
status_out OUT NUMBER,
status_msg_out OUT VARCHAR2,
id_inout IN OUT INTEGER,
title_in IN VARCHAR2,
text_out OUT CLOB,
categories_in IN list_of_numbers
);
END blog;
/
[1] [2] [3]