將你的網站從MySQL改為PostgreSQL(下)(轉)

ba發表於2007-08-12
將你的網站從MySQL改為PostgreSQL(下)(轉)[@more@]在這篇文章的上半部分,我們講解了如何將資料從MySQL轉換到Postgres。我們還說明了我改用Postgres的理由。而在下半部分,我們將指導你完成另一半的轉換工作並講解如果將已編制好的用於MySQL的PHP程式碼改用於Postgres。

準備

在你做這項工作之前,你需要一些準備工作。你的程式設計技巧和網站的複雜性將對程式的轉換影響很大。為了防止在工作中發生錯誤,在你的Web伺服器上為你的站點作一份備份肯定是必要的。我當時的解決方法是設定了一個指定的子域來測試我正在修改的程式碼。因為站點會根據一個配置檔案中的一些基本配置自動處理URL,所以這件工作很簡單。你也許不能這樣做,但是不管怎樣,你都需要一個另外的空間來放置你正在改動的程式,這可以是一個子域、你的站點的一個子目錄、另一個Web主機也可以是一個本地的開發機器。

注意:如果你在另一臺機器修改你的程式,你必須確保這個Web伺服器的配置和你正在使用的站點一樣。如果你的Web主機只允許從本地連線Postgres,你也許將不得不將你的Postgres資料複製到開發機器上。要得到更多有關複製一個Postgres資料庫的資訊,可以參看上的pg_dump和pg_restore。
做好了複製並且經過測試它可以正常工作之後,你可以著手對程式進行解剖了。

PHP手術:程式碼解剖

如何你之前沒有一箇中心的配置檔案,那麼首先建立它。這將減輕我們的很多工作量,而且也使得我們的改動可以即時對整個站點發生作用。這個配置檔案也該是不允許其它人透過網站訪問的,否則這將是一個安全隱患。PHP的預設包含目錄是/usr/local/lib/php/。你需要在你的Web主機上建立這樣一個目錄並不允許透過網站訪問。你還得確保這對於使用同一Web主機的其他人也無法讀取這個目錄。如果你的站點和我的一樣,包含了一個標準的標頭檔案,你可以將你的Postgres設定檔案放在那兒,這個配置檔案將會是這樣的:


// /usr/local/lib/php/mysite/configfile.php
$hostname = "localhost";
$username = "username";
$database = "mydb";
$password = "mypasswd";
?>
//標準的HTML頭
include("mysite/configfile.php");
?>



...


...
有了這樣一個前面這樣的中心配置檔案,改變資料庫系統將變得非常簡單。現在可以著手改程式了。

連線和查詢

