You say eether and I say eyether, You say neether and I say nyther; Eether, eyether, neether, nyther - Let's call the whole thing off ! You like potato and I like po-tah-to, You like tomato and I like to-mah-to; Potato, po-tah-to, tomato, to-mah-to - Let's call the whole thing off !
我愛聽這首歌,尤其是由Louis Armstrong 及 Ella 的二重唱版本。它顯示出兩個戀愛中人的彼此競爭有多辛苦,也是關於互相妥協並尋求共通之處的歷程。
PHP可以用最少的功夫以及最多的樂趣來建立動態的網站,要建立動態網站我們需要使用資料庫來擷取登入帳號資訊、散佈動態新聞、儲存討論區的文章。就以使用最通用的MySQL資料來說,你們公司已經完成了如此神奇的工作,讓你們的網站比你們所能想像的還要出名。接著你們也發現MySQL無法應付實際的工作量了,是該更換資料庫系統的時候了。
不幸地,在PHP中所有資料庫的存取都有些微的不同。與MySQL連結你要使用 mysql_connect(),當你決定升級到Oracle或Microsoft SQL Server時,你必須分別改用ocilogon() 或 mssql_connect()。更糟糕的是不同連結所使用的參數也都不一樣,有的資料庫說po-tato(馬鈴薯的發音),別的資料庫又說pota-to(馬鈴薯的另一個發音),喔…..天啊。
我們不要放棄
當你需要確保你程式的可攜性的時候,一個叫做ADODB的資料庫封包程式庫已經出現了。它提供了共通的應用程式介面來跟所有支援的資料庫溝通,因此你無須放棄!
ADODB是Active Data Object DataBase的縮寫(很抱歉!玩電腦的有時候不是很有原創性)。ADODB目前支援MySQL、PostgreSQL、Oracle、Interbase、Microsoft SQL Server、Access、FoxPro、Sybase、ODBC及ADO,你可以從 http://php.weblogs.com/adodb下載 ADODB。
MySQL的例子
PHP中最通用的資料庫是MySQL,所以我想你會喜歡下面的程式碼,它連結到localhost的MySQL伺服器,資料庫名稱是mydab,並且執行一個SQL的select指令查詢,查詢結果會一列列地印出來。
$db = mysql_connect("localhost", "root", "password");
mysql_select_db("mydb",$db);
$result = mysql_query("SELECT * FROM employees",$db);
if ($result === false) die("failed");
while ($fields = mysql_fetch_row($result)) {
for ($i=0, $max=sizeof($fields); $i < $max; $i++) {
print $fields[$i].' ';
}
print "<br>n";
}
上列的程式碼用顏色標出分段,第一段是連結的部分,第二段是執行SQL指令,最後一段則是顯示欄位,while迴圈掃描結果的每一列,而for迴圈掃描到每列的欄位。
接下來是以ADODB的程式碼得到同樣的結果:
include("adodb.inc.php");
$db = NewADOConnection('mysql');
$db->Connect("localhost", "root", "password", "mydb");
$result = $db->Execute("SELECT * FROM employees");
if ($result === false) die("failed");
while (!$result->EOF) {
for ($i=0, $max=$result->FieldCount(); $i < $max; $i++)
print $result->fields[$i].' ';
$result->MoveNext();
print "<br>n";
}
現在改成指向Oracle資料庫,程式碼只要修改第二行成為 NewADOConnection('oracle'),讓我們看一下完整的程式碼...
與資料庫連結
include("adodb.inc.php"); $db = NewADOConnection('mysql'); $db->Connect("localhost", "root", "password", "mydb");
連結的程式碼比起原來MySQL的程式碼有老練一些,因為我們正是需要更老練些。在ADODB我們使用物件導向的方法來管理多樣資料庫的複雜性,我們用不同類別(class)來控制不同資料庫。假如你不熟悉物件導向程式設計,別擔心!所有的複雜事情都隱藏在NewADOConnection()函數之後。
為了節省記憶體,我們只載入與你所連結資料庫相關的PHP程式碼,我們透過呼叫NewADOConnection(databasedriver)來完成這件事,合法的資料庫驅動程式包含mysql,mssql,oracle,oci8,postgres,sybase,vfp,access,ibase以及許多其他的驅動程式。
接著我們透過呼叫NewADOConnection(),來從連結類別產生一個新的物件實體,最後我們使用$db->Connect()來連結資料庫。
執行SQL指令
$result = $db->Execute("SELECT * FROM employees");
if ($result === false) die("failed");
直接傳送SQL指令到伺服器,當成功執行之後,Execute()將傳回一個recordset物件,你可以如同上面所列來檢查$result。
一個初學者容易混淆的議題是,在ADODB有兩種類型的物件,連結物件以及recordset物件,我們何時用這些物件呢?
連結物件($db)是負責連結資料庫,格式化你的SQL查詢。而recordset物件($result)則是負責擷取結果並將回應資料規格化成文字或陣列。
唯一我需要增加的事情是,ADODB提供許多有用的函數來讓INSERT及UPDATE指令更容易些,這點我們在進階的章節會提到。
擷取資料
while (!$result->EOF) { for ($i=0, $max=$result->FieldCount(); $i < $max; $i++) print $result->fields[$i].' '; $result->MoveNext(); print "<br>n"; }
前面取得資料的範例很像從檔案讀資料,在每一行我們首先檢查是否到了檔案的結尾(EOF),若還沒到結尾,迴圈掃過每列中的欄位,然後移到下一行(MoveNext)接著重複同樣的事情。
$result->fields[]陣列是由PHP資料庫延伸系統所產生的,有些資料庫延伸系統並不會以欄位名稱建立該陣列的索引,要強迫以名稱排序索引該陣列,使用$ADODB_FETCH_MODE的通用變數。
$ADODB_FETCH_MODE = ADODB_FETCH_NUM; $rs1 = $db->Execute('select * from table'); $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; $rs2 = $db->Execute('select * from table'); print_r($rs1->fields); // shows array([0]=>'v0',[1] =>'v1') print_r($rs2->fields); // shows array(['col1']=>'v0',['col2'] =>'v1')
如同你所見的上面例子,兩個recordset儲存並使用不同的取用模式,當recordset由Execute()產生後再設定$ADODB_FETCH_MODE。
ADOConnection
連結到資料庫的物件,執行SQL指令並且有一組工具函數來標準格式化SQL指令,比如關聯與日期格式等指令。
其他有用的函數
$recordset->Move($pos)捲動目前的資料列,ADODB支援整個資料庫往前捲動,有一些資料庫並不支援往後的捲動,這倒不會是個問題,因為你能夠用暫存紀錄到快取來模擬往後捲動。
$recordset->RecordCount()傳回SQL指令存取到的紀錄筆數,有些資料庫會因為不支援而傳回-1。
$recordset->GetArray()以陣列的方式傳回結果。
rs2html($recordset)函數將傳進的recordset轉為HTML的表格格式。下例中以粗體字顯示相關用法:
include('adodb.inc.php'); include('tohtml.inc.php'); /* includes the rs2html function */ $conn = &ADONewConnection('mysql'); $conn->PConnect('localhost','userid','password','database'); $rs = $conn->Execute('select * from table'); rs2html($rs); /* recordset to html table */
還有許多其他有用的函數列示在文件之中,可從下列網址查得 http://php.weblogs.com/adodb_manual
進階題材
新增及更新
假設你要新增下列資料到資料庫中。
ID = 3
TheDate=mktime(0,0,0,8,31,2001) /* 31st August 2001 */
Note= sugar why don't we call it off
當你改用別的資料庫,可能就沒辦法新增資料。
第一個問題是,每一個資料庫各自有不同的內定日期格式,MySQL使用 YYYY-MM-DD 格式,而其他資料庫則有不同的內定格式,ADODB提供DBDate()函數來轉換不同資料庫之間的日期內定格式。
次一個問題是單引號(don't)的表示法,在MySQL可以直接使用單引號(don't),但在其他資料庫如Sybase、Access、 Microsoft SQL Server,則用兩個單引號表示(don''t),qstr()函數可以解決此問題。
我們如何使用這些函數?就像這樣:
$sql = "INSERT INTO table (id, thedate,note) values (" . $ID . ',' . $db->DBDate($TheDate) .',' . $db->qstr($Note).")"; $db->Execute($sql);
ADODB還有$connection->Affected_Rows()函數,傳回受最後update或delete指令影響的資料列數,及$recordset->Insert_ID()函數,傳回最後因insert指令而自動產生的資料列編號,預先提醒大家,沒有任何資料庫有提供這兩個函數。
MetaTypes
你可以得到關於欄位的更多資訊,透過recordset的方法FetchField($fieldoffset)傳回物件的3個屬性:name,type,max_length。
舉例說明:
$recordset = $conn->Execute("select adate from table"); $f0 = $recordset->FetchField(0);
結果$f0->name的內容是'adata',$f0->type將是'date',假如max_length不知道,其內容將會是-1。
處理不同資料庫的一個問題是,每一個資料庫對於相同的資料型態會有不同的稱呼,比如timestamp型態在某資料庫中稱為datetime,而另一個資料庫則稱為time,所以ADODB提供MetaType($type,$max_length)函數來標準化下列的資料型態:
C: character and varchar types
X: text or long character (eg. more than 255 bytes wide).
B: blob or binary image
D: date
T: timestamp
L: logical (boolean)
I: integer
N: numeric (float, double, money)
在前面的例子中,
$recordset = $conn->Execute("select adate from table");
$f0 = $recordset->FetchField(0);
$type = $recordset->MetaType($f0->type, $f0->max_length);
print $type; /* should print 'D'
*/
Select指令的Limit及Top支援
ADODB有個$connection->SelectLimit($sql,$nrows,$offset)函數讓你擷取recordset的部分集合,這是採用Microsoft產品中的SELECT TOP用法,及PostgreSQL與MySQL中的SELECT...LIMIT用法的優點,即使原來的資料庫並沒有提供此用法,本函數也模擬提供該使用方式。
快取支援
ADODB允許你在你的檔案系統中暫存recordset的資料,並且在$connection->CacheExecute($secs2cache,$sql)及 $connection->CacheSelectLimit($secs2cache,$sql,$nrows,$offset)等設定的時間間隔到達之後,才真正去做資料庫的查詢以節省時間。
PHP4 Session支援
ADODB也支援PHP4 session handler,你可以存放你的session變數在資料庫中,相關功能訊息請參考 http://php.weblogs.com/adodb-sessions
鼓勵商業使用
假如你計劃寫商用的PHP應用軟體來銷售,你也可以使用ADODB,我們依據GPL來出版ADODB,也就是說你可以合法地在商用應用軟體中引用,並保有你程式碼的所有權。強烈地鼓勵ADODB的商業應用,我們自己內部也正以這個理由如此使用中。
結論
為了感謝你看完這篇文章,下面就是let's call the whole thing off的完整歌詞。
Refrain You say eether and I say eyether, You say neether and I say nyther; Eether, eyether, neether, nyther - Let's call the whole thing off ! You like potato and I like po-tah-to, You like tomato and I like to-mah-to; Potato, po-tah-to, tomato, to-mah-to - Let's call the whole thing off ! But oh, if we call the whole thing off, then we must part. And oh, if we ever part, then that might break my heart. So, if you like pajamas and I like pa-jah-mas, I'll wear pajamas and give up pa-jah-mas. For we know we Need each other, so we Better call the calling off off. Let's call the whole thing off ! Second Refrain You say laughter and I say lawfter, You say after and I say awfter; Laughter, lawfter, after, awfter - Let's call the whole thing off ! You like vanilla and I like vanella, You, sa's'parilla and I sa's'parella; Vanilla, vanella, choc'late, strawb'ry - Let's call the whole thing off ! But oh, if we call the whole thing off, then we must part. And oh, if we ever part, then that might break my heart. So, if you go for oysters and I go for ersters, I'll order oysters and cancel the ersters. For we know we Need each other, so we Better call the calling off off. Let's call the whole thing off !
Song and lyrics by George and Ira Gershwin, introduced by Fred Astaire and Ginger Rogers in the film "Shall We Dance?"
中文翻譯 網際焦點