数据库设计:多个表 vs 单个表

26

我正在制作一个网站,其中有不同类型的项目,例如博客、帖子、文章等。用户可以将其中任何一种设置为他/她的收藏夹。现在当我处理这件事时,我有两个选择:

  1. 为每种对象类型创建用户收藏夹表。
  2. 为所有用户的所有类型的对象创建公共收藏夹表。

第一种结构的问题在于我必须查询很多表才能显示特定用户的收藏夹。但它将使我可以轻松地将收藏夹分成不同的类别。

然而,如果我必须显示所有收藏夹在一个页面上并将它们全部合并,在时间排序后,那就变得困难了。但如果我使用第二个模型,我可以轻松获取最新的收藏夹,并且按对象类型进行分组也不难,但是我将拥有一个站点范围内的大型表格。

这两种策略中哪种更具可扩展性。

第一种需要多次数据库查询,而第二种则需要一个大的单表。

如果有帮助的话,我使用MySql


如果表正确地建立索引,那么针对该大表的查询性能应与#1相似。 - Ben English
3个回答

14
看起来你已经知道答案了,但要记住,设计的系统应该简单易修改,因为商业模式随着时间变化或最终失败(这是一个概括,但你明白我的意思)。其推论是,如果你制定了一个刚性的模型,无论快慢,它都是刚性的,更改将更加困难,最终用户也看不出任何区别,因此没有实现任何金钱/幸福的变化,除非它是一个非常糟糕的变化。 你的问题不是技术问题,而是一种哲学问题,即易于更改与表面速度之间的优势。 问问自己,拥有规范化的数据库的优点是什么?考虑到清晰的架构和设计,性能在当今世界已经不再是最大的问题,因为处理价格便宜,存储也便宜。但是设计是昂贵的。 规范化的目的是制造不依赖于最后时刻决策而依赖于结构化设计过程的系统。 对于MySql来说,大表并不是大问题,但是它们对于维护、修改和扩展却是大问题。这不仅仅是添加一个列,而是数据本身的刚性结构问题。最终,你只会添加包含索引的列,而这些索引将指向小表。MySql仍将在所有这些数据周围开辟自己的道路。 所以我会选择第一个,许多小表,多对多。

5
我在我的网站上有这个设计。我的模块包括:新闻、文章、视频、照片、下载、评论、测验、投票等等,每个模块都在单独的表格中。我有一个喜欢的表格,用户可以喜欢或不喜欢一篇文章(在你的情况下是收藏)。获取这些内容的查询并不复杂。
首先,大多数模块的表格结构基本相同:
- id - title - content - user_id(作者) - date - 等等
有一些例外,比如有时标题被称为问题,或者没有内容列。但这并不会引起任何问题。
我的喜欢表格设置如下:
- id - page_id - module_id(它来自哪个表格...我有一个模块表格,每个模块都有一个标题、关联id、目录等) - post_id(对应于模块表格的id) - user_id(进行喜欢或发布的用户) - status(0=喜欢,1=不喜欢) - date(喜欢/不喜欢发生的日期)
模块表格示例:
- id - title - directory - post_type 示例
id      title              directory         post_type
 1       News                news               news
 2     Episode Guide       episodes            episode
 3       Albums           discography/albums    album

基本上,您的设置将类似,根据您的需求修改表结构。

查询特定用户所有点赞或收藏的内容:

$getlikes = mysql_query("SELECT DISTINCT post_id, module_id, page_id FROM likes WHERE user_id = $profile_id ORDER BY id DESC LIMIT $offset, $likes_limit", $conn);
$likes = mysql_num_rows($getlikes);

