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 注入安全问题的一种方法。

绑定允许数据库重用语句上下文和来自先前执行语句的缓存,即使另一个用户或进程最初执行了该语句。绑定减少了 SQL 注入的风险,因为与绑定变量关联的数据永远不会被视为 SQL 语句的一部分。它不需要引用或转义。

已绑定的 PHP 变量可以更改,并且语句可以重新执行,而无需重新解析语句或重新绑定。

在 Oracle 中,绑定变量通常分为 IN 绑定(用于传递到数据库的值)和 OUT 绑定(用于返回到 PHP 的值)。绑定变量可以同时是 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 的当前长度来设置最大长度。在这种情况下,var 必须存在,并且在调用 oci_bind_by_name() 时包含数据。

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";
}
// 输出结果是
// 北京
// 新加坡

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

?>

对于少量固定的 IN 子句条件,使用单独的绑定变量。运行时未知的值可以设置为 NULL。这允许单个语句被所有应用程序用户使用,最大限度地提高 Oracle DB 缓存效率。

示例 #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 在 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("一个很长的字符串");

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 BOOLEAN

<?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 notes

abiyi2000 at yahoo dot com
12 年前
不幸的是,我花了一整天时间试图让它作为 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
7 年前
示例 #7 仅展示了在 IN 子句中绑定少量固定数量的值。还有一种方法可以使用可变数量的值绑定多个条件。

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

$conn = oci_pconnect($user, $pass, $tns);
// 使用 ORACLE table() 函数从子查询中获取 id
$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 倍。将第三行更改为

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

将解决此问题。

此问题也存在于 ADODB DB 类(adodb.sf.net)中,您在使用 SelectLimit 方法时要小心。
splintyg at gmail dot com
6 年前
伙计们,我一直都在寻找如何将 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
6 个月前
关于 PHP var 参数是引用,某些类型的循环无法正常工作的说明在这里非常重要。但是,如果您创建一个临时变量,在绑定中使用它,然后取消设置它,则可以使 foreach 循环正常工作。例如

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

这将每个键绑定到 $value 的位置,但在绑定后取消设置它时,可以再次设置和使用它。

https://php.net/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
4 年前
如果您在将 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
6 年前
伙计们,我一直都在寻找如何将 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);

// 要返回的变量
// 暂时将类型改为整数,因为现在需要它来作为索引 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 需要作为引用传递,因此你的函数需要像这样准备好这个引用

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
9 年前
使用 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 );

// 获取会话 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 表,其中包含三个列:id、user 和 email,这些列都在 Oracle 中。
// 例如,我在构造函数中建立连接,你可以根据自己的需求进行修改。
//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;
}

// 可以使用上面的两个函数创建插入、更新函数

}
?>
匿名
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("用户名", "密码")
$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
18 年前
请注意,常量标识符有一些更改,并且文档目前并不完全准确。

运行以下脚本;

<?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: can bind a LONG value only for insert into a LONG column"。这个错误非常误导人,尤其是在你没有 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