PHP Conference Japan 2024

oci_bind_by_name

(PHP 5, PHP 7, PHP 8, PECL OCI8 >= 1.1.0)

oci_bind_by_name將 PHP 變數繫結至 Oracle 預留位置

描述

oci_bind_by_name(
    資源 $statement,
    字串 $param,
    混合 &$var,
    整數 $max_length = -1,
    整數 $type = 0
): 布林值

將 PHP 變數 var 繫結至 Oracle 繫結變數預留位置 param。繫結對於 Oracle 資料庫效能很重要,也是避免 SQL Injection 安全性問題的方法。

繫結允許資料庫重複使用之前執行語句的語句上下文和快取,即使另一個使用者或程序最初執行了它。繫結減少了對 SQL Injection 的擔憂,因為與繫結變數關聯的資料永遠不會被視為 SQL 語句的一部分。它不需要引號或跳脫。

已繫結的 PHP 變數可以變更,並且可以重新執行語句,而無需重新解析語句或重新繫結。

在 Oracle 中,繫結變數通常分為傳遞到資料庫的值的 IN 繫結,以及返回到 PHP 的值的 OUT 繫結。繫結變數可以是 INOUT 兩者。繫結變數是否用於輸入或輸出是在執行時期決定的。

使用 OUT 繫結時,您必須指定 max_length,以便 PHP 分配足夠的記憶體來保留傳回的值。

對於 IN 繫結,如果語句使用 PHP 變數的不同值多次重新執行,建議設定 max_length 長度。否則,Oracle 可能會將資料截斷為初始 PHP 變數值的長度。如果您不知道最大長度是多少,則在每次呼叫 oci_execute() 之前,使用目前的資料大小重新呼叫 oci_bind_by_name()。繫結不必要的長度會影響資料庫中的程序記憶體。

繫結呼叫會告訴 Oracle 從哪個記憶體位址讀取資料。對於 IN 繫結,當呼叫 oci_execute() 時,該位址需要包含有效資料。這表示繫結的變數必須在執行之前保持在作用域中。如果沒有,可能會發生意外結果或錯誤,例如「ORA-01460:要求未實作或不合理的轉換」。對於 OUT 繫結,其中一個徵兆是沒有在 PHP 變數中設定值。

對於重複執行的語句,繫結從未變更的值可能會降低 Oracle 優化器選擇最佳語句執行計畫的能力。很少重新執行的長時間執行語句可能無法從繫結中獲益。但是,在這兩種情況下,繫結可能比將字串加入 SQL 語句更安全,因為如果串連未經過濾的使用者文字,這可能會帶來安全風險。

參數

statement

有效的 OCI8 語句識別碼。

param

語句中使用的以冒號為前綴的繫結變數預留位置。冒號在 param 中是可選的。Oracle 不使用問號作為預留位置。

var

要與 param 關聯的 PHP 變數

max_length

設定資料的最大長度。如果您將其設定為 -1,則此函式將使用 var 的目前長度來設定最大長度。在這種情況下,當呼叫 oci_bind_by_name() 時,var 必須存在並包含資料。

type

Oracle 將資料視為的資料類型。使用的預設 typeSQLT_CHR。在可能的情況下,Oracle 將在此類型與資料庫欄(或 PL/SQL 變數類型)之間轉換資料。

如果您需要繫結抽象資料類型(LOB/ROWID/BFILE),您需要先使用 oci_new_descriptor() 函式配置它。抽象資料類型不使用 length,應設定為 -1。

type 的可能值為

傳回值

成功時傳回 true,失敗時傳回 false

範例

範例 1 使用 oci_bind_by_name() 插入資料

<?php

// 使用以下指令建立資料表:
// CREATE TABLE mytab (id NUMBER, text VARCHAR2(40));

$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}

$stid = oci_parse($conn,"INSERT INTO mytab (id, text) VALUES(:id_bv, :text_bv)");

$id = 1;
$text = "要插入的資料 ";
oci_bind_by_name($stid, ":id_bv", $id);
oci_bind_by_name($stid, ":text_bv", $text);
oci_execute($stid);

// 資料表現在包含:1, '要插入的資料 '

?>

範例 #2 為多次執行綁定一次

<?php

// 使用以下指令建立資料表:
// CREATE TABLE mytab (id NUMBER);

$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}

$a = array(1,3,5,7,11); // 要插入的資料

