作者:Harry Fuecks
原文鏈接:http://www.oracle.com/technetwork/cn/articles/fuecks-sps-082839-zhs.html
存儲(chǔ)過(guò)程是實(shí)際位于 Oracle 中的程序趁蕊。大多數(shù)存儲(chǔ)過(guò)程都是用 PL/SQL 編寫(xiě)的熟嫩,在 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版和更高版本中素挽,您可以用 Java枪眉、.NET 或其他語(yǔ)言將它們編寫(xiě)為外部過(guò)程蹬叭。
存儲(chǔ)過(guò)程通常將一系列相關(guān)操作組成一個(gè) API藕咏。存儲(chǔ)過(guò)程執(zhí)行的操作包括由 SQL 語(yǔ)句以及 PL/SQL 語(yǔ)句執(zhí)行的操作,SQL 語(yǔ)句用于獲取和修改數(shù)據(jù)秽五,PL/SQL 語(yǔ)句將對(duì)這些數(shù)據(jù)進(jìn)行相應(yīng)操作孽查,如執(zhí)行某些數(shù)學(xué)運(yùn)算、對(duì)值進(jìn)行詳細(xì)驗(yàn)證值以及處理錯(cuò)誤條件坦喘。它們降低了調(diào)用程序與數(shù)據(jù)庫(kù)之間的“往返”次數(shù)并簡(jiǎn)化了客戶端中的數(shù)據(jù)管理邏輯盲再,從而有利于提高性能。
如果考慮一下管理表之間的多對(duì)多關(guān)系通常需要的代碼瓣铣,則會(huì)發(fā)現(xiàn)對(duì)現(xiàn)有數(shù)據(jù)執(zhí)行更新通常涉及三個(gè)不同的查詢答朋。通過(guò)將該進(jìn)程封裝在單個(gè)存儲(chǔ)過(guò)程中,將減少客戶端與數(shù)據(jù)庫(kù)之間的通信量棠笑,而通常需要在客戶端代碼分多個(gè)步驟執(zhí)行的操作將減化為一個(gè)數(shù)據(jù)庫(kù)調(diào)用梦碗。
PHP OCI8 擴(kuò)展支持對(duì)存儲(chǔ)過(guò)程的調(diào)用,您可以將參數(shù)綁定到過(guò)程語(yǔ)句(與將參數(shù)綁定到普通的 SQL 語(yǔ)句方法相同),并可以訪問(wèn)結(jié)果游標(biāo)和 Oracle 集合洪规。本方法文檔中提供了存儲(chǔ)過(guò)程的常見(jiàn)操作示例印屁。
存儲(chǔ)過(guò)程輸入和輸出
調(diào)用 Oracle 存儲(chǔ)過(guò)程時(shí),所有輸入和輸出數(shù)據(jù)均以參數(shù)形式傳遞給過(guò)程斩例。如果您習(xí)慣于使用某些參數(shù)調(diào)用 PHP 函數(shù)并讓它返回一個(gè)值的過(guò)程雄人,那么起初您可能對(duì)此感到有些迷惑不解,但通過(guò)示例卻可以一目了然念赶。假設(shè)有以下存儲(chǔ)過(guò)程簽名:
sayHello (name IN VARCHAR2, greeting OUT VARCHAR2)
調(diào)用此過(guò)程時(shí)础钠,第一個(gè)參數(shù)名將包含一個(gè)在調(diào)用時(shí)提供的輸入值,而 greeting 將由該過(guò)程填充叉谜,作為一個(gè)“返回”值珍坊,在該過(guò)程完成后使用。
閱讀規(guī)范
PL/SQL 編程不是本方法文檔的范疇正罢,但您需要對(duì)存儲(chǔ)過(guò)程有一個(gè)大致的了解并能夠閱讀接口規(guī)范阵漏,但不必深究。
對(duì)于存儲(chǔ)過(guò)程的源代碼翻具,開(kāi)始都需要先定義接受的參數(shù)履怯,例如:
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
);
該過(guò)程名為 edit_entry。圓括號(hào)中定義了可以傳遞給該過(guò)程的各參數(shù)(由逗號(hào)分隔)裆泳。每個(gè)參數(shù)中叹洲,你會(huì)看到用于在該過(guò)程內(nèi)部引用其值的名稱(不需要在 PHP 腳本中使用同一名稱)、參數(shù)的模式(如下所示)以及該參數(shù)的類型工禾。
對(duì)于該示例中的第一個(gè)參數(shù):
status_out OUT NUMBER,
內(nèi)部名稱為 status_out运提,模式為 OUT,類型為 NUMBER(它是一個(gè)原生的 Oracle 數(shù)據(jù)類型)闻葵。
后面有一個(gè) id_inout 參數(shù)民泵。
id_inout IN OUT INTEGER,
它的模式為 IN OUT,類型為 INTEGER槽畔。
最后是 categories_in 參數(shù):
categories_in IN list_of_numbers
此處的類型是由用戶定義的(稍后將對(duì)該類型進(jìn)行詳細(xì)介紹)栈妆。
參數(shù)模式
參數(shù)模式描述了數(shù)據(jù)從調(diào)用方到過(guò)程的“流”向:
IN - 該模式的參數(shù)由調(diào)用方提供。
OUT - 參數(shù)可以由過(guò)程分配值并返回至調(diào)用方厢钧。
IN OUT - 參數(shù)可以在兩個(gè)“方向”使用鳞尔;即,調(diào)用方可以為該參數(shù)提供值早直,而過(guò)程也可以修改參數(shù)值寥假。
參數(shù)項(xiàng)是必選項(xiàng)。從 PHP 調(diào)用過(guò)程時(shí)霞扬,必須將 PHP 變量綁定到它定義的所有參數(shù)糕韧。您不必向 PHP 變量分配值拾给,即使它們是輸入?yún)?shù) - 如果未向標(biāo)量類型分配值,Oracle 將把它視為 NULL 值兔沃。
值得注意的是蒋得,存儲(chǔ)過(guò)程可以在 Oracle 中“重載”。換言之乒疏,可以有兩個(gè)名稱相同但參數(shù)簽名不同的過(guò)程额衙。將依據(jù) PHP 變量綁定到的參數(shù)的數(shù)目和類型來(lái)決定要調(diào)用哪個(gè)過(guò)程。
復(fù)雜類型
存儲(chǔ)過(guò)程使用的參數(shù)并不只局限于 VARCHAR2 和 INTEGER 等標(biāo)量類型怕吴。也可以傳遞并接收復(fù)雜的數(shù)據(jù)類型窍侧,如值列表或與從表中選擇的行集相對(duì)應(yīng)的結(jié)果游標(biāo)。
一般說(shuō)來(lái)转绷,如果存在要迭代的數(shù)據(jù)行伟件,則您將通常會(huì)收到從存儲(chǔ)過(guò)程返回的游標(biāo),而如果您需要傳入值列表议经,則通常將使用集合斧账。以下示例通過(guò) PHP 演示了這些復(fù)雜類型。
調(diào)用方與定義方權(quán)限煞肾。Oracle 對(duì)“調(diào)用方”(執(zhí)行存儲(chǔ)過(guò)程的用戶)和定義方(以其身份執(zhí)行 CREATE PROCEDURE 語(yǔ)句的用戶)進(jìn)行了區(qū)分咧织。
默認(rèn)情況下,存儲(chǔ)過(guò)程是以定義方的權(quán)限執(zhí)行的籍救,即使調(diào)用方是不同的用戶习绢。這意味著表的所有訪問(wèn)權(quán)限(例如,在過(guò)程中的訪問(wèn)權(quán)限)將由定義方的權(quán)限控制蝙昙,因此調(diào)用方只需要執(zhí)行過(guò)程的權(quán)限而非它使用的表的權(quán)限闪萄。
可以在過(guò)程定義中用關(guān)鍵字 AUTHID CURRENT_USER 更改此模型。設(shè)置該指令后奇颠,執(zhí)行存儲(chǔ)過(guò)程時(shí)所需的權(quán)限將在運(yùn)行時(shí)依據(jù)執(zhí)行該過(guò)程的當(dāng)前用戶來(lái)決定败去。
該方法的一個(gè)用途是測(cè)試一個(gè)修改表數(shù)據(jù)但實(shí)際上不修改實(shí)時(shí)數(shù)據(jù)的過(guò)程。這種情況下大刊,調(diào)用方在他們自己的模式中定義一個(gè)表(該表與從他們需要執(zhí)行的過(guò)程中訪問(wèn)的表同名)为迈,而過(guò)程依據(jù)本地表而非提供給定義方的表執(zhí)行。
從 PHP 中調(diào)用存儲(chǔ)過(guò)程
對(duì)于要從 PHP 中執(zhí)行以調(diào)用過(guò)程的 SQL 語(yǔ)句而言缺菌,您將通常在 Oracle BEGIN ...END; 塊(稱作 匿名塊)中嵌入調(diào)用。例如:
<?php
// etc.
$sql = 'BEGIN sayHello(:name, :message); END;';
然后搜锰,通過(guò)調(diào)用 oci_bind_by_name() 將參數(shù)綁定到 PHP 變量伴郁。
如果使用以下 DDL 語(yǔ)句定義了 sayHello:
CREATE OR REPLACE PROCEDURE
sayHello (name IN VARCHAR2, greeting OUT VARCHAR2)
AS
BEGIN
greeting := 'Hello ' || name;
END;
/
注意,您可以使用 SQLPlus 命令行運(yùn)行上面的語(yǔ)句蛋叼。將該語(yǔ)句保存到文件 (SAYHELLO.SQL)焊傅。接下來(lái)剂陡,使用 SQLPlus 登錄:
$ sqlplus username@SID
然后,使用 START 命令創(chuàng)建該過(guò)程:
SQL> START /home/username/SAYHELLO.SQL
以下 PHP 腳本調(diào)用該過(guò)程:
<?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 示例程序包狐胎。為演示調(diào)用存儲(chǔ)過(guò)程方面的某些技巧鸭栖,您將在此處使用以下名為 blog 的程序包,該程序包提供了一個(gè) API握巢,用于獲取和修改假設(shè)的網(wǎng)志應(yīng)用程序中的條目晕鹊。程序包用于通過(guò)其自身的作用域?qū)⑦^(guò)程、函數(shù)和數(shù)據(jù)封裝在其自身的命名空間內(nèi)部暴浦,并使它們獨(dú)立于全局?jǐn)?shù)據(jù)庫(kù)命名空間中的其他過(guò)程溅话。調(diào)用程序包中的過(guò)程時(shí),將使用句號(hào)來(lái)分隔程序包名稱與過(guò)程名稱歌焦。
可以使用以下語(yǔ)句指定 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;
/
該程序包提供了兩個(gè)過(guò)程: blog.latest(返回包含最新 num_entries 網(wǎng)志條目的結(jié)果游標(biāo))和 blog.edit_entry(允許插入新的網(wǎng)志條目以及修改現(xiàn)有的網(wǎng)志條目)飞几。如果為 id_inout 參數(shù)提供值,則該過(guò)程將嘗試更新具有該 id 的相應(yīng)網(wǎng)志條目独撇。否則屑墨,它將插入一個(gè)新的網(wǎng)志條目并使用新行的主鍵填充 id_inout。該過(guò)程還接受與網(wǎng)志條目的主體相對(duì)應(yīng)的 CLOB 對(duì)象以及與該條目歸檔到的類別列表相對(duì)應(yīng)的集合對(duì)象纷铣。此處引用的集合類型 list_of_numbers 由以下語(yǔ)句定義:
CREATE OR REPLACE TYPE list_of_numbers AS VARRAY(50) OF NUMBER;
下面顯示了該程序包的主體绪钥。您可以通過(guò)其中的注釋了解它的功能而不必深入了解 PL/SQL:
CREATE OR REPLACE PACKAGE BODY blog AS
/*------------------------------------------------*/
PROCEDURE latest(
num_entries_in IN NUMBER,
entries_cursor_out OUT cursorType
) AS
BEGIN
OPEN entries_cursor_out FOR
SELECT * FROM blogs WHERE rownum < num_entries_in
ORDER BY date_published DESC;
END latest;
/*------------------------------------------------*/
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
AS
ENTRY_NOT_FOUND EXCEPTION;
entry_found INTEGER := 0;
BEGIN
/* Default status to success */
status_out := 1;
/* If id_inout has a value then attempt to UPDATE */
IF id_inout IS NOT NULL THEN
/* Check the id exists - raise ENTRY_NOT_FOUND if not */
SELECT COUNT(*) INTO entry_found
FROM blogs b WHERE b.id = id_inout;
IF entry_found != 1 THEN RAISE ENTRY_NOT_FOUND; END IF;
/* Update the blogs table returning the CLOB field */
UPDATE blogs b SET b.title = title_in, b.text = EMPTY_CLOB()
WHERE b.id = id_inout RETURNING b.text INTO text_out;
/* Remove any existing relationships to categories
- new categories inserted below */
DELETE FROM blogs_to_categories WHERE blog_id = id_inout;
status_msg_out := 'Blog entry ' || id_inout || ' updated';
/* id_inout was null so INSERT new record */
ELSE
INSERT INTO blogs b ( b.id, b.title, b.date_published, b.text )
VALUES ( blog_id_seq.nextval, title_in, SYSDATE, EMPTY_CLOB() )
RETURNING b.id, b.text INTO id_inout, text_out;
status_msg_out := 'Blog entry ' || id_inout || ' inserted';
END IF;
/* Now handle assignment to categories.
Loop over the categories_in collection,
inserting the new category assignments */
FOR i IN 1 .. categories_in.count
LOOP
INSERT INTO blogs_to_categories (blog_id,category_id)
VALUES (id_inout,categories_in(i));
END LOOP;
status_msg_out := status_msg_out || ' - added to '
|| categories_in.count || ' categories';
EXCEPTION
/* Catch the exception when id_inout not found */
WHEN ENTRY_NOT_FOUND THEN
status_out := -1001;
status_msg_out := 'No entry found in table blogs with id = '
|| id_inout;
/* Catch any other exceptions raised by Oracle */
WHEN OTHERS THEN
status_out := -1;
status_msg_out := 'Error:' || TO_CHAR (SQLCODE) || SQLERRM;
END edit_entry;
END blog;
/
The underlying table structure the procedures are using is:
CREATE SEQUENCE blog_id_seq
INCREMENT BY 1;
/
CREATE TABLE blogs (
id NUMBER PRIMARY KEY,
title VARCHAR2(200),
date_published DATE,
text CLOB
);
/
CREATE SEQUENCE category_id_seq
INCREMENT BY 1;
CREATE TABLE categories (
id NUMBER PRIMARY KEY,
name VARCHAR2(30) UNIQUE
);
/
CREATE TABLE blogs_to_categories (
blog_id INTEGER NOT NULL
REFERENCES blogs(id),
category_id INTEGER NOT NULL
REFERENCES categories(id),
PRIMARY KEY (blog_id, category_id)
);
/
存儲(chǔ)過(guò)程和引用游標(biāo)
看一下 blog.latest 過(guò)程,您將看到它返回一個(gè)用于迭代 blogs 表行的引用游標(biāo)关炼。
與直接從 SELECT 語(yǔ)句中訪問(wèn)行相比程腹,在 PHP 中使用游標(biāo)需要兩個(gè)額外的步驟。第一步是使用 oci_new_cursor() 函數(shù)(該函數(shù)隨后用于綁定到相應(yīng)的參數(shù))在 PHP 中準(zhǔn)備一個(gè)游標(biāo)資源儒拂。執(zhí)行 SQL 語(yǔ)句后寸潦,第二步是對(duì)游標(biāo)資源調(diào)用 oci_execute() 。
以下 PHP 腳本演示了該過(guò)程:
<?php
$conn = oci_connect('SCOTT','TIGER') or die;
$sql = 'BEGIN blog.latest(:num_entries, :blog_entries); END;';
$stmt = oci_parse($conn, $sql);
// Bind the input num_entries argument to the $max_entries PHP variable
$max_entries = 5;
oci_bind_by_name($stmt,":num_entries",$max_entries,32);
// Create a new cursor resource
$blog_entries = oci_new_cursor($conn);
// Bind the cursor resource to the Oracle argument
oci_bind_by_name($stmt,":blog_entries",$blog_entries,-1,OCI_B_CURSOR);
// Execute the statement
oci_execute($stmt);
// Execute the cursor
oci_execute($blog_entries);
print "The $max_entries most recent blog entries\n";
// Use OCIFetchinto in the same way as you would with SELECT
while ($entry = oci_fetch_assoc($blog_entries, OCI_RETURN_LOBS )) {
print_r($entry);
}
?>
存儲(chǔ)過(guò)程和 LOB
Oracle Long 對(duì)象與存儲(chǔ)過(guò)程之間可以進(jìn)行相互傳遞社痛,方法與內(nèi)部的 SQL 之間進(jìn)行的相互傳遞幾乎相同见转。
以下示例演示了如何使用 CLOB 調(diào)用 blog.edit_entry 過(guò)程。該示例未向 id 參數(shù)分配值蒜哀,因此它相當(dāng)于插入一個(gè)新的網(wǎng)志條目:
<?php
$conn = oci_connect('SCOTT','TIGER') or die;
$sql = 'BEGIN blog.edit_entry(:status, :status_msg, :id, :title, :text, :categories); END;';
$stmt = oci_parse($conn,$sql);
$title = 'This is a test entry';
oci_bind_by_name($stmt,":status",$status,32);
oci_bind_by_name($stmt,":status_msg",$status_msg,500);
oci_bind_by_name($stmt,":id",$id,32);
oci_bind_by_name($stmt,":title",$title,200);
// Explained in the next example...(use an empty value for now)
$Categories = oci_new_collection($conn,'LIST_OF_NUMBERS');
oci_bind_by_name($stmt,':categories',$Categories,32,OCI_B_SQLT_NTY);
// Create a new lob descriptor object
$textLob = oci_new_descriptor($conn, OCI_D_LOB);
// Bind it to the parameter
oci_bind_by_name($stmt, ":text", $textLob, -1, OCI_B_CLOB);
// Execute the statement but do not commit
oci_execute($stmt, OCI_DEFAULT);
// The status parameter will be negative if the procedure encountered a problem
if ( !$status ) {
// Rollback the procedure
oci_rollback($conn);
die ("$status_msg\n");
}
// Save the body of the blog entry to the CLOB
if ( !$textLob->save('This is the body of the test entry') ) {
// Rollback the procedure
oci_rollback($conn);
die ("Error saving lob\n");
}
// Everything OK so commit
oci_commit($conn);
print $status_msg."\n";
?>
正如該腳本所演示的斩箫,關(guān)鍵問(wèn)題是如何在使用 LOB 時(shí)處理事務(wù)。由于更新 LOB 是一個(gè)分為兩階段的過(guò)程撵儿,因此您在此處選擇將所有事務(wù)處理委托給 PHP 腳本乘客。
注意,默認(rèn)情況下淀歇,Oracle 只允許在任何給定的會(huì)話中一次運(yùn)行一個(gè)事務(wù)易核。這意味著從 PHP 調(diào)用的過(guò)程中發(fā)出的 commit 或 rollback 語(yǔ)句將覆蓋對(duì) oci_commit() 或 oci_rollback() 的調(diào)用±四可以使用匿名事務(wù)(使用位于過(guò)程定義內(nèi)部的 pragma PRAGMA AUTONOMOUS_TRANSACTION 啟用)更改此行為牡直。例如缀匕,您可以在從其他過(guò)程中調(diào)用的日志記錄程序包中使用匿名事務(wù);使用這一方法您可以記錄有關(guān)存儲(chǔ)過(guò)程調(diào)用的信息碰逸,而不會(huì)干擾正在會(huì)話中運(yùn)行的事務(wù)乡小。
存儲(chǔ)過(guò)程和集合
集合是一種用于將復(fù)雜數(shù)據(jù)類型傳遞到存儲(chǔ)過(guò)程中的機(jī)制。在網(wǎng)志應(yīng)用程序中饵史,可以將網(wǎng)志條目歸檔到多個(gè)分類中(與“blogs”表和“categories”表之間的多對(duì)多關(guān)系相對(duì)應(yīng))满钟。
必須在數(shù)據(jù)庫(kù)中全局定義 Oracle 中的集合類型,在本示例中约急,您將使用以下定義:
CREATE OR REPLACE TYPE list_of_numbers AS VARRAY(50) OF NUMBER;
該定義允許您一次最多向 50 個(gè)類別分配一個(gè)網(wǎng)志條目零远,方法是將該類型的實(shí)例傳遞給 blog.edit_entry 過(guò)程。
在 PHP 中厌蔽,集合由預(yù)定義的 PHP 類 OCI-Collection 表示牵辣。可以通過(guò)調(diào)用 oci_new_collection() 函數(shù)創(chuàng)建此類實(shí)例奴饮。OCI-Collection 對(duì)象提供了以下方法:
append :將元素添加到集合末尾
assign :從現(xiàn)有集合中將元素添加到某個(gè)集合
assignElem :將值分配給集合纬向,并標(biāo)識(shí)應(yīng)將該元素置于的集合中的索引位置
free :釋放與集合句柄關(guān)聯(lián)的資源
getElem :從集合中的特殊索引位置檢索元素
max :返回集合中的最大元素?cái)?shù)
size :返回集合的當(dāng)前大小
trim :從集合末尾刪除一些元素
此處,您只希望使用 append 方法戴卜,因此可以將類別 ID 列表附加到過(guò)程調(diào)用逾条。在以下示例中,您將更新在前一個(gè)示例中創(chuàng)建的現(xiàn)有網(wǎng)志條目投剥,方法是將它的 ID 傳遞給 blog.edit_entry 過(guò)程以及類別 id 列表:
<?php
$conn = oci_connect('SCOTT','TIGER') or die;
$sql = 'BEGIN blog.edit_entry(:status, :status_msg, :id, :title, :text, :categories); END;';
$stmt = oci_parse($conn, $sql);
$id = 1; // ID of the new entry
$title = 'This is a test entry (v2)';
oci_bind_by_name($stmt,":status",$status,32);
oci_bind_by_name($stmt,":status_msg",$status_msg,500);
oci_bind_by_name($stmt,":id",$id,32);
oci_bind_by_name($stmt,":title",$title,200);
$textLob = oci_new_descriptor($conn, OCI_D_LOB);
oci_bind_by_name($stmt, ":text", $textLob, -1, OCI_B_CLOB);
// Create an OCI-Collection object
$Categories = oci_new_collection($conn,'LIST_OF_NUMBERS');
// Append some category IDs to the collection;
$Categories->append(2);
$Categories->append(4);
$Categories->append(5);
// Bind the collection to the parameter
oci_bind_by_name($stmt,':categories',$Categories,-1,OCI_B_SQLT_NTY);
oci_execute($stmt, OCI_DEFAULT);
if ( !$status ) {
oci_rollback($conn);
die ("$status_msg\n");
}
if ( !$textLob->save('This is the body of the test entry [v2]') ) {
oci_rollback($conn);
die ("Error saving lob\n");
}
oci_commit($conn);
print $status_msg."\n";
?>
結(jié)論
您現(xiàn)在已經(jīng)了解了有關(guān)如何從 PHP 中調(diào)用存儲(chǔ)過(guò)程(既包括只涉及標(biāo)量數(shù)據(jù)類型的簡(jiǎn)單過(guò)程师脂,也包含更復(fù)雜的使用 LOB、游標(biāo)和集合的過(guò)程)的示例江锨。還對(duì)存儲(chǔ)過(guò)程的定義進(jìn)行了足夠的了解吃警,能讀懂它們的 PL/SQL 規(guī)范,這樣您就可以從 PHP 中正確地調(diào)用它們并綁定相應(yīng)的類型啄育。