数据库设计:多表与单表

Posted

技术标签:

【中文标题】数据库设计:多表与单表【英文标题】:Database Design: Multiple tables vs a single table 【发布时间】:2012-02-19 18:23:02 【问题描述】:

我正在创建一个网站,其中包含不同类型的项目,例如博客、帖子、文章等。用户可以将其中任何一个设置为他/她的最爱。现在当我接近这件事时,我有两个选择

    为每种类型的对象制作一个用户最喜欢的表格。 为所有用户的所有类型的对象创建一个公用表。

第一种结构的问题是我必须查询很多表来显示特定用户的收藏夹。但它可以让我轻松地将收藏夹分为不同的类别。

但是,如果我必须在一个页面上显示所有收藏夹并将它们全部合并,按时间排序,那将变得很困难。但是如果我使用第二种模式,我可以很容易地获得最新的收藏夹,并且根据对象类型对其进行分组并不难,但我将拥有一个全站点的大表。

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

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

如果有帮助,我正在使用mysql

【问题讨论】:

#2,如果该表被正确索引,则针对该大表的查询性能应该类似于#1。 【参考方案1】:

所以如果我没记错的话,您是在尝试创建一个Favorites 表来收集用户最喜欢的项目,对吧?如果是这样,您至少需要两张表。

Types:资源的类型。

+----+---------+
| 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 |
+--------+----------+--------------+

帖子:示例帖子表,您可能还有BlogsPhotosAlbums 表。

+-----+------------------+
| 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!    |
+-----+------------------+

【讨论】:

【参考方案2】:

您似乎已经知道答案,但请记住,保持您设计的系统易于修改,因为业务模型总是会随着时间的推移而变化,否则它们最终会失败(这是一个概括,但您明白了)。一个推论是,如果你制作一个僵化的模型,快或慢,它是僵化的,改变会更难,最终用户不会看到差异,因此不会实现金钱/幸福的改变,除非它是一个非常糟糕的改变。 您的问题在查询在引擎上工作的方式上不是技术性的,而是更多的哲学问题,简单的更改与明显的速度。 问问自己,拥有标准化数据库的优势是什么?考虑一个干净的架构和设计,性能是当今世界上最少的问题,因为处理更便宜,存储也更便宜。但是设计很昂贵。 进行规范化是为了使系统不依赖于最后一刻的决策,而是依赖于结构化的设计过程。 大表对 MySql 来说没什么大不了的,但它们对于维护、修改和扩展来说是一件大事。这不仅仅是增加一列,而是关于数据本身的刚性结构。最终,您将只添加包含索引的列,这些索引将指向小表。无论如何,MySql 将围绕所有这些数据进行探索。 所以我会选择第一个,很多小桌子,多对多。

【讨论】:

【参考方案3】:

我的网站上有这个设计。我的模块是:新闻、文章、视频、照片、下载、评论、测验、民意调查等。所有这些都在单独的表格中。我有一个喜欢表,用户可以在其中喜欢或不喜欢帖子(在您的情况下为收藏夹)。获取这些的查询并不复杂。

首先,我的模块表的大部分结构都是相同的:

身份证 标题 内容 user_id(作者) 日期 等

除了一些例外情况,有时标题称为问题或没有内容列。这不会导致任何问题。

我的点赞表是这样设置的:

身份证 page_id module_id(它来自什么表...我有一个模块表,其中每个模块都有一个标题、关联的 id、目录等) post_id(对应模块表id) user_id(点赞或发帖的用户) 状态(0 = 喜欢,1 = 不喜欢) 日期(喜欢/不喜欢发生的时间)

模块表示例:

身份证 标题 目录 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 或标题。

【讨论】:

非常感谢您提供的详尽答案。我也有一个和你很相似的结构,所以把它放在同一张桌子上并不难​​。此外,我正在Django 具有通用外键的 python 框架中工作。这允许我将不同的对象引用保存在同一个表中。但是我的问题不是可操作的,更多的是基于性能。当用户数量增加并且这些表开始填满时会发生什么?

以上是关于数据库设计:多表与单表的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 多表where查询与单表select where in哪一个效率高

DMLDQL与单表多表的增删改查(20190215下午)

MySQL 数据库设计 - 存储图像 - 单表或多表

DynamoDb 表设计:单表或多表

数据库设计表与表之间的关系详细介绍

MySQL数据迁移工具的设计与实现