$stid = oci_parse($conn, 'INSERT INTO mytab (id) VALUES (:bv)');
oci_bind_by_name($stid, ':bv', $v, 20);
foreach (
$a as $v) {
$r = oci_execute($stid, OCI_DEFAULT); // 不要自動提交
}
oci_commit($conn); // 一次提交所有內容

// 資料表包含五個資料列:1, 3, 5, 7, 11

oci_free_statement($stid);
oci_close($conn);

?>

範例 #3 使用 foreach 迴圈綁定

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}

$sql = 'SELECT * FROM departments WHERE department_name = :dname AND location_id = :loc';
$stid = oci_parse($conn, $sql);

$ba = array(':dname' => 'IT Support', ':loc' => 1700);

foreach (
$ba as $key => $val) {

// oci_bind_by_name($stid, $key, $val) 無效
// 因為它會將每個預留位置綁定到相同的位置:$val
// 請改用資料的實際位置:$ba[$key]
oci_bind_by_name($stid, $key, $ba[$key]);
}

oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS);
foreach (
$row as $item) {
print
$item."<br>\n";
}

oci_free_statement($stid);
oci_close($conn);

?>

範例 #4 在 WHERE 子句中綁定

<?php

$conn
= oci_connect("hr", "hrpwd", "localhost/XE");
if (!
$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}

$sql = 'SELECT last_name FROM employees WHERE department_id = :didbv ORDER BY last_name';
$stid = oci_parse($conn, $sql);
$didbv = 60;
oci_bind_by_name($stid, ':didbv', $didbv);
oci_execute($stid);
while ((
$row = oci_fetch_array($stid, OCI_ASSOC)) != false) {
echo
$row['LAST_NAME'] ."<br>\n";
}

// 輸出結果為:
// Austin
// Ernst
// Hunold
// Lorentz
// Pataballa

oci_free_statement($stid);
oci_close($conn);

?>

範例 #5 使用 LIKE 子句綁定

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}

// 找出所有以 'South' 開頭的城市
$stid = oci_parse($conn, "SELECT city FROM locations WHERE city LIKE :bv");
$city = 'South%'; // '%' 在 SQL 中是萬用字元
oci_bind_by_name($stid, ":bv", $city);
oci_execute($stid);
oci_fetch_all($stid, $res);

foreach (
$res['CITY'] as $c) {
print
$c . "<br>\n";
}
// 輸出結果為
// South Brunswick
// South San Francisco
// Southlake

oci_free_statement($stid);
oci_close($conn);

?>

範例 #6 使用 REGEXP_LIKE 綁定

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}

// 找出所有包含 'ing' 的城市
$stid = oci_parse($conn, "SELECT city FROM locations WHERE REGEXP_LIKE(city, :bv)");
$city = '.*ing.*';
oci_bind_by_name($stid, ":bv", $city);
oci_execute($stid);
oci_fetch_all($stid, $res);

foreach (
$res['CITY'] as $c) {
print
$c . "<br>\n";
}
// 輸出結果為
// Beijing
// Singapore

oci_free_statement($stid);
oci_close($conn);

?>

對於少量、固定數量的 IN 子句條件,請使用個別的綁定變數。執行時期未知的數值可以設定為 NULL。這樣允許所有應用程式使用者使用單一語句,最大化 Oracle 資料庫快取的效率。

範例 #7 在 IN 子句中綁定多個數值

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}

$sql = 'SELECT last_name FROM employees WHERE employee_id in (:e1, :e2, :e3)';
$stid = oci_parse($conn, $sql);
$mye1 = 103;
$mye2 = 104;
$mye3 = NULL; // 假設我們沒有取得這個值
oci_bind_by_name($stid, ':e1', $mye1);
oci_bind_by_name($stid, ':e2', $mye2);
oci_bind_by_name($stid, ':e3', $mye3);
oci_execute($stid);
oci_fetch_all($stid, $res);
foreach (
$res['LAST_NAME'] as $name) {
print
$name ."<br>\n";
}

// 輸出結果為
// Ernst
// Hunold

oci_free_statement($stid);
oci_close($conn);

?>

範例 #8 綁定查詢所返回的 ROWID

<?php

// 使用以下指令建立表格:
// CREATE TABLE mytab (id NUMBER, salary NUMBER, name VARCHAR2(40));
// INSERT INTO mytab (id, salary, name) VALUES (1, 100, 'Chris');
// COMMIT;

$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}

$stid = oci_parse($conn, 'SELECT ROWID, name FROM mytab WHERE id = :id_bv FOR UPDATE');
$id = 1;
oci_bind_by_name($stid, ':id_bv', $id);
oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS);
$rid = $row['ROWID'];
$name = $row['NAME'];

