热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何将网站从MySQL转移到PostgreSQL数据库

在这篇文章的上半部分,我们讲解了如何将数据从MySQL转换到Postgres。我们还说明了我改用Postgres的理由。而在下半部分,我们将指导你完成另一半的转换工作并讲解如果将已编制好的用于MySQL的PHP代码改用于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设置文件放在那儿,这个配置文件将会是这样的:


// /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<$limit;$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的资源。


推荐阅读
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • Docker的安全基准
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • 本文详细介绍了如何通过多种编程语言(如PHP、JSP)实现网站与MySQL数据库的连接,包括创建数据库、表的基本操作,以及数据的读取和写入方法。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
  • PHP 编程疑难解析与知识点汇总
    本文详细解答了 PHP 编程中的常见问题,并提供了丰富的代码示例和解决方案,帮助开发者更好地理解和应用 PHP 知识。 ... [详细]
  • 深入理解OAuth认证机制
    本文介绍了OAuth认证协议的核心概念及其工作原理。OAuth是一种开放标准,旨在为第三方应用提供安全的用户资源访问授权,同时确保用户的账户信息(如用户名和密码)不会暴露给第三方。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • MySQL中枚举类型的所有可能值获取方法
    本文介绍了一种在MySQL数据库中查询枚举(ENUM)类型字段所有可能取值的方法,帮助开发者更好地理解和利用这一数据类型。 ... [详细]
author-avatar
air12345
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有