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

绑定允许数据库重用语句上下文和来自先前执行语句的缓存,即使另一个用户或进程最初执行了该语句。绑定减少了 SQL 注入问题,因为与绑定变量关联的数据永远不会被视为 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 和 name,然后更新 salary
// 使用以下语句创建表:
// 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 和 name
$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 列使用空白填充比较语义。为了使 WHERE 子句成功,您的 PHP 变量应填充与该列相同的宽度。

注意:

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() 函数从子查询中获取 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 倍。将第 3 行更改为

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

将解决此问题。

此问题也存在于 ADODB DB 类(adodb.sf.net)中,在使用 SelectLimit 方法时要小心。
splintyg at gmail dot com
7 年前
各位,我一直在寻找很长时间,如何将 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
[email protected]
9 个月前
关于 PHP 变量参数是引用以及某些类型的循环无法正常工作这一点非常重要。但是,如果创建一个临时变量,在绑定中使用它,然后取消设置它,则可以使 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
[email protected]
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";
[email protected]
5 年前
如果在将 FLOAT 值插入 NUMBER 列时出现“ORA-01722: 无效数字错误”,请根据当前区域设置检查绑定值的格式是否正确。

默认的“american”区域设置假设发送到 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 也能正确捕获它。
[email protected]
7 年前
各位,我一直在寻找很长时间,如何将 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
匿名
7 年前
请记住,您不能将保留字用于绑定变量。否则,您将收到 ORA-01745: 无效主机/绑定变量名称错误。
[email protected]
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),并且无法写入。)
[email protected]
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
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 );

//获取会话 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 表,并且在 Oracle 的 users 表中存在三列,即 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