// 將名稱變為大寫並儲存變更
$name = strtoupper($name);
$stid = oci_parse($conn, 'UPDATE mytab SET name = :n_bv WHERE ROWID = :r_bv');
oci_bind_by_name($stid, ':n_bv', $name);
oci_bind_by_name($stid, ':r_bv', $rid, -1, OCI_B_ROWID);
oci_execute($stid);

// 此時表格內容會變成 1, 100, CHRIS

oci_free_statement($stid);
oci_close($conn);

?>

範例 #9 在 INSERT 時綁定 ROWID

<?php

// 這個範例會插入 id 和名稱,然後更新薪水
// 使用以下指令建立表格:
// CREATE TABLE mytab (id NUMBER, salary NUMBER, name VARCHAR2(40));
//
// 基於 thies at thieso dot net (980221) 的原始 ROWID 範例

$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}

$sql = "INSERT INTO mytab (id, name) VALUES(:id_bv, :name_bv)
RETURNING ROWID INTO :rid"
;

$ins_stid = oci_parse($conn, $sql);

$rowid = oci_new_descriptor($conn, OCI_D_ROWID);
oci_bind_by_name($ins_stid, ":id_bv", $id, 10);
oci_bind_by_name($ins_stid, ":name_bv", $name, 32);
oci_bind_by_name($ins_stid, ":rid", $rowid, -1, OCI_B_ROWID);

$sql = "UPDATE mytab SET salary = :salary WHERE ROWID = :rid";
$upd_stid = oci_parse($conn, $sql);
oci_bind_by_name($upd_stid, ":rid", $rowid, -1, OCI_B_ROWID);
oci_bind_by_name($upd_stid, ":salary", $salary, 32);

// 要插入的 id 和名稱
$data = array(1111 => "Larry",
2222 => "Bill",
3333 => "Jim");

// 每個人的薪水
$salary = 10000;

// 插入並立即更新每一列
foreach ($data as $id => $name) {
oci_execute($ins_stid);
oci_execute($upd_stid);
}

$rowid->free();
oci_free_statement($upd_stid);
oci_free_statement($ins_stid);

// 顯示新的資料列
$stid = oci_parse($conn, "SELECT * FROM mytab");
oci_execute($stid);
while (
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {
var_dump($row);
}

oci_free_statement($stid);
oci_close($conn);

?>

範例 #10 綁定 PL/SQL 儲存函數

<?php

// 在執行 PHP 程式之前,請在 SQL*Plus 或 SQL Developer 中建立預存函數:
//
// CREATE OR REPLACE FUNCTION myfunc(p IN NUMBER) RETURN NUMBER AS
// BEGIN
// RETURN p * 3;
// END;

$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message']), E_USER_ERROR);
}

$p = 8;

$stid = oci_parse($conn, 'begin :r := myfunc(:p); end;');
oci_bind_by_name($stid, ':p', $p);

// 傳回值是一個 OUT 綁定。預設類型會是字串類型,所以綁定長度 40 表示最多會傳回 40 個數字。
oci_bind_by_name($stid, ':r', $r, 40);

oci_execute($stid);

print
"$r\n"; // 輸出 24

oci_free_statement($stid);
oci_close($conn);

?>

範例 #11 為 PL/SQL 預存程序綁定參數

<?php

// 在執行 PHP 程式之前,請在 SQL*Plus 或 SQL Developer 中建立預存程序:
//
// CREATE OR REPLACE PROCEDURE myproc(p1 IN NUMBER, p2 OUT NUMBER) AS
// BEGIN
// p2 := p1 * 2;
// END;

$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message']), E_USER_ERROR);
}

$p1 = 8;

$stid = oci_parse($conn, 'begin myproc(:p1, :p2); end;');
oci_bind_by_name($stid, ':p1', $p1);

// 第二個程序參數是一個 OUT 綁定。預設類型會是字串類型,所以綁定長度 40 表示最多會傳回 40 個數字。
oci_bind_by_name($stid, ':p2', $p2, 40);

oci_execute($stid);

print
"$p2\n"; // 輸出 16

oci_free_statement($stid);
oci_close($conn);

?>

範例 #12 綁定 CLOB 資料行

<?php

// 在執行之前,請建立表格:
// CREATE TABLE mytab (mykey NUMBER, myclob CLOB);

$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message']), E_USER_ERROR);
}

