数据库中有一个大表还是许多小表?

时间:2015-04-29 01:56:09

标签: database postgresql database-design

假设我想使用像postgresql这样的db创建一个典型的todo-webApp。用户应该能够创建待办事项列表。在这个列表中,他应该能够制作实际的待办事项。

我认为todo-list是一个具有不同属性的对象,如所有者,名称等,当然还有实际的todo-entries,它们有自己的属性,如内容,优先级,日期.......

我的想法是为所有用户的所有待办事项列表创建一个表。在此表中,我将存储每个列表的所有属性。但出现的问题是如何存储todo条目本身?当然在附加表中,但我应该宁愿:

1。为所有条目创建一个大表,并有一个字段存储它们所属的待办事项列表的id,如下所示:

todo-list: id, owner, ...
todo-entries: list.id, content, ...

总共会得到2个表。 todo-entries表可能变得非常大。虽然我们知道条目到期,但是表格只会随着使用量的增加而增长但不会随着时间的推移而增长。然后我们会编写类似SELECT * FROM todo-entries WHERE todo-list-id=id的内容,其中id是我们要检索的列表。

OR

2。基于每个用户创建一个todo-entries表

todo-list: id, owner, ...
todo-entries-owner: list.id, content,. ..

条目数表取决于系统中的用户数。像SELECT * FROM todo-entries-owner这样的东西。中型表格取决于用户总共输入的条目数。

OR

第3。为每个 todo-list 创建一个todo-entries-table ,然后将生成的表名存储在表的字段中。例如,我们可以在表名中使用todos-list唯一ID,如:

todo-list: id, owner, entries-list-name, ...    
todo-entries-id: content, ... //the id part is the id from the todo-list id field. 

在第三种情况下,我们可能会有相当多的表。用户可能会创建许多“短”&#39;待办事项名单。要检索列表,我们只需沿着SELECT * FROM todo-entries-id行,其中todo-entries-id应该是待办事项列表中的字段,或者可以通过连接&todo-entries&#39隐式完成;使用todos-list唯一ID。顺便说一句:如何在js中完成,或者可以直接在PostgreSQL中完成?与此非常相关:在SELECT * FROM <tablename>语句中,是否可以将某些其他表的某些字段的值设为<tablename>?像SELECT * FROM todo-list(id).entries-list-name左右那样。

这三种可能性从几个大桌到几个小桌。我个人的感觉是第二种或第三种解决方案更好。我认为他们可能会更好地扩展。但我不确定这一点,我想知道什么是典型的&#39;方法是。

我可以更深入地了解我对每种方法的看法,但要谈到我的问题:

  • 我应该选择三种可能性中的哪一种? (或其他任何东西,这与标准化有关吗?)

跟进:

  • 那么(PostgreSQL)语句会是什么样的?

1 个答案:

答案 0 :(得分:5)

唯一可行的选择是第一个。它管理起来要容易得多,并且很可能比其他选项更快。

图片您有100万用户,平均每个有3个待办事项列表,每个列表平均有5个条目。

场景1

在第一个场景中,您有三个表:

  • Imports System.Devices Imports System.Devices.RemoteDeviceManager Public Class FormHome Dim PdtRemoteDevice As RemoteDevice Dim Devicemanager As New RemoteDeviceManager Private Sub ButtonHomeCopyToDevice_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonHomeCopyToDevice.Click PdtRemoteDevice = Devicemanager.Devices.FirstConnectedDevice If PdtRemoteDevice Is Nothing Then MessageBox.Show("No Device Connected, Please Try Again") Exit Sub Else Dim Th As Thread Th = New Thread(AddressOf UploadDatabase) Th.IsBackground = True Th.Start() End If End Sub Private Sub UploadDatabase() Using PdtRemoteDevice = Devicemanager.Devices.FirstConnectedDevice If Not (PdtRemoteDevice Is Nothing) Then RemoteFile.CopyFileToDevice(PdtRemoteDevice, "C:\sample.txt", "\test\sample.txt", True) End If End Using End Sub End Class :100万条记录
  • todo_users:300万条记录
  • todo_lists:1500万条记录

这样的表大小对于PostgreSQL没有问题,并且使用正确的索引,您将能够在不到一秒的时间内检索任何数据(这意味着只是简单的查询;如果您的查询变得更复杂(例如:让我获取todo_entries) todo_users的前15%的最长todo_list在3个月内输入的todo_lists少于3个todo_lists,输入的todo_entries最高)它显然会更慢(如在其他场景中)。查询非常简单:

todo_entries

您还可以将三个查询合并为一个:

-- Find user data based on username entered in the web site
-- An index on 'username' is essential here
SELECT * FROM todo_users WHERE username = ?;

-- Find to-do lists from a user whose userid has been retrieved with previous query
SELECT * FROM todo_lists WHERE userid = ?;

-- Find entries for a to-do list based on its todoid
SELECT * FROM todo_entries WHERE listid = ?;

使用SELECT u.*, l.*, e.* -- or select appropriate columns from the three tables FROM todo_users u LEFT JOIN todo_lists l ON l.userid = u.id LEFT JOIN todo_entries e ON e.listid = l.id WHERE u.username = ?; 表示您还将获得没有列表或没有条目的列表的用户数据(但列值将为LEFT JOIN)。

插入,更新和删除记录可以使用非常相似的语句完成,同样快速。

PostgreSQL将数据存储在“页面”上(通常大小为4kB),并且大多数页面都会被填充,这是一件好事,因为与其他操作相比,阅读写入页面非常慢。 / p>

场景2

在这种情况下,每个用户只需要两个表(NULLtodo_lists),但您需要一些机制来识别要查询的表。

  • 100万todo_entries个表,每个表都有几个记录
  • 100万todo_lists个表,每个表有几十个记录

唯一可行的解​​决方案是从与用户名相关的“basename”或您网站上的一些其他持久身份验证数据构建完整的表名。所以像这样:

todo_entries

然后用这些表名查询。您最有可能需要一个username = 'Jerry'; todo_list = username + '_lists'; todo_entries = username + '_entries'; 表来存储您的100万用户的个人数据,用户名和密码。

在大多数情况下,表格非常小​​,PostgreSQL不会使用任何索引(也不必使用)。但是,在查找适当的表时会遇到更多麻烦,并且您很可能会在代码中构建查询,然后将它们提供给PostgreSQL,这意味着它无法优化查询计划。更大的问题是为新用户创建表(todo_list和todo_entries)或删除过时的列表或用户。这通常需要您在之前的场景中避免的幕后管家。最大的性能损失是大多数页面只有很少的内容,所以你浪费磁盘空间和很多时间阅读和编写那些部分填充的页面。

场景3

这种情况甚至更糟糕的情况2.不要这样做,这很疯狂。

  • 300万个表todo_users,每个表都有几个记录

所以...

坚持选项1.这是你唯一真正的选择。