我試圖確定為什么與使用 OCI8 的 PHP 7.4 相比,使用 python3 和 cx_Oracle 回傳 CLOB 資料的查詢運行得更快。
Oracle 客戶端庫版本為 19.5.0.0.0。使用同一用戶在同一客戶端和同一資料庫上運行查詢。請參閱下面的測驗 PHP 和 Python 腳本。PHP 腳本需要大約 16 秒左右的時間來檢索 220 行的結果集。Python 腳本需要 0.2 秒的時間來運行并將結果集列印到控制臺。python cx_Oracle 包是否存在某種客戶端快取?或者,與 PHP 擴展將 CLOB 作為字串檢索的方式相比,將 CLOB 作為字串檢索的 outputtypehandler 有什么不同?cx_Oracle 包可能利用 Oracle 12.2 及更高版本的 LOB 預取?
<?php
// Create connection to Oracle, change HOST IP and SID string!
$conn = oci_connect("user", "password", 'host');
print oci_client_version() . PHP_EOL;
if (!$conn) {
$m = oci_error();
echo $m['message'] . PHP_EOL;
exit;
}
else {
print "Oracle database connection online" . PHP_EOL;
}
$stid = oci_parse($conn, "SELECT
EMS.MODULE_NAME,
EM.MAINT_TITLE,
EM.MAINT_VALIDATION,
CASE EMS.DATA_TYPE WHEN 8 THEN 'Logic' WHEN 9 THEN 'Action' END AS DATA_TYPE,
EM.MAINT_AUTHOR,
-- Return all three of the below to speed up the report as LOB retrieval is slow
-- CAST(EMS.EKM_INFO AS VARCHAR2(4000)) AS CLOB_VARCHAR,
-- DBMS_LOB.GETLENGTH(EMS.EKM_INFO) AS CLOB_LENGTH,
EMS.EKM_INFO AS CLOB
FROM
EKS_MODULE EM,
EKS_MODULESTORAGE EMS
WHERE
EMS.MODULE_NAME = EM.MODULE_NAME
AND EMS.VERSION = EM.VERSION
AND EMS.DATA_TYPE IN (8, 9)
AND EMS.EKM_INFO LIKE '%@PAGER%' -- This is a CLOB field so we can filter out anything that doesn't actually use @PAGER
AND EM.ACTIVE_FLAG = 'A'
ORDER BY
EM.MAINT_VALIDATION DESC, EMS.MODULE_NAME");
list($usec, $sec) = explode(" ", microtime());
$start = (float)$usec (float)$sec;
oci_execute($stid);
$num_results = oci_fetch_all($stid, $results);
list($usec, $sec) = explode(" ", microtime());
$end = (float)$usec (float)$sec;
print ($end - $start) . ' seconds' . PHP_EOL;
print $num_results . PHP_EOL;
?>
import cx_Oracle
import time
from pprint import pprint
def output_type_handler(cursor, name, default_type, size, precision, scale):
if default_type == cx_Oracle.DB_TYPE_CLOB:
return cursor.var(cx_Oracle.DB_TYPE_LONG, arraysize=cursor.arraysize)
if default_type == cx_Oracle.DB_TYPE_BLOB:
return cursor.var(cx_Oracle.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize)
print(cx_Oracle.clientversion())
connection = cx_Oracle.connect(
user="user",
password="password",
dsn="server")
print("Successfully connected to Oracle Database")
cursor = connection.cursor()
sql = """
SELECT
EMS.MODULE_NAME,
EM.MAINT_TITLE,
EM.MAINT_VALIDATION,
CASE EMS.DATA_TYPE WHEN 8 THEN 'Logic' WHEN 9 THEN 'Action' END AS DATA_TYPE,
EM.MAINT_AUTHOR,
-- Return all three of the below to speed up the report as LOB retrieval is slow
-- CAST(EMS.EKM_INFO AS VARCHAR2(4000)) AS CLOB_VARCHAR,
-- DBMS_LOB.GETLENGTH(EMS.EKM_INFO) AS CLOB_LENGTH,
EMS.EKM_INFO AS CLOB
FROM
EKS_MODULE EM,
EKS_MODULESTORAGE EMS
WHERE
EMS.MODULE_NAME = EM.MODULE_NAME
AND EMS.VERSION = EM.VERSION
AND EMS.DATA_TYPE IN (8, 9)
AND EMS.EKM_INFO LIKE '%@PAGER%' -- This is a CLOB field so we can filter out anything that doesn't actually use @PAGER
AND EM.ACTIVE_FLAG = 'A'
ORDER BY
EM.MAINT_VALIDATION DESC, EMS.MODULE_NAME
"""
start = time.time()
connection.outputtypehandler = output_type_handler
cursor.execute(sql)
rows = cursor.fetchall()
stop = time.time()
pprint(rows)
print(f"Elapsed time: {stop - start}")
uj5u.com熱心網友回復:
一個很大的區別是 Python 示例中的輸出型別處理程式。(但請注意,這將只允許獲取最多 1GB 的 LOBS)。這比在沒有 Python 型別處理程式的情況下或在 PHP OCI8 中發生的 Oracle LOB 定位器的內部提取要高效得多。定位器就像指標。使用定位器,需要在應用程式和資料庫之間進行額外的往返來獲取實際的 LOB 資料。cx_Oracle 檔案和示例return_lobs_as_strings.py 中提到了這一點。
另一個區別是 cx_Oracle 支持行陣列獲取(每次訪問資料庫時獲取多行),而 PHP OCI8 只有行預取 - 這不適用于 LOB。在 PHP 中獲取每一行需要一個單獨的資料庫往返。
使用來自 PECL 的 PHP OCI8 3.2 for PHP 8.1,或使用 PHP 8.2 開發分支中捆綁的 OCI8,您可以啟用LOB 預取,這可以在處理非大型 LOBS 時為 PHP OCI8 提供很好的提升。這可以洗掉往返以從每個定位器獲取資料。但是,您無法克服 PHP OCI8 中缺少陣列獲取的問題。
在 PHP 中計算往返次數的一些片段是:
function getSid($c) {
$sql = "SELECT sys_context('userenv', 'sid') FROM dual";
$s = oci_parse($c, $sql);
oci_execute($s);
$arr = oci_fetch_array($s);
return($arr[0]);
}
function getRoundTrips($systemconn, $sid) {
$sql = <<<'ENDND'
SELECT ss.value
FROM v$sesstat ss, v$statname sn
WHERE ss.sid = :sid
AND ss.statistic# = sn.statistic#
AND sn.name LIKE '%roundtrip%client%'
ENDND;
$s = oci_parse($systemconn, $sql);
oci_bind_by_name($s, ":sid", $sid);
oci_execute($s);
$arr = oci_fetch_array($s);
return($arr[0]);
}
您可以getRoundTrips()在運行應用程式查詢之前和之后呼叫。這兩個值之間的差異是您的查詢進行的往返次數。請注意使用不同的系統連接來獲取統計資訊。這些片段可以很容易地用 Python 重寫。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/398204.html
上一篇:從同一行的不同列中選擇n個最高值
下一篇:用sql中的最新時間更新表