$mykey = 12343; // 此範例的任意鍵值;

$sql = "INSERT INTO mytab (mykey, myclob)
VALUES (:mykey, EMPTY_CLOB())
RETURNING myclob INTO :myclob"
;

$stid = oci_parse($conn, $sql);
$clob = oci_new_descriptor($conn, OCI_D_LOB);
oci_bind_by_name($stid, ":mykey", $mykey, 5);
oci_bind_by_name($stid, ":myclob", $clob, -1, OCI_B_CLOB);
oci_execute($stid, OCI_DEFAULT);
$clob->save("A very long string");

oci_commit($conn);

// 擷取 CLOB 資料

$query = 'SELECT myclob FROM mytab WHERE mykey = :mykey';

$stid = oci_parse ($conn, $query);
oci_bind_by_name($stid, ":mykey", $mykey, 5);
oci_execute($stid);

print
'<table border="1">';
while (
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_LOBS)) {
print
'<tr><td>'.$row['MYCLOB'].'</td></tr>';
// 在迴圈中,於第二次擷取之前釋放大型變數可降低 PHP 的尖峰記憶體使用量
unset($row);
}
print
'</table>';

?>

範例 #13 綁定 PL/SQL 布林值

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message']), E_USER_ERROR);
}

$plsql =
"begin
:output1 := true;
:output2 := false;
end;"
;

$s = oci_parse($c, $plsql);
oci_bind_by_name($s, ':output1', $output1, -1, OCI_B_BOL);
oci_bind_by_name($s, ':output2', $output2, -1, OCI_B_BOL);
oci_execute($s);
var_dump($output1); // true
var_dump($output2); // false

?>

注意事項

警告

請勿同時使用 addslashes()oci_bind_by_name(),因為不需要加上引號。任何自動加入的引號都會被寫入您的資料庫,因為 oci_bind_by_name() 會逐字插入資料,不會移除引號或跳脫字元。

注意:

如果您將字串繫結到 WHERE 子句中的 CHAR 欄位,請記住 Oracle 對於 CHAR 欄位使用空格填補的比較語意。您的 PHP 變數應該使用空格填補到與欄位相同的寬度,這樣 WHERE 子句才能成功。

注意:

PHP 的 var 引數是一個參考。某些形式的迴圈無法如預期運作

<?php
foreach ($myarray as $key => $value) {
oci_bind_by_name($stid, $key, $value);
}
?>

這會將每個鍵繫結到 $value 的位置,因此所有繫結的變數最終都會指向最後一個迴圈迭代的值。請改用以下方式

<?php
foreach ($myarray as $key => $value) {
oci_bind_by_name($stid, $key, $myarray[$key]);
}
?>

另請參閱

新增註解

使用者貢獻的註解 18 則註解

abiyi2000 at yahoo dot com
13 年前
我不幸地花了一整天的時間試圖讓這個在使用 OCI bind_by_name 插入時可以運作

<?php
if(is_numeric($v2)){
oci_bind_by_name($stmth, $bvar, $v2, -1, OCI_B_INT);
}else{
$v2 = (string) $v2;
oci_bind_by_name($stmth, $bvar, $v2, -1, SQLT_CHR);
}
?>

字串欄位總是正確插入,沒有任何截斷。字串欄位是 varchar2(160) CHAR,但用來填入它的資料長度為 40 個字元。

數字部分在資料庫中是 Number 類型,用於儲存 Unix 時間(自 1970/01/01 以來的 10 位數秒數)。

問題是,插入會截斷為 9 位數,而且有些虛假的數值甚至與輸入無關,也就是說,這不只是捨棄最左邊或最右邊的數字的問題,它會直接插入一個 9 位數的虛假數字。

我能夠解決此數字欄位問題的唯一方法是將最大長度設定為 8(而不是輸入中的位數 10)。

<?php
if(is_numeric($v2)){
oci_bind_by_name($stmth, $bvar, $v2, 8, OCI_B_INT);
}else{
$v2 = (string) $v2;
oci_bind_by_name($stmth, $bvar, $v2, -1, SQLT_CHR);
}
?>

希望您能盡快看到這個,以免您花費大量時間重複我遇到的相同問題。
martin dot abbrent at ufz dot de
8 年前
範例 #7 只顯示在 IN 子句中繫結少量固定數值。還有一種方法可以使用可變數量的數值繫結多個條件。

<?php
$ids
= array(
103,
104
);

