在2001年六月,我將我的站點的後端數據庫從mysql改為PostgreSQL。這僅僅花了我一天的時間。從那時起,Postgres一直工作得很正常,這證明我作出了正確的選擇。
在這篇文章的上半部分,我們講解了如何將數據從MySQL轉換到Postgres。我們還說明了我改用Postgres的理由。而在下半部分,我們將指導你完成另一半的轉換工作並講解如果將已編制好的用於MySQL的PHP代碼改用於Postgres。
准備
在你做這項工作之前,你需要一些准備工作。你的編程技巧和網站的復雜性將對程序的轉換影響很大。為了防止在工作中發生錯誤,在你的Web服務器上為你的站點作一份備份肯定是必要的。我當時的解決方法是設置了一個指定的子域來測試我正在修改的代碼。因為站點會根據一個配置文件中的一些基本配置自動處理URL,所以這件工作很簡單。你也許不能這樣做,但是不管怎樣,你都需要一個另外的空間來放置你正在改動的程序,這可以是一個子域、你的站點的一個子目錄、另一個Web主機也可以是一個本地的開發機器。
注意:如果你在另一台機器修改你的程序,你必須確保這個Web服務器的配置和你正在使用的站點一樣。如果你的Web主機只允許從本地連接Postgres,你也許將不得不將你的Postgres數據拷貝到開發機器上。要得到更多有關拷貝一個Postgres數據庫的信息,可以參看http://www.postgresql.org/上的pg_dump和pg_restore。
做好了拷貝並且經過測試它可以正常工作之後,你可以著手對程序進行解剖了。
PHP手術:代碼解剖
如何你之前沒有一個中心的配置文件,那麼首先建立它。這將減輕我們的很多工作量,而且也使得我們的改動可以即時對整個站點發生作用。這個配置文件也該是不允許其它人通過網站訪問的,否則這將是一個安全隱患。PHP的默認包含目錄是/usr/local/lib/php/。你需要在你的Web主機上建立這樣一個目錄並不允許通過網站訪問。你還得確保這對於使用同一Web主機的其他人也無法讀取這個目錄。如果你的站點和我的一樣,包含了一個標准的頭文件,你可以將你的Postgres設置文件放在那兒,這個配置文件將會是這樣的:</P>
<?php
// /usr/local/lib/php/mysite/configfile.php
$hostname = "localhost";
$username = "username";
$database = "mydb";
$passWord = "mypasswd";
?>
<?php
//標准的Html頭
include("mysite/configfile.php");
?>
<html>
<head>
<title>Bill's Kazoos</title>
...
</head>
<body>
...
有了這樣一個前面這樣的中心配置文件,改變數據庫系統將變得非常簡單。現在可以著手改程序了。
連接和查詢
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</CODE>參數,而且沒有一個內部的計數器。除此之外,它與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<$limit;$rownum++)
{
$value=pg_fetch_array($rslt, $rownum);
//完成處理工作
}
在上面的例子中,你可以注意到Postgres的代碼要稍微長一點,這是因為你必須指定行號。然而,如果你使用了你編寫的自己的計數函數,問題就變得很簡單了。這兒是一個添加了這樣一個函數的有用的文件。請注意在postg_query()中使用了三個全局變量。
<?php
// /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($hostna
<?php
// /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($hostna