if($likes == "0"){
echo "<br><Center>$profile_username does not have any liked posts at this time.</center><BR>";
}
else {
echo "<table width='100%' cellspacing='0' cellpadding='5'>

<Tr><th>Post</th><th align='center'>Module</th><th align='center'>Page</th><tr>";

while ($rowlikes = mysql_fetch_assoc($getlikes)) {
   // echo data

$like_page_id = $rowlikes['page_id'];
$like_module_id = $rowlikes['module_id'];
$like_post_id = $rowlikes['post_id'];


// different modules have different fields for the "title", most are called title but quotes is called "content" and polls is called "questions"
if($like_module_id == "11"){
$field = "question";
}
elseif($like_module_id == "19"){
$field = "content";
}
else{
$field = "title";
}





// FUNCTIONS
PostURL($like_page_id, $like_module_id, $like_post_id);
ModTitle($like_module_id);
ModTable($like_module_id);
ModURL($like_page_id, $like_module_id);
fpgURL($like_page_id);


$getpostinfo = mysql_query("SELECT $field AS field FROM $mod_table WHERE id = $like_post_id", $conn);
$rowpostinfo = mysql_fetch_assoc($getpostinfo);
$like_post_title = $rowpostinfo['field'];

// Using my "tiny" function to shorten the title if the module is "Quotes"
if($like_module_id == "19"){
Tiny($like_post_title, "75");
$like_post_title = "\"$tiny\"";
}


if(!$like_post_title){
$like_post_title = "<i>Unknown</i>";
}
else {
$like_post_title = "<a href='$post_url'>$like_post_title</a>";
}

echo "<tr class='$altrow'>
<td>$like_post_title</td>
<td align='center'><a href='$mod_url'>$mod_title</a></td>
<td align='center'>$fpg_url</td>


</tr>";

$altrow = ($altrow == 'altrow')?'':'altrow';

} // end while

echo "<tr><Td align='center' colspan='3'>";

// FUNCTIONS - Pagination links
PaginationLinks("$cs_url/users/$profile_id", "likes");

echo "</td></tr></table>";

} // end else if no likes

可能对你来说有些难以理解,因为我有很多自己的变量,但基本上它从likes表中获取模块id和帖子id,然后运行查询以获取帖子的标题和其他任何我想要的信息,例如原始作者。

我设置了“模块”功能,如果你提供其id,它将返回模块的URL或标题。


非常感谢您提供的详尽答案。我也有一个与您相似的结构,因此将其保留在同一张表中并不困难。此外,我正在使用Python的Django框架,它具有通用外键。这使我能够在同一张表中保留不同的对象引用。但我的问题不是操作性的,而是基于性能的。当用户数量增加并且这些表开始填充时会发生什么? - Sachin

4

如果我没记错的话,您正在尝试创建一个“收藏夹”表格来收集用户喜欢的项目,对吗? 如果是这样,您将需要至少两个表格。

类型:资源的类型。

+----+---------+
| ID |  Name   |
+----+---------+
|  0 | blog    |
|  1 | post    |
|  2 | article |
|  3 | photo   |
|  4 | video   |
+----+---------+

收藏夹:收藏夹系统最重要的部分,类似于一个关系地图。

+--------+----------+--------------+
| UserID | TargetID | TargetTypeID |
+--------+----------+--------------+
|    941 |        1 |            0 |
|      6 |      935 |            1 |
|     26 |       51 |            4 |
|      7 |       87 |            2 |
+--------+----------+--------------+

文章 (Posts):这是一个示例文章表格,你可能还有博客 (Blogs)照片 (Photos)相册 (Albums)表格。

+-----+------------------+
| ID  |      Title       |
+-----+------------------+
|   0 | This is my post! |
|  51 | Oh, how are you? |
| 935 | Hello, world!    |
+-----+------------------+

现在,SQL查询可能像这样(未经测试):

-- Get the posts
SELECT p.*
FROM Posts p
LEFT JOIN Favorites f 
-- Which are favorited by the user 6
ON f.UserID = 6 
-- Also get the type id of the `post`,
-- so we can specify the favorite type of the favorite items
AND f.TargetTypeID = (
    SELECT ID 
    FROM Types
    WHERE Name = 'post'
)
-- Make sure we only get the posts which are favorited by the user.
WHERE p.ID = f.TargetID

通过上面的SQL查询语句,您可以获取被用户ID 6收藏的喜爱的帖子。
+-----+------------------+
| ID  |      Title       |
+-----+------------------+
| 935 | Hello, world!    |
+-----+------------------+

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接