$conn = oci_pconnect($user, $pass, $tns);
// 使用 ORACLE table() 函數從子查詢中取得 ids
$sql = 'SELECT * FROM employees WHERE employee_id IN (SELECT column_value FROM table(:ids))';
$stmt = oci_parse($conn, $sql);
// 建立數字集合。字串的內建類型是 ODCIVARCHAR2LIST,但您也可以建立自己的類型。
$idCollection = oci_new_collection($conn, 'ODCINUMBERLIST', 'SYS');

// ODCINUMBERLIST 類型的集合最大長度為 32767,您可能應該檢查一下!
foreach ($ids as $id) {
$idCollection->append($id);
}

oci_bind_by_name($stmt, ':ids', $idCollection, -1, SQLT_NTY);
oci_execute($stmt, OCI_DEFAULT);
oci_fetch_all($stmt, $return);
oci_free_statement($stmt);

oci_close($conn);

?>
avenger at php dot net
15 年前
別忘了第五個參數:$type。有時這會使您的程式碼變慢。例如

<?php
$sql
= "select * from (select * from b xxx) where rownum < :rnum";
$stmt = OCIParse($conn,$sql);
OCIBindByName($stmt, ":rnum", $NUM, -1);
OCIExecute($stmt);
?>

下面的程式碼比不使用繫結值的速度慢 5~6 倍。將第 3 行變更為

<?php
OCIBindByName
($stmt, ":rnum", $NUM, -1, SQLT_INT);
?>

將解決這個問題。

這個問題也出現在 ADODB DB 類別 (adodb.sf.net) 中,您在使用 SelectLimit 方法時要小心。
splintyg at gmail dot com
7 年前
各位,我一直在尋找如何將 clob 傳遞給程序以及如何從程序取得 clob 的方法
CREATE OR REPLACE PROCEDURE myproc(p1 IN clob, p2 OUT clob);

這是一個解答

<?php
$conn
= oci_connect("TEST", "html", "//hostname", "UTF8");

$filename = "./clob.txt";
$handle = fopen($filename, "r");
$f = fread($handle, filesize($filename));
fclose($handle);

$stid = oci_parse($conn, "begin myproc(:p1, :p2); end;");
$p1 = oci_new_descriptor($conn, OCI_D_LOB);
$p2 = oci_new_descriptor($conn, OCI_D_LOB);

oci_bind_by_name($stid, ":p1", $p1, -1, OCI_B_CLOB);
oci_bind_by_name($stid, ":p2", $p2, -1, OCI_B_CLOB);
$p1->writeTemporary($f, OCI_TEMP_BLOB);
oci_execute($stid); -- 找出 OCI_NO_AUTO_COMMIT
oci_commit
($conn);
echo
$p2->load();

$p1 ->close();
$p2 ->close();
oci_free_statement($stid);
oci_close($conn);
?>

一本關於「PHP 和 Oracle」的完美書籍
http://www.oracle.com/technetwork/topics/php/underground-php-oracle-manual-098250.html
dub357 at gmail dot com
9 個月前
這裡關於 PHP 變數引數為參考,以及某些迴圈無法運作的注意事項非常重要。但是,如果建立一個臨時變數,在綁定中使用該變數,然後取消設定它,就可以使 foreach 迴圈運作。例如

<?php
foreach ($myarray as $key => $val) {
$value = $val;
oci_bind_by_name($stid, $key, $value);
unset(
$value);
}
?>

這會將每個鍵綁定到 $value 的位置,但是當您在綁定後取消設定它時,可以再次設定和使用它。

https://php.dev.org.tw/manual/en/function.unset.php
charles dot fisher at arconic dot com
3 年前
我正在嘗試將 ADOdb 函式庫呼叫重構為 OCI,而我今天編寫的這個函式正能幫上忙。

function OraQry(&$Results, $Query, $Binds = false) {
global $xdb;

$Results = oci_parse($xdb, $Query);

if($Binds) foreach($Binds as $BindNm => $BindValJunk)
oci_bind_by_name($Results, $BindNm, $Binds[$BindNm], -1);

oci_execute($Results, OCI_NO_AUTO_COMMIT);

return null;
}

這也與 PDO 相似,可以傳遞綁定變數的陣列,而且如果它們以數值方式命名(從零開始),則可以省略呼叫 array() 函式

OraQry($rs,
'select status from all_tables where owner=:0 and table_name=:1',
[$owner, $table_name]);

