当使用 OCI_RETURN_LOBS 获取 BFILE(使用目录存储)时,用户需要对目录进行读取操作。(GRANT READ on DIRECTORY <目录名称> TO <用户>)否则,您将收到一个难以理解的错误。警告:OCILobFileOpen:ORA-22285:在 ... 的 ... 行上进行 FILEOPEN 操作时,目录或文件不存在。
<BR>
创建目录的用户会自动获得 READ WITH THE GRANT OPTION 权限。
(PHP 5, PHP 7, PHP 8, PECL OCI8 >= 1.1.0)
oci_fetch_array — 将查询的下一行作为关联数组或数字数组返回
返回一个包含查询的下一结果集行的数组。每个数组条目对应于行的列。此函数通常在循环中调用,直到它返回 false
,表示不再存在行。
如果 statement
对应于返回 Oracle 数据库隐式结果集的 PL/SQL 块,那么将连续获取所有集合中的行。如果 statement
是由 oci_get_implicit_resultset() 返回的,那么将只返回一个子查询的行的子集。
有关 OCI8 扩展执行的数据类型映射的详细信息,请参阅 驱动程序支持的数据类型
statement
一个有效的 OCI8 语句标识符,由 oci_parse() 创建并由 oci_execute() 执行,或一个 REF CURSOR
语句标识符。
也可以是 oci_get_implicit_resultset() 返回的语句标识符。
mode
可选的第二个参数可以是以下常量的任何组合
常量 | 说明 |
---|---|
OCI_BOTH |
返回一个包含关联和数字索引的数组。这与 OCI_ASSOC + OCI_NUM 相同,也是默认行为。 |
OCI_ASSOC |
返回一个关联数组。 |
OCI_NUM |
返回一个数字数组。 |
OCI_RETURN_NULLS |
为 null 字段创建元素。元素值将是 PHP null 。 |
OCI_RETURN_LOBS |
返回 LOB 的内容,而不是 LOB 描述符。 |
默认的 mode
是 OCI_BOTH
。
使用加号运算符“+”一次指定多个模式。
返回一个包含关联和/或数字索引的数组。如果 statement
中没有更多行,则返回 false
。
默认情况下,LOB
列作为 LOB 描述符返回。
DATE
列作为格式化为当前日期格式的字符串返回。默认格式可以使用 Oracle 环境变量(如 NLS_LANG
)更改,或者通过先前执行的 ALTER SESSION SET NLS_DATE_FORMAT
命令更改。
Oracle 的默认不区分大小写的列名在结果数组中将具有大写关联索引。区分大小写的列名将具有使用确切列大小写的数组索引。使用 var_dump() 在结果数组上验证每个查询要使用的适当大小写。
表名不包含在数组索引中。如果查询包含两个名称相同的不同列,请使用 OCI_NUM
或向查询添加列别名以确保名称唯一性,请参见示例 #7。否则,将只通过 PHP 返回一列。
示例 #1 oci_fetch_array() 与 OCI_BOTH
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
$stid = oci_parse($conn, 'SELECT department_id, department_name FROM departments');
oci_execute($stid);
while (($row = oci_fetch_array($stid, OCI_BOTH)) != false) {
// 使用大写列名作为关联数组索引
echo $row[0] . " and " . $row['DEPARTMENT_ID'] . " are the same<br>\n";
echo $row[1] . " and " . $row['DEPARTMENT_NAME'] . " are the same<br>\n";
}
oci_free_statement($stid);
oci_close($conn);
?>
示例 #2 oci_fetch_array() 与 OCI_NUM
<?php
/*
在运行之前,创建表:
CREATE TABLE mytab (id NUMBER, description CLOB);
INSERT INTO mytab (id, description) values (1, 'A very long string');
COMMIT;
*/
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
$stid = oci_parse($conn, 'SELECT id, description FROM mytab');
oci_execute($stid);
while (($row = oci_fetch_array($stid, OCI_NUM)) != false) {
echo $row[0] . "<br>\n";
echo $row[1]->read(11) . "<br>\n"; // 这将输出 DESCRIPTION 的前 11 个字节
}
// 输出为:
// 1
// A very long
oci_free_statement($stid);
oci_close($conn);
?>
示例 #3 oci_fetch_array() 与 OCI_ASSOC
<?php
/*
在运行之前,创建表:
CREATE TABLE mytab (id NUMBER, description CLOB);
INSERT INTO mytab (id, description) values (1, 'A very long string');
COMMIT;
*/
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
$stid = oci_parse($conn, 'SELECT id, description FROM mytab');
oci_execute($stid);
while (($row = oci_fetch_array($stid, OCI_ASSOC)) != false) {
echo $row['ID'] . "<br>\n";
echo $row['DESCRIPTION']->read(11) . "<br>\n"; // 这将输出 DESCRIPTION 的前 11 个字节
}
// 输出为:
// 1
// A very long
oci_free_statement($stid);
oci_close($conn);
?>
示例 #4 oci_fetch_array() 与 OCI_RETURN_NULLS
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
$stid = oci_parse($conn, 'SELECT 1, null FROM dual');
oci_execute($stid);
while (($row = oci_fetch_array ($stid, OCI_ASSOC)) != false) { // 忽略 NULL 值
var_dump($row);
}
/*
上面的代码打印:
array(1) {
[1]=>
string(1) "1"
}
*/
$stid = oci_parse($conn, 'SELECT 1, null FROM dual');
oci_execute($stid);
while (($row = oci_fetch_array ($stid, OCI_ASSOC+OCI_RETURN_NULLS)) != false) { // 获取 NULL 值
var_dump($row);
}
/*
上面的代码打印:
array(2) {
[1]=>
string(1) "1"
["NULL"]=>
NULL
}
*/
?>
示例 #5 oci_fetch_array() 与 OCI_RETURN_LOBS
<?php
/*
在运行之前,创建表:
CREATE TABLE mytab (id NUMBER, description CLOB);
INSERT INTO mytab (id, description) values (1, 'A very long string');
COMMIT;
*/
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
$stid = oci_parse($conn, 'SELECT id, description FROM mytab');
oci_execute($stid);
while (($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_LOBS)) != false) {
echo $row['ID'] . "<br>\n";
echo $row['DESCRIPTION'] . "<br>\n"; // 这包含了整个 DESCRIPTION
// 在循环中,在第二次获取之前释放大型变量可以减少 PHP 的峰值内存使用量
unset($row);
}
// 输出为:
// 1
// A very long string
oci_free_statement($stid);
oci_close($conn);
?>
示例 #6 oci_fetch_array() 与区分大小写的列名
<?php
/*
运行前,创建表:
CREATE TABLE mytab ("Name" VARCHAR2(20), city VARCHAR2(20));
INSERT INTO mytab ("Name", city) values ('Chris', 'Melbourne');
COMMIT;
*/
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
$stid = oci_parse($conn, 'select * from mytab');
oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS);
// 因为 'Name' 被创建为区分大小写的列,所以数组索引使用相同的 case。但是大写 'CITY' 必须
// 用于区分大小写列的索引
print $row['Name'] . "<br>\n"; // 打印 Chris
print $row['CITY'] . "<br>\n"; // 打印 Melbourne
oci_free_statement($stid);
oci_close($conn);
?>
示例 #7 oci_fetch_array() 带有重复名称的列
<?php
/*
运行前,创建表:
CREATE TABLE mycity (id NUMBER, name VARCHAR2(20));
INSERT INTO mycity (id, name) values (1, 'Melbourne');
CREATE TABLE mycountry (id NUMBER, name VARCHAR2(20));
INSERT INTO mycountry (id, name) values (1, 'Australia');
COMMIT;
*/
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
$sql = 'SELECT mycity.name, mycountry.name
FROM mycity, mycountry
WHERE mycity.id = mycountry.id';
$stid = oci_parse($conn, $sql);
oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC);
var_dump($row);
// 输出只包含一个 "NAME" 条目:
// array(1) {
// ["NAME"]=>
// string(9) "Australia"
// }
// 要查询重复的列名,使用 SQL 列别名,例如 "AS ctnm":
$sql = 'SELECT mycity.name AS ctnm, mycountry.name
FROM mycity, mycountry
WHERE mycity.id = mycountry.id';
$stid = oci_parse($conn, $sql);
oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC);
var_dump($row);
// 输出现在包含两个选定的列:
// array(2) {
// ["CTNM"]=>
// string(9) "Melbourne"
// ["NAME"]=>
// string(9) "Australia"
// }
oci_free_statement($stid);
oci_close($conn);
?>
示例 #8 oci_fetch_array() 带有 DATE
列
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
// 为此连接设置日期格式。
// 出于性能原因,请考虑在触发器中或使用环境变量来更改格式
$stid = oci_parse($conn, "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'");
oci_execute($stid);
$stid = oci_parse($conn, 'SELECT hire_date FROM employees WHERE employee_id = 188');
oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC);
echo $row['HIRE_DATE'] . "<br>\n"; // 打印 1997-06-14
oci_free_statement($stid);
oci_close($conn);
?>
示例 #9 oci_fetch_array() 带有 REF CURSOR
<?php
/*
创建 PL/SQL 存储过程如下:
CREATE OR REPLACE PROCEDURE myproc(p1 OUT SYS_REFCURSOR) AS
BEGIN
OPEN p1 FOR SELECT * FROM all_objects WHERE ROWNUM < 5000;
END;
*/
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
$stid = oci_parse($conn, 'BEGIN myproc(:rc); END;');
$refcur = oci_new_cursor($conn);
oci_bind_by_name($stid, ':rc', $refcur, -1, OCI_B_CURSOR);
oci_execute($stid);
// 执行返回的 REF CURSOR 并像语句标识符一样从中获取
oci_execute($refcur);
echo "<table border='1'>\n";
while (($row = oci_fetch_array($refcur, OCI_ASSOC+OCI_RETURN_NULLS)) != false) {
echo "<tr>\n";
foreach ($row as $item) {
echo " <td>".($item !== null ? htmlentities($item, ENT_QUOTES) : " ")."</td>\n";
}
echo "</tr>\n";
}
echo "</table>\n";
oci_free_statement($refcur);
oci_free_statement($stid);
oci_close($conn);
?>
示例 #10 使用 LIMIT
类查询的 oci_fetch_array() 分页
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
// 查找数据库版本
preg_match('/Release ([0-9]+)\./', oci_server_version($conn), $matches);
$oracleversion = $matches[1];
// 这是您要“分页”的查询
$sql = 'SELECT city, postal_code FROM locations ORDER BY city';
if ($oracleversion >= 12) {
// 利用 Oracle 12c OFFSET / FETCH NEXT 语法
$sql = $sql . ' OFFSET :offset ROWS FETCH NEXT :numrows ROWS ONLY';
} else {
// 较旧的 Oracle 版本需要一个嵌套查询,从 $sql 中选择一个子集
// 或者,如果在开发时已知 SQL 语句,请考虑使用 row_number() 函数代替此
// 嵌套解决方案。在生产环境中,请注意使用连接避免 SQL 注入问题。
$sql = "SELECT * FROM (SELECT a.*, ROWNUM AS my_rnum
FROM ($sql) a
WHERE ROWNUM <= :offset + :numrows)
WHERE my_rnum > :offset";
}
$offset = 0; // 跳过这些行
$numrows = 5; // 返回 5 行
$stid = oci_parse($conn, $sql);
oci_bind_by_name($stid, ':numrows', $numrows);
oci_bind_by_name($stid, ':offset', $offset);
oci_execute($stid);
while (($row = oci_fetch_array($stid, OCI_ASSOC + OCI_RETURN_NULLS)) != false) {
echo $row['CITY'] . " " . $row['POSTAL_CODE'] . "<br>\n";
}
// 输出为:
// Beijing 190518
// Bern 3095
// Bombay 490231
// Geneva 1730
// Hiroshima 6823
oci_free_statement($stid);
oci_close($conn);
?>
示例 #11 使用 Oracle 数据库隐式结果集的 oci_fetch_array()
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/pdborcl');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
// 需要 OCI8 2.0(或更高版本)和 Oracle 数据库 12c(或更高版本)
// 也请参见 oci_get_implicit_resultset()
$sql = 'DECLARE
c1 SYS_REFCURSOR;
BEGIN
OPEN c1 FOR SELECT city, postal_code FROM locations WHERE ROWNUM < 4 ORDER BY city;
DBMS_SQL.RETURN_RESULT(c1);
OPEN c1 FOR SELECT country_id FROM locations WHERE ROWNUM < 4 ORDER BY city;
DBMS_SQL.RETURN_RESULT(c1);
END;';
$stid = oci_parse($conn, $sql);
oci_execute($stid);
// 注意:oci_fetch_all 和 oci_fetch 不能以这种方式使用
echo "<table>\n";
while (($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) != false) {
echo "<tr>\n";
foreach ($row as $item) {
echo " <td>".($item!==null?htmlentities($item, ENT_QUOTES|ENT_SUBSTITUTE):" ")."</td>\n";
}
echo "</tr>\n";
}
echo "</table>\n";
// 输出为:
// Beijing 190518
// Bern 3095
// Bombay 490231
// CN
// CH
// IN
oci_free_statement($stid);
oci_close($conn);
?>
注意:
对于使用不区分大小写名称创建的标准 Oracle 列,关联数组索引需要大写。
注意:
对于返回大量行的查询,可以通过增加 oci8.default_prefetch 或使用 oci_set_prefetch() 来显著提高性能。
注意:
oci_fetch_array() 的速度比 oci_fetch_assoc() 或 oci_fetch_row() 略慢,但更灵活。
当使用 OCI_RETURN_LOBS 获取 BFILE(使用目录存储)时,用户需要对目录进行读取操作。(GRANT READ on DIRECTORY <目录名称> TO <用户>)否则,您将收到一个难以理解的错误。警告:OCILobFileOpen:ORA-22285:在 ... 的 ... 行上进行 FILEOPEN 操作时,目录或文件不存在。
<BR>
创建目录的用户会自动获得 READ WITH THE GRANT OPTION 权限。
这里有一个关于 rowid 的提示。
不要忘记 oracle 函数
"rowidtochar" 和 "chartorowid"
"select rowidtochar(rowid) as FOO from table ...."
当您想在表单或链接中传递 rowid 时,这就是
唯一的方法。