为什么我需要拥有多个关系?

8
我最近遇到了一个关于关系数据库的有趣情况。我正在编写一个Ruby应用程序,使用PostgreSQL作为我的数据库。我有一个名为"用户"的父对象和一个相关的对象"物品",其中一个用户可以拥有一个或多个物品。与将数据嵌入到父表字段中相比,使用单独的表有什么优势呢?
来自ActiveRecord的示例:
使用相关表:
def change
    create_table :users do |i|
        i.text :name
    end
    create_table :thingies do |i|
        i.integer :thingie
        i.text :discription
    end
end
class User < ActiveRecord::Base
    has_many :thingies
end
class Thingie < ActiveRecord::Base
    belongs_to :user
end

使用嵌入式数据结构(多维数组)方法:

def change
    create_table :users do |i|
        i.text :name
        i.text :thingies, array: true # example contents: [[thingie,discription],[thingie,discription]]
    end
end
class User < ActiveRecord::Base
end

相关信息

我正在使用Heroku和Heroku-Postgres作为我的数据库。我正在使用它们的免费选项,限制了我只能使用10,000行数据。这似乎让我想使用多维数组方式,但我并不真正了解。


http://stackoverflow.com/questions/27257093/rails-use-serialized-attributes-or-a-belongs-to-association - Lanny Bose
@LannyBose 不,那是一个不同的问题。那是关于序列化 vs. has_many 的问题,而我不会有这个问题,因为我将使用多维数组。那个答案对我来说不是一个答案,而且问题与我的不同。 - thesecretmaster
3
如果您确实希望在不进行反序列化和循环的情况下查询关系,该怎么办? - Dave Newton
1
has_one/has_many、belongs_to 宏是特定于 ActiveRecord 的。看起来这个问题更多地涉及关系型数据库设计,而不是 ActiveRecord 的使用。换句话说,“使用单独的表与将对象嵌入数组/对象字段相比有什么优势”。是这样吗? - jpgeek
@jpgeek 是的!在问题中,我该如何更清楚地表达呢?我甚至没有想过这个角度。你能否为我提供一些修改建议或直接进行修改呢?那将非常有帮助。 - thesecretmaster
显示剩余3条评论
4个回答

11

在某些简单情况下,将数据结构嵌入字段中可能是可行的,但它会阻止您利用关系型数据库。关系型数据库被设计用于查找、更新、删除和保护您的数据。使用包含自己一大块数据(数组、JSON、xml 等)的嵌入式字段,最终您会编写所有代码来执行这些操作。

有时候嵌入式字段可能更加适合,但本问题的例子是为了突出关联表方法的优势。

想象一个博客的用户和帖子示例。

对于一个嵌入式帖子解决方案,您可能会拥有类似以下的表(假代码 - 这些可能不是有效的 DDL):

create table Users {
id int auto_increment,
name varchar(200)
post text[][],
}

使用相关表时,你可以这样做:

create table Users {
id int auto_increment,
name varchar(200)
}
create table Posts {
id auto_increment,
user_id int,
content text
}

对象关系映射(ORM)工具: 对于嵌入式文章,您需要手动编写代码来添加文章到用户、浏览现有的文章、验证和删除它们等。通过单独的表设计,您可以利用ActiveRecord(或您正在使用的任何对象关系系统)工具完成这项工作,这应该使您的代码更简洁。

灵活性: 想象一下,您想在文章中添加一个日期字段。您可以使用嵌入式字段进行操作,但是您将不得不编写代码来解析数组、验证字段、更新现有的嵌入式文章等。通过单独的表,这会简单得多。此外,假设您想向系统添加一个编辑器来批准所有文章。在关系示例中,这很容易。例如,要通过ActiveRecord查找由'Bob'编辑的所有文章,您只需要:

Editor.where(name: 'Bob').posts

对于嵌入式方案,您需要编写代码以遍历数据库中的每个用户,解析他们的每个帖子并查找编辑器字段中的“Bob”。

性能:假设您有10,000个用户,每个用户平均有100篇帖子。现在您想要找到某个日期发布的所有帖子。使用嵌入式字段,您必须循环遍历每个记录,解析所有帖子的整个数组,提取日期并检查是否与您想要的日期相匹配。这将消耗CPU和磁盘I/O。使用数据库,您可以轻松地索引日期字段并提取您需要的确切记录,而无需解析每位用户的每个帖子。

标准:使用供应商特定的数据结构意味着将应用程序移动到另一个数据库可能会很麻烦。Postgres似乎具有丰富的数据类型集,但它们与MySQL、Oracle、SQL Server等不同。如果您坚持使用标准数据类型,将更容易交换后端。

这些是我最初看到的主要问题。我曾犯过这个错误,并为此付出了代价,因此,除非有超级强有力的理由,否则我会使用单独的表。


2
如果用户John和Ann拥有相同的物品,记录将会重复,如果您决定更改物品的名称,您将不得不更改两个或更多的记录。如果物品存储在单独的表中,您只需要更改一条记录。请参考https://en.wikipedia.org/wiki/Database_normalization了解更多信息。

2

一对多关系的好处:

  1. 更容易集成ORM(对象关系映射)。您可以使用任一方式,但必须使用本地SQL定义表格。具有清晰的表格更容易,您可以利用自动生成的映射。
  2. 在两个或多个人可以拥有相同“东西”的情况下,您的10,000行空间限制将进一步发挥一对多关系的作用。
  3. 分别处理用户和"东西"。在某些情况下,您可能只关心人或东西,而不是它们之间的关系。例如更新用户名或东西描述,获取所有东西(或所有用户)的列表。从单个表中选择可能会使其难以处理。
  4. 维护和操作更容易。如果更新了用户或东西(名称更改,电子邮件地址更新等),则只需在其表中更新1条记录,而不必编写“WHERE user_id =?”的更新语句。
  5. 可强制执行的数据库约束条件。如果没有人拥有一个东西怎么办?现在用户列是否为空?在单个表的情况下,它必须是nillable,因此您无法强制执行简单的“非空”用户名,例如。

当然,有很多原因。如果您使用关系型数据库,请通过将对象(用户和东西)分别作为单独的表来使用一对多。考虑到您的记录数量限制以及数据集的大小较小(低于10,000),您不应该感受到规范化数据的负面影响。

简单的真相是两者都有好处。例如,您可以从单个表方法中获得更快的读取时间,因为您不需要复杂的连接。

这里是一个良好的参考,其中包含两种方法的优缺点(规范化是多表方法,去规范化是单表方法)。 http://www.ovaistariq.net/199/databases-normalization-or-denormalization-which-is-the-better-technique/


如果我说我的收获是使用包含较少重复或更DRY的方式,那么我会错吗? - thesecretmaster
我不会分享我的东西所有权。人们可以使用我的东西,但是这些东西仍然属于我。 - Bassel Samman

1
除了其他人提到的好处之外,标准还有一件事情。如果你是独自开发这个应用程序,那么这不是一个问题,但如果其他人想要更改某些内容,那么噩梦就开始了。这个人可能需要很长时间才能理解它是如何工作的。而像这样修改某些内容将需要更多的时间。这样,一些简单的改进可能真的会耗费很多时间。在某些时候,你将与其他人一起工作。因此,始终以最终使用你的代码的人是知道你住在哪里的残忍的精神病患者的方式编写代码。

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