while($arr = oci_fetch_assoc($rs)) echo $arr['STATUS'] . "\n";
asui dot dev dot null at gmail dot com
5 年前
如果您在將 FLOAT 值插入/更新到 NUMBER 資料行時收到「ORA-01722:無效數字錯誤」,請根據目前的地區設定檢查綁定值格式的正確性。

預設的「美國」地區設定假設傳送到 oracle 的值將是一個小數點分隔符號(就像 4127.5 一樣)。但是使用 setlocale('pl_PL.UTF-8'),您的浮點數將表示為 4127,5,而這種形式將在傳送資料至 oracle 時使用,導致問題...
這就是我的情況(8 小時的偵錯)。

您可以使用 setlocale(LC_ALL, 0) 檢查您目前的地區設定。

我可以推薦的解決方案
a) 不要設定地區設定,或在傳送資料時將其設定為 'C';
b) 將浮點數轉換為與目前 oracle 工作階段 NLS_NUMERIC_CHARACTERS 參數值相容的字串格式。
例如:當 NLS_NUMERIC_CHARACTERS = '.,' 時,浮點數值 4127.5 應轉換為 '4127.5'。這樣即使目前地區設定不同,oracle 也會正確擷取它。
splintyg at gmail dot com
7 年前
各位,我一直在尋找如何將 clob 傳遞給程序以及如何從程序取得 clob 的方法
CREATE OR REPLACE PROCEDURE myproc(p1 IN clob, p2 OUT clob);

這是一個解答

<?php
$conn
= oci_connect("TEST", "html", "//hostname", "UTF8");

$filename = "./clob.txt";
$handle = fopen($filename, "r");
$f = fread($handle, filesize($filename));
fclose($handle);

$stid = oci_parse($conn, "begin myproc(:p1, :p2); end;");
$p1 = oci_new_descriptor($conn, OCI_D_LOB);
$p2 = oci_new_descriptor($conn, OCI_D_LOB);

oci_bind_by_name($stid, ":p1", $p1, -1, OCI_B_CLOB);
oci_bind_by_name($stid, ":p2", $p2, -1, OCI_B_CLOB);
$p1->writeTemporary($f, OCI_TEMP_BLOB);
oci_execute($stid); -- 找出 OCI_NO_AUTO_COMMIT
oci_commit
($conn);
echo
$p2->load();

$p1 ->close();
$p2 ->close();
oci_free_statement($stid);
oci_close($conn);
?>

一本關於「PHP 和 Oracle」的完美書籍
http://www.oracle.com/technetwork/topics/php/underground-php-oracle-manual-098250.html
Anonymous
7 年前
請記住,您不能將保留字用於綁定變數。否則,您會收到 ORA-01745:無效主機/綁定變數名稱錯誤。
marki at trash-mail dot com
8 年前
請注意,在我先前關於在函式中使用 oci_bind_by_name() 的說明中,當傳回類似「UPDATE table SET bla='blubb' RETURNING id INTO :id」的值時,這會變得更加複雜。

您可以按如下方式執行

<?php
function sql($q, &$vars_in=array(), &$vars_out=array()) {
...
$stid = oci_parse($conn, $q);
...
reset($vars_in);
do {
if (
current($vars_in)===FALSE) {
break;
}
$b = oci_bind_by_name($stid, key($vars_in), current($vars_in));
// 在這裡插入例外處理
} while (each($vars_in) !== FALSE);

// 要傳回的 VARS
// 我們將此修復為整數類型,因為目前我們需要它用於索引 ID
foreach ($vars_out as $k => $v) {
$b = oci_bind_by_name($stid, $k, $vars_out[$k], -1, SQLT_INT);
// 在這裡插入例外處理
}

...
}
?>

像這樣使用

<?php
$blubb
= 'blubb';
$b = array(':bla' => $blubb);
$b_out = array(':id' => ''); // 讓值為空
$x = sql($q, $b, $b_out);
$id = $b_out[':id'];
?>

(重點是:您將無法將任何內容傳回 $b[':bla'],因為 $b[':bla'] 會在 sql() 內部變成 current($vars_in),且無法寫入。)
marki at trash-mail dot com
8 年前
我有一個查詢,乍看之下執行正常,執行時沒有錯誤,什麼也沒有,但是執行階段根本沒有傳回任何結果。

當您將資料庫命令放入函式,並在函式中使用 oci_fetch_xxx() 將變數綁定到該處時,請務必小心。

function sql($conn, $stmt, $var) {
$stid = oci_parse($conn, $stmt);
...
oci_bind_by_name($stid, ':val', $var);
...
}
sql($conn, $q, $var);
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS);