PHP有關MySQL的函式和Postgres很相似,所以轉換程式碼的工作並不是太複雜。事實上,你可以自己寫一些函式來完成這種轉換。在做這項工作之前,讓我們來看看兩者的不同:
要連線到MySQL資料庫需要用到兩個命令:
$connection_id=mysql_connect($hostname, $username, $password);
mysql_select_db($database, $connection_id);
如果你使用一個持久的連線:
$connection_id=mysql_pconnect($hostname, $username, $password);
mysql_select_db($database, $connection_id);
然而,PHP連線Postgres的函式只需要一個字串引數,與MySQL函式不同,這是一個複合的字串引數。Postgres函式也需要你指定使用的資料庫。下面是一個示例:
$connection_id=pg_connect("host=$hostname dbname=$database user=$username
password=$password");
一個持久的連線執行同樣的工作,只是需要呼叫pg_pconnect()函式。
PHP的MySQL和Postgres的查詢函式同樣有點不同。MySQL的查詢函式是$result_data = mysql_query("query goes here",$connection_id);,而Postgres的查詢函式是這樣的:$result_data = pg_exec($connection_id, "query goes here")。
正如你所看到的,PHP對MySQL和Postgres和連線和查詢的支援區別並不大,但是函式引數的不同還是需要我們慢慢處理。要提高速度,你可以寫一些函式使得Postgres可以使用和MySQL一樣的函式來連線。如果你有了包含這樣一種函式的中心庫,你可以將這些函式也放在那裡。你也可以將它們放置在我們前面所提到的配置檔案中,因為它會自動地被每個頁面包含。
//連線到資料庫
function postg_connect($hostname, $username, $password, $database)
{
return pg_connect("host=$hostname dbname=$database
user=$username password=$password");
}
//如果你僅僅使用一個資料庫,你最好將這些變數放到你的配置檔案中
function postg_autoconnect()
{
global $hostname, $username $password $database;
return pg_connect("host=$hostname dbname=$database
user=$username password=$password");
}
//查詢函式
function postg_query($query, $connection_id)
{
return pg_exec($connection_id, $query);
}
不管你是否使用這種函式,程式碼轉換的工作總是相當簡單的。Postgres幾乎可以支援所有的以前在MySQL下使用的SQL查詢,但是你可能還是要整理一下你的查詢。因為在不同的地方資料模型和程式碼會有一些不同,我在這裡不想詳細解釋這個問題。然而,對SQL的轉換並不困難。首先轉換程式碼,然後看看有哪些查詢無法在Postgres中正常執行。對MySQL語言指南和PostgreSQL使用者嚮導中的相關問題進行比較,你也許不能在Postgres中找到所有與MySQL同等的功能,但是Postgres支援所有的通用的功能。
現在你已經把連線和查詢的程式碼改好了,下面的問題可能要稍微複雜一點。PHP中MySQL和Postgres對結果集處理的不同可能需要你對程式碼作更多的變動。

讓我們來看看對結果的處理

PHP的Postgres對結果的處理並不完全和MySQL一一對應;它們有一些微小的不同。這些微小的差別可能只需要對程式碼作微小的改動,但是也可能是一個挺複雜的問題。
首先,讓我們看看MySQL和Postgres有哪些相似的地方。下面這個列表介紹了普通的MySQL結果處理函式和它們相對應的Postgres函式:
MySQL
mysql_num_rows($result) 返回結果集的行數,這僅對SELECT語句有效
mysql_affected_rows($result) 返回在一個INSERT、UPDATE或DELETE查詢中受到影響的行數
mysql_fetch_object($result) 取得一行的資料並將其作為一個物件返回。欄位名對應於類的屬性名。(即$field1 = $var->field1;)這個函式儲存了一個內部變數以保證每次呼叫時可以返回下一行。
mysql_fetch_row($result) 這個函式以一個陣列的形式返回結果集的一行。這個值可以透過一個從0開始的陣列值獲得。(即$field1 = $var[0];)。同樣,這個函式儲存了一個內部的計數器以保證每次呼叫時可以返回下一行。
mysql_fetch_array($result) 這個函式和另外兩個fetch函式基本相同,只是它以一個聯合陣列的形式返回一個行($field1 = $var["field1"];)。
Postgres
pg_numrows($result) 與對應的mysql_num_rows($result)完全一樣
pg_cmdtuples($result) 與對應的mysql_affected_rows($result)完全一樣
pg_fetch_object($result, $row) 獲得結果集中的指定行。必須使用$row引數,而且沒有一個內部的計數器。除此之外,它與mysql_fetch_object($result)完全相同。
pg_fetch_row($result, $row) 以一個陣列的形式返回結果集中的指定行。同樣必須使用$row引數,而且沒有一個內部的計數器。
pg_fetch_array($result, $row) 與對應的mysql_fetch_array($result)基本一樣,只是需要指定行,並且缺少一個內部的計數器。
有關這些函式的更詳細的資訊,請參看PHP.Net上的PHP文件。
PHP對MySQL和Postgres支援的最本質的不同在於對結果集的閱讀。MySQL自動決定獲取哪一行,而Postgres必須指定要閱讀哪一行。下面是一些例子,你也可能會遇到這些問題,對於它們有兩個解決方案。
//第一個普通的例子:
$rslt=mysql_query("SELECT * FROM blah", $connection_id);
while($value=mysql_fetch_array($rslt))
{
//完成資料處理工作
}
//對於Postgres,這樣的程式碼無法執行,因為他們需要指定行號
//程式碼將作如下改動(如果你沒有使用前面討論的函式):
$rslt=pg_exec($connection_id, "SELECT * from blah");
$limit=pg_numrows($rslt);
for($rownum=0;$rownum{
$value=pg_fetch_array($rslt, $rownum);
//完成處理工作
}
在上面的例子中,你可以注意到Postgres的程式碼要稍微長一點,這是因為你必須指定行號。然而,如果你使用了你編寫的自己的計數函式,問題就變得很簡單了。這兒是一個新增了這樣一個函式的有用的檔案。請注意在postg_query()中使用了三個全域性變數。
// /usr/local/lib/php/mysite/configfile.php
$hostname = "localhost";
$username = "username";
$database = "mydb";
$password = "mypasswd";
//內部計數變數
$fetch_array_counter=0;
$fetch_object_counter=0;
$fetch_row_counter=0;
//處理連線到PostgreSQL資料庫的函式
function postg_connect($hostname, $username, $password, $database)
{
return pg_connect(host=$hostname, dbname=$database user=$username,
password=$password");
}
//不需要任何引數的連線
function postg_autoconnect()
{
global $hostname, $username, $password, $database;
return pg_connect(host=$hostname, dbname=$database user=$username,
password=$password");
}
//查詢函式
function postg_query($query, $connection_id)
{
//將全域性變數設定為0
global $fetch_array_counter, $fetch_row_counter, $fetch_object_counter;
$fetch_array_counter=$fetch_row_counter=$fetch_object_counter=0;
return pg_exec($connection_id, $query);
}
//pg_fetch_array()置換
function postg_fetch_array($rslt)
{
global $fetch_array_counter;
$fetch_array_counter++;
//計數器加一
return pg_fetch_array($rslt, $fetch_array_counter);
}
//pg_fetch_row()置換
function postg_fetch_row($rslt)
{
global $fetch_row_counter;
$fetch_row_counter++;
//計數器加一
return pg_fetch_row($rslt, $fetch_row_counter);
}
//pg_fetch_object()置換
function postg_fetch_object($rslt)
{
global $fetch_object_counter;
$fetch_object_counter++;
//計數器加一
return pg_fetch_object($rslt, $fetch_object_counter);
}
?>
當然,如果你在同一個迴圈中同時對兩個結果集進行操作,上面的函式將無法正常地工作,因為它們只使用了一個內部的計數器。如果因為某種原因,你需要同時閱讀幾個結果集,你將不得不使用傳統的Postgres方法。
另一個你可能遇到的問題是在Postgres中沒有與MySQL中mysql_insert_id()相應的函式,這個函式反映最後的INSERT查詢的索引值。PHP文件往往會讓讀者誤以為pg_getlastoid()會完成這項工作,但是實際情況並不是這樣。缺少這一樣一個函式並不一個無法逾越的障礙,你可以利用Postgres的SEQUENCE系統來實現這樣的功能。
幸運的是,要獲得最後的ID是相當容易的。你可以透過SQL獲得Sequence資訊,因此你可以用這個語句來實現mysql_insert_id()的功能:
function postg_insert_id($tablename, $fieldname)
{
global connection_id;
$result=pg_exec($connection_id, "SELECT last_value FROM ${tablename}_
${fieldname}_seq");
$seq_array=pg_fetch_row($result, 0);
return $seq_array[0];
}
因為Postgres使用了一個特別的命名系統來命名序列,我上面建立的這個函式需要指定表名和欄位名。呼叫這個函式,會返回你的表中的任意SERIAL欄位的最後一個序列值,即使在表中有不止一個這樣的欄位。
經過上面的這些處理後,你已經可以在你的MySQL站點上成功地執行PostgreSQL了。然而,這僅僅是第一步;如果你想了解更多,繼續看下去,你會看到一些有用的PostgreSQL的資源。

更進一步的資源

從PostgreSQL非FAQ文件站點你可以看到最初的和最重要的PostgreSQL資源。這個有價值的資源可以向你提供大量的書籍、參考、技術參考甚至於具體的工作。它同樣會涉及將後端資料庫從MySQL 改為Postgres,此外對於使用PostgreSQL的其它問題它也可以給你幫助。
另一個有價值的PostgreSQL提供的資源是PostgreSQL互動文件。其中涉及到使用PostgreSQL的很多問題。
Xach Beane,因為其在The GIMP(一種圖形處理軟體)上的工作而著名,他也編寫了關於將MySQL dump轉換為Postgres dump的書寫一個指令碼。他的程式可以更為全面地處理這些問題。不過,對這些問題的處理並不是十分完美的,因此你得小心地使用它。
Dobrica Pavlinusic也編寫了一個程式以處理從MySQL到Postgres的轉換。同樣得提醒你這個程式的處理仍然不是十分完美的,因此還是少不了象我們上面所討論的手工的修改。
從這兒你可以找到一個非常完全的PostgreSQL&PHP指南。它會從安裝開始介紹PostgreSQL的使用。這個指南非常值得初學者閱讀。
Bruce Momjohan編寫了一本關於PostgreSQL的名為PostgreSQL: Introduction and Concepts的書,已由Addison Wesley出版。你甚至還可以線上閱讀!
最後,OpenDocs也出版了實用PostgreSQL。這本書在2001年十月份出版,你可以從the OpenDocs Linuxports.com站點閱讀。
將你的站點的後端資料庫從MySQL改為PostgreSQL是一個明智的選擇。轉換工作肯定會耗費時間和精力,但是經過這些努力後,你的站點可以擁有一個更完美的資料庫系統。就象我一樣,你也會覺得這項工作是有意義的!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617731/viewspace-950681/,如需轉載,請註明出處,否則將追究法律責任。

相關文章