我们需要保存一些关于发表到论坛中的文章的属性,其中包括:文章的作者(称作poster)、文章的标题、发表时间和文章正文。因此我们需要一个文章表,还必须为每一篇文章创建一个唯一的ID,称作postid。
每篇文章需要一些关于它在继承关系中所属位置的信息。我们可以将关于文章的子文章的位置随该文章一起保存起来。然而,每篇文章都会有许多回复,因此这可能会在数据库的结构上导致某些问题。由于每篇文章只能是一篇文章的回复,因此保存父文章的引用可能会更容易些,父文章即当前文章回复的那篇文章。
这样我们需要为每篇文章保存以下数据:
■postid:每篇文章的唯一的ID
■parent:父文章的postid
■poster:该文章的作者
■title:该文章的标题
■posted:该文章发表的时间和日期
■message:该文章的正文
当然,我们还需要对以上数据进行一些优化。
当需要判断一篇文章是否有回复时,必须运行一个SQL查询来确定是否有任何的文章将该文章作为其父文章。我们需要为每一篇所列出文章生成这些信息。需要运行的查询语句越少,代码运行的速度就越快。我们可以通过增加一个表示是否有任何的回复的字段来去除这种查询的必要。我们把这个字段叫做children并将它设为查询效率较高的Boolean类型——如果节点有"children",则为1,否则为0。
当然,优化总是要付出代价的。这里,我们选择了存储冗余数据。由于我们以两种方式保存这些数据,必须保证两种表示方法互相匹配。当增加子节点时,必须更新其父节点。如果删除子节点,需要更新父节点确保数据库的一致性。在该项目中,我们并不打算添加删除文章的功能,因此可以避免一半的问题。如果决定扩展该代码,请记住这个问题。
在这个项目中,我们还需要完成一些其他优化。我们将消息体与其他数据分离,并且将这些消息体保存在一个单独的表格中。这样做可以使消息体具有MySQL的text属性。在一个表中使用这种类型的数据将降低该表的查询速度。由于我们要完成许多小查询来建立树形结构,因此这会使该系统的速度下降很多。将消息正文放在单独的表中,我们可以在用户需要查看某个特定消息时,将它从数据库中取出。
MySQL查询固定大小记录的速度比查询可变大小记录的速度要快。如果想使用可变大小的数据,可以通过对用来检索数据库的字段上创建索引来实现。当然对于某些项目来说,将文本字段与其他字段一样放在相同的记录中,并为所有要基于这些字段的检索指定索引可能也会好些。创建索引要花费时间,而且论坛数据可能经常会发生变化,因此我们必须经常重新建立索引。
我们还增加一个area属性以备以后决定在一个程序中实现多个论坛。在这里,我们不实现这个功能,但是该方法可以保留为将来使用。
基于以上所有这些考虑,创建论坛数据库的SQL脚本如程序清单31-1所示。
程序清单31-1 create_database.sql——创建论坛数据库的SQL脚本
create database discussion;
use discussion;
create table header
(
parent int not null,
poster char(20)not null,
title char(20)not null,
children int default 0 not null,
area int default 1 not null,
posted datetime not null,
postid int unsigned not null auto_increment primary key
);
create table body
(
postid int unsigned not null primary key,
message text
);
grant select,insert,update,delete
on discussion.*
to [email protected] identified by'password';
可以在MySQL中运行如下所示的脚本来创建这个数据库结构:
mysql-u root-p<create_database.sql
需要提供root用户密码。此外,还应该更改我们为论坛用户设置的密码。
为了更好地理解该结构如何保持文章及它们相互之间存在的关系,请参阅图31-3。
图 31-3 以一个平面关系表格方式保存的树形结构数据可以看到,数据库中每篇文章的父字段保存了树形结构中它上面的文章的postid。父文章是将被回复的文章。
我们还可以看到,根节点postid 1没有父节点,讨论组所有新主题都在这个位置。对于这种类型的文章,我们在数据中将它们的父节点保存为0。