如同你從 oci_bind_by_name() 的定義所見,$var 需要以參考(reference)的方式傳遞,所以你的函式必須像這樣準備好這個參考:

function sql($conn, $stmt, &$var) {
$stid = oci_parse($conn, $stmt);
...
oci_bind_by_name($stid, ':val', $var);
...
}

背景是,如果你不以參考的方式傳遞(在這種情況下,函式內部的 $var 是函式外部 $var 的副本),那麼 oci_bind_by_name() 一開始看起來會運作正常。
然而,由於你用來實際取得資料的 oci_fetch 語句會參考當函式結束時已不存在的 $var。事實上,由於 varbind 看起來像是指標,該指標在那時會指向無效的位置,而你的變數將不會在 SQL 中被替換。

這一切也意味著

1) 你必須傳遞一個變數,而不僅僅是一個值

這樣做無效

$stid = sql($conn, $q, array('bla'=>'blubb'));

這樣比較好

$vars = array('bla'=>'blubb');
$stid = sql($conn, $q, $vars);

2) 即使以參考的方式傳遞給你的輔助函式,你也不能使用例如 foreach

這樣做無效

function sql($conn, $q, $vars) {
...
foreach ($vars as $k => $v) {
oci_bind_by_name($stid, $k, $v);
}
...
}

再次強調,因為 $k 和 $v 是區域變數,一旦你在函式外部執行 oci_fetch,它們就會消失。

相反,你必須以更低階的方式處理陣列,像這樣

function sql($conn, $q, &$vars) {
...
$stid = oci_parse($conn, $q);
...
reset($vars);
do {
if (current($vars)===FALSE) { // 陣列結尾
break;
}
$b = oci_bind_by_name($stid, key($vars), current($vars));
if ($b === FALSE) {
DIE('無法綁定變數');
}
} while (each($vars) !== FALSE);
}
$binds = array(':bla1' => 'blubb1',
':bla2' => 'blubb2');
$stid = sql($conn, $q, $binds);
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS);

無論你在哪裡使用 oci_bind_by_name(),指向初始資料的指標都必須從頭到尾存在。
xorinox at gmx dot ch
10 年前
對我來說,使用 Oracle 以及原始資料類型進出,運作方式如下:

<?php
/*oracle 預存程序
procedure open_session(
i_instance_id in raw,
o_session_id out raw,
o_errcode out number,
o_errmsg out varchar2
);
*/

//開啟資料庫
$conn = DBOpen( DB_DEV_USER );

//取得 session id
$sql = "begin p_loader.open_session( hextoraw( :instance_id ), :session_id, :errcode, :errmsg ); end;";
$stmt = oci_parse( $conn, $sql );
$instanceId = DB_INSTANCE_ID;
oci_bind_by_name( $stmt, ":instance_id", $instanceId, 1, SQLT_CHR );
oci_bind_by_name( $stmt, ":session_id", $sessionId, 16, SQLT_BIN );
oci_bind_by_name( $stmt, ":errcode", $errcode, 12, SQLT_INT );
oci_bind_by_name( $stmt, ":errmsg", $errmsg, 4000, SQLT_CHR );

oci_execute( $stmt );
$sessionId = bin2hex( $sessionId ); //現在這是一個十六進位字串

