技術交流

Using ADODB with PHP and Oracle (Part III)

Dennis Dennis

在上期Using ADODB with PHP and Oracle (Part II) 中我們討論ADODB連Oracle Database時如何 Call Oracle Store Procedure 和 Functions,介紹了 In/Outer Parameter的 Procedures和直接調用Functions的方法。本部分我們一起討論與效能有關的方法及技巧。

在SQL中,大部分的Performance Issue都是由於Select語句引起,部分是由於Insert / Update語句造成。本期我們來討論如何利用ADODB中提供的方法來提高Performance。

在ADODB中,提供Caching的功能來提高查詢的效能。如果有大量的Insert或是Update 語句時,利用Prepare( )把相關的語句重複利用,讓Database Driver SQL Parser只解析一次,可以重複利用這些語句。

Caching

在實做應用程式時,可以針對以下情況caching以減輕Database Server 的負擔,並提高應用程式的回應速度:

  • User對資料及時性要求不高的資料。
  • 查詢不會頻繁修改的資料。
  • Select 語句有複雜的 where condition。
  • Select 語句中存在有 Order by 或是 Group B,非常影響效能。

為解決以上Performance Issue,ADODB 提供了caching的功能。當要使用caching的功能時,要先定義一個caching數據存放的地方。ADODB 中有一個內置的變量叫作 “ADODB_CACHE_DIR”,我們可以透過程式動態設定這個存放路徑也可以在初始化時指定一個固定的資料夾,後面的例子中我們會列出採用兩種不同方式設定的代碼。在使用caching的時候我們還可以根據需要設定caching的時間,時間過期後程式才會重新到 Database 中去拿資料。

設置caching路徑及時間:動態設定


// Caching Sample
$sql = 'select * from emp where id=:id';
$ADODB_CACHE_DIR = '/eHR3/tmp/AdodbCache'; // 設定 cache directory
$rs = $DbConn->CacheExecute(3600,$sql,array('id'=>1)); // caching 1 小時

Caching 的方法

  • CacheGetArray():從caching的資料中取出 2-D Array (多筆記錄)。
  • CacheGetRow():從caching的資料中取出 2-D Array (單筆記錄)。
  • CacheGetOne():從caching的資料中取出一個 column 的值。
  • CacheSelectLimit():從caching資料中取出指定筆數的 2-D Array (多筆記錄)。

這四個 Function 的第一個參數是 Data 要caching的時間,單位為”秒”,第二個參數是 SQL Statements,第三個參數您也可以傳以綁定變量為下標的數組。且與GetArray()、 GetRow()、GetOne()及 SelectLimit 功能類似,只是第一個參數不同,CacheXXX 的第一個參數是要caching的時間。

設置caching路徑及時間:一次設定

除了上面的例子中所列,動態設定之外,還可以一次性設定,其它地方用到之時不用再設定。


$ADODB_CACHE_DIR = '/eHR3/tmp/AdodbCache'; // 設定 cache directory
$DbConn->cacheSecs = 3600; // caching 1 小時
$sql = 'select * from emp where id=:id';
$rs = $DbConn->CacheExecute($sql,array('id'=>1)); // 此後執行預設都是caching一小時

caching的路徑可能是同一個地方,設定一次就好。至於caching的時間,就要根據商業邏輯來做不同的設定了。在實際應用時,根據程式的需要選擇合適的方式。

Prepare()

PHP 連結 Oracle 在執行相關 SQL 或是 Store Procedure 時,Database Driver 都會對這些 Statement 進行解析,看其有沒有語法上的錯誤,解析通過後才會傳到 Database 中執行。

那在什麼時機下適合用 Prepare() 呢?正如前所述,當我們在重復執行某個SQL( insert、 update or delete),SQL 語句是一樣的。只是欄位值或是條件值不一樣,傳統的方法我們可能會用如下方式:


// 新增 1000 筆資料到 table emp 中 
for ($i=0; $i<1000; $i++) 
{ 
 $sql ='insert into emp(id,name) values(:id,:name)'; 
 $DbConn->Execute($sql,array('id'=>$i,'name'=>'Dennis'.$i); 
} 
// update 
$emp_tax_rate = array(); 
$emp_list = array(); 
for ($i=0; $iExecute($sql,array('tax_rate'=>
$emp_tax_rate[$j]['tax_rate'],'id'=>$emp_list[$i]['emp_id']); 
   } 
  } 
} 

這樣的代碼當然可以執行,但是當資料量大時,效能上可能就會有問題。而用 Prepare() 的方式修改後的代碼如下:


// 新增 1000 筆資料到 table emp 中 
$sql = 'insert into emp(id,name) values(:id,:name)'; 
// Prepare SQL once before execute 
$DbConn->Prepare ( $sql ); 
for($i = 0; $i < 1000; $i ++) { 
 $DbConn->Execute($sql,array('id'=>$i,'name'=>'Dennis'.$i); 
} 
// update Sample 
$emp_tax_rate = array (); 
$emp_list = array (); 
$sql = 'update emp set tax_rate =  :tax_rate where id=:id'; 
// Prepare SQL once before execute 
$DbConn->Prepare ( $sql ); 
for($i = 0; $i < count ( $emp_list ); $i ++) { 
 for($j = 0; $j < count ( $emp_tax_rate ); $j ++) { 
  if ($emp_list [$i] ['emp_id'] == $emp_tax_rate [$j] ['emp_id'])
{ 
   $DbConn->Execute($sql,array('tax_rate'=> 
$emp_tax_rate[$j]['tax_rate'], 
              'id'=>$emp_list[$i]['emp_id']) 
     ; 
    } 
   } 
  } 

在實際的應用中,比如說資料批量的導入,用 Prepare() 的方式會大大提高程式運行的效能。有興趣的朋友,可測試一下上面兩種方式執行的結果及所用的時間。