PHP 日本研討會 2024

oci_set_prefetch

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

oci_set_prefetch設定查詢預先提取的列數

說明

oci_set_prefetch(資源 $statement, int $rows): bool

設定在成功呼叫 oci_execute() 查詢後,以及每次後續對資料庫的內部提取請求時,Oracle Client 程式庫要緩衝的列數。對於傳回大量列的查詢,將預先提取計數增加到高於預設值 oci8.default_prefetch 的值,可以顯著提高效能。

預先提取是 Oracle 從資料庫在每個網路請求中傳回多個資料列的有效方式。這可以提升網路和 CPU 的使用率。列的緩衝是 OCI8 內部的,而且無論預先提取計數為何,OCI8 提取函式的行為都不會改變。例如,oci_fetch_row() 將永遠傳回一列。預先提取緩衝區是每個陳述式的,且不會由重新執行的陳述式或其他連線使用。

在呼叫 oci_execute() 之前,呼叫 oci_set_prefetch()

調整目標是將預先提取值設定為網路和資料庫可以處理的合理大小。對於傳回非常大量列的查詢,如果從資料庫分塊擷取列(即將預先提取值設定為小於列數),則整體系統效率可能會更高。這允許資料庫在 PHP 腳本處理目前列集時處理其他使用者的陳述式。

查詢預先提取是在 Oracle 8i 中引入的。REF CURSOR 預先提取是在 Oracle 11gR2 中引入的,且當 PHP 與 Oracle 11gR2(或更新版本)用戶端程式庫連結時發生。巢狀游標預先提取是在 Oracle 11gR2 中引入的,且需要 Oracle 用戶端程式庫和資料庫的版本皆為 11gR2 或更高版本。

當查詢包含 LONG 或 LOB 資料行時,不支援預先提取。預先提取值將被忽略,且當不支援預先提取的所有情況下,將使用單列提取。

當使用 Oracle Database 12c 時,PHP 設定的預先提取值可能會被 Oracle 的用戶端 oraaccess.xml 組態檔覆寫。請參閱 Oracle 文件以取得更多詳細資訊。

參數

statement

oci_parse() 建立並由 oci_execute() 執行的有效 OCI8 陳述式識別符,或 REF CURSOR 陳述式識別符。

rows

要預先提取的列數,>= 0

傳回值

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

範例

範例 1 變更查詢的預設預先提取值

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/XE');

$stid = oci_parse($conn, 'SELECT * FROM myverybigtable');
oci_set_prefetch($stid, 300); // 在呼叫 oci_execute() 之前設定
oci_execute($stid);

echo
"<table border='1'>\n";
while (
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {
echo
"<tr>\n";
foreach (
$row as $item) {
echo
" <td>".($item !== null ? htmlentities($item, ENT_QUOTES) : "&nbsp;")."</td>\n";
}
echo
"</tr>\n";
}
echo
"</table>\n";

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

?>

範例 2 變更 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');

$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);

// 在執行游標之前變更預先提取。
// 當 PHP 與 Oracle 11gR2 或更新版本的用戶端程式庫連結時,REF CURSOR 預先提取才會生效
oci_set_prefetch($refcur, 200);
oci_execute($refcur);

echo
"<table border='1'>\n";
while (
$row = oci_fetch_array($refcur, OCI_ASSOC+OCI_RETURN_NULLS)) {
echo
"<tr>\n";
foreach (
$row as $item) {
echo
" <td>".($item !== null ? htmlentities($item, ENT_QUOTES) : "&nbsp;")."</td>\n";
}
echo
"</tr>\n";
}
echo
"</table>\n";

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

?>

如果 PHP OCI8 從 REF CURSOR 擷取,然後將 REF CURSOR 傳回第二個 PL/SQL 程序進行進一步處理,則將 REF CURSOR 預先提取計數設定為 0,以避免結果集中「遺失」列。預先提取值是在每個 OCI8 內部對資料庫的請求中擷取的額外列數,因此將其設定為 0 表示一次僅擷取一列。

範例 #3 將 REF CURSOR 傳回 Oracle 時設定預取值

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/orcl');

// 取得 REF CURSOR
$stid = oci_parse($conn, 'BEGIN myproc(:rc_out); END;');
$refcur = oci_new_cursor($conn);
oci_bind_by_name($stid, ':rc_out', $refcur, -1, OCI_B_CURSOR);
oci_execute($stid);

// 顯示兩列,但不要預取任何額外的列,否則
// 這些額外的列將不會傳回給 myproc_use_rc()。
oci_set_prefetch($refcur, 0);
oci_execute($refcur);
$row = oci_fetch_array($refcur);
var_dump($row);
$row = oci_fetch_array($refcur);
var_dump($row);

// 將 REF CURSOR 傳遞給 myproc_use_rc() 以使用結果集執行更多資料處理
$stid = oci_parse($conn, 'begin myproc_use_rc(:rc_in); end;');
oci_bind_by_name($stid, ':rc_in', $refcur, -1, OCI_B_CURSOR);
oci_execute($stid);

?>

參見

新增註解

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

1
bmichael at goldparrot dot com
21 年前
如果您在任何專案中使用 Oracle 的 OCI 函式庫(PHP 也會使用),則可以使用此限制。

我已對此參數的效果進行網路層級測試。它確實提高了效率。大幅提高。

Oracle 使用 SQL*Net 作為您的連線與資料庫之間資料的傳輸機制。這就是為什麼您必須正確設定 Oracle 的原因。

此參數會告知 SQL*NET 緩衝更多結果。當 SQL*NET(在伺服器端)收到資料請求時,它會將 X 列(1、2、3、1000 等)捆綁在一起進行傳輸。它會將適當的 SQL*NET 標頭傳回給用戶端,等待 ACK,然後開始以 MTU 大小的區塊傳送資料(乙太網路類似於 1500 位元組,而 ATM 的 WAN 大約為 1000 位元組)。區塊大小也可以在 SQL*NET 中調整,但改進幅度較小。

然後,TCP/IP 會透過網路傳輸資料,將其分解為多個 TCP/IP 封包。

交換完成後,SQL*NET 用戶端會傳送一個
ACK 回到 SQL*NET 接聽器(Oracle 伺服器),交易即完成。

每次往返時,SQL*NET 都會在伺服器記憶體(UGA - 使用者全域區域)內尋找查詢結果。然後,它會擷取需要傳送的列。無論是一列還是 1000 列。流程都是相同的。

關於資料庫本身的反應,我有很多可以告訴您。如果您可以大幅減少往返次數,那就太棒了。

如需 Oracle OCI 的更多資訊,請前往 http://otn.oracle.com
To Top