//關閉資料庫
DBClose( $conn );
?>
ajitsingh4u at gmail dot com
16 年前
//呼叫 Oracle 預存程序
//我假設您有一個 users 表格,並且 users 表格中有三個欄位,即 oracle 中的 id、user、email
//例如,我在建構子中建立了連線,您可以根據您的需求修改。
//http://www.devshed.com/c/a/PHP/Understanding-Destructors-in-PHP-5/1/
<?php
class Users{
private
$connection;

public function
__construct()
{
$this->connection = oci_connect("scott", "tiger", $db); // 建立與 Oracle 伺服器的連線;
}

public function
selectUsers($start_index=1, $numbers_of_rows=20)
{
$sql ="BEGIN sp_users_select(:p_start_index, :p_numbers_of_rows, :p_cursor, :p_result); END;";
$stmt = oci_parse($this->connection, $sql);

//綁定輸入參數
oci_bind_by_name($stmt, ':p_start_index', $start_index, 20);
oci_bind_by_name($stmt, ':p_numbers_of_rows', $numbers_of_rows, 20);

//綁定輸出參數
oci_bind_by_name($stmt, ':p_result', $result, 20); // 如果預存程序成功執行,則返回 0。

//綁定游標
$p_cursor = oci_new_cursor($this->connection);
oci_bind_by_name($stmt, ':p_cursor', $p_cursor, -1, OCI_B_CURSOR);

// 執行語句
oci_execute($stmt);
oci_execute($p_cursor, OCI_DEFAULT);

oci_fetch_all($p_cursor, $cursor, null, null, OCI_FETCHSTATEMENT_BY_ROW);

echo
$result;
echo
'<br>';
var_dump($cursor); // $cursor 是一個關聯陣列,所以我們可以使用 print_r() 來列印此資料。
// 您可以從此函式返回資料,以便在您的使用者介面上使用它。
}

public function
deleteUser($id)
{
$sql ="BEGIN sp_user_delete(:p_id, :p_result); END;";
$stmt = oci_parse($this->connection, $sql);

// 綁定輸入和輸出變數
oci_bind_by_name($stmt, ':p_id', $id, 20);
oci_bind_by_name($stmt, ':p_result', $result, 20);

//執行語句
$check = oci_execute($stmt);

if(
$check == true)
$commit = oci_commit($this->connection);
else
$commit = oci_rollback($this->connection);

return
$result;
}

// 您可以使用上述兩個函式來製作插入、更新的函式

}
?>
Anonymous
17 年前
這就是舊的 OCI_B_* 常數現在的名稱
(PHP 5.1.6 win32)

OCI_B_NTY - SQLT_NTY
OCI_B_BFILE - SQLT_BFILEE
OCI_B_CFILEE - SQLT_CFILEE
OCI_B_CLOB - SQLT_CLOB
OCI_B_BLOB - SQLT_BLOB
OCI_B_ROWID - SQLT_RDD
OCI_B_CURSOR - SQLT_RSET
OCI_B_BIN - SQLT_BIN
OCI_B_INT - SQLT_INT
OCI_B_NUM - SQLT_NUM
Chris Delcamp
17 年前
這是一個從插入中返回主鍵的範例,以便您可以根據該值對其他具有外鍵的表格進行插入。日期僅用於提供半唯一的插入資料。

$conn = oci_connect("username", "password")
$stmt = oci_parse($conn, "INSERT INTO test (test_msg) values (:data) RETURN test_id INTO :RV");
$data = date("d-M-Y H:i:s");
oci_bind_by_name($stmt, ":RV", $rv, -1, SQLT_INT);
oci_bind_by_name($stmt, ":data", $data, 24);
oci_execute($stmt);
print $rv;
hfuecks at nospam dot org
19 年前
請注意,常數識別碼發生了一些變化,並且目前文件不完全準確。

執行以下腳本;

<?php
foreach (array_keys(get_defined_constants()) as $const) {
if (
preg_match('/^OCI_B_/', $const) ) {
print
"$const\n";
}
}
?>

在 PHP 4.4.0 版本中,我得到以下結果:

OCI_B_SQLT_NTY < 在 PHP5 中已重新命名為 OCI_B_NTY
OCI_B_BFILE
OCI_B_CFILEE
OCI_B_CLOB
OCI_B_BLOB
OCI_B_ROWID
OCI_B_CURSOR
OCI_B_BIN

在 PHP 5.0.4 版本中,我得到以下結果:

OCI_B_NTY
OCI_B_BFILE < 文件目前有誤
OCI_B_CFILEE < 文件目前有誤
OCI_B_CLOB
OCI_B_BLOB
OCI_B_ROWID
OCI_B_CURSOR
OCI_B_BIN < 這是一個謎
adrian dot crossley at hesa dot ac dot uk
15 年前
有時您會遇到「ORA-01461: 只能將 LONG 值繫結以插入 LONG 資料行」的錯誤。當您沒有 LONG 資料行或 LONG 值時,此錯誤尤其具有高度誤導性。

根據我的測試,當繫結變數的值超出配置的長度時,似乎會導致此錯誤。

為了避免此錯誤,請務必在繫結 varchar 時指定長度,例如:
<?php
oci_bind_by_name
($stmt,':string',$string, 256);
?>

對於數值,請使用預設長度 (-1),但告訴 Oracle 它是整數,例如:
<?php
oci_bind_by_name
($stmt,':num',$num, -1, SQLT_INT);
?>
jjeffman at cpovo.net
10 年前
設定傳回參數 (:r) 的最大長度非常重要,即使它傳回的是數字,否則可能會引發 ORA-01460 例外 (請求未實作或不合理的轉換)。
To Top