【问题标题】:How to store data with dynamic number of attributes in a database如何在数据库中存储具有动态数量属性的数据
【发布时间】:2010-11-29 11:46:04
【问题描述】:

我有许多具有不同属性的不同对象。到目前为止,我已经将数据保存在 XML 文件中,这些文件很容易允许不断变化的属性数量。但我正在尝试将其移至数据库。

您首选的存储这些数据的方式是什么?

目前我已经确定的一些策略:

  • 在对象表中有一个名为“attributes”的字段,并将序列化或 json 化的数据存储在其中。
  • 将数据存储在两个表(对象、属性)中,并使用第三个表来保存关系,使其成为真正的 n:m 关系。非常干净的解决方案,但获取整个对象及其所有属性可能非常昂贵
  • 识别所有对象共有的属性,并为这些对象的表创建字段。将剩余的属性作为序列化数据存储在另一个字段中。与第一种策略相比,这具有优势,使搜索更容易。

有什么想法吗?

【问题讨论】:

  • 转向数据库解决方案的动机是什么?您在下面说,速度是您主要关心的问题。您认为 db 解决方案会比您当前的 XML 方法更快吗?
  • 第四种策略出现在 DVK 提出的相同建议中:将公共属性存储在对象表中,并使用 1:n 关系将所有其他属性存储在第二个表中。似乎是速度、灵活性和清洁解决方案的最佳折衷方案 (@Tobiask)
  • @Corey,不,我没有。目前,XML 解决方案的速度令人难以置信,我认为数据库无法跟上这一速度。这对我来说更像是一种练习,试图使系统在存储选项方面更加灵活,同时提高我的 MySQL 技能。

标签: mysql database rdbms


【解决方案1】:

如果您曾经计划搜索特定属性,那么将它们序列化为单个列是一个坏主意,因为您必须使用每行函数来获取信息 - 这个很少能很好地扩展。

我会选择你的第二个选择。在属性表中有一个属性列表,在它们自己的表中拥有对象,以及一个称为对象属性的多对多关系表。

例如:

objects:
    object_id    integer
    object_name  varchar(20)
    primary key  (object_id)
attributes:
    attr_id      integer
    attr_name    varchar(20)
    primary key  (attr_id)
object_attributes:
    object_id    integer  references (objects.object_id)
    attr_id      integer  references (attributes.attr_id)
    oa_value     varchar(20)
    primary key (object_id,attr_id)

注意到您对性能的担忧,但根据我的经验,拆分一列总是比合并多列成本更高。如果事实证明存在性能问题,出于性能原因破坏 3NF 是完全可以接受的。

在这种情况下,我会以相同的方式存储它,但也会有一列包含原始序列化数据。如果您使用插入/更新触发器来保持列数据和组合数据同步,那么您不会有任何问题。但在实际问题浮出水面之前,您不必担心这一点。

通过使用这些触发器,您可以将所需的工作减少到仅在数据更改时。通过尝试提取子列信息,您对每个选择都做了不必要的工作。

【讨论】:

  • 正是我对第一个策略的关注。
  • 问题是哪个对性能更好,你的方法是什么你对 json 建模存储数据的看法
  • @babakfaghihian,我想我会在最后两段中介绍这一点,是吗?只要您了解并减轻风险(数据元素彼此“不同意”),就可以打破 3NF 以提高性能。存储原始数据(XML、JSON 或其他)是解决此问题的一种方法。
  • @fadi,很可能是object_attributes 表中的额外值列。我没有添加 all 可能的列,因为我的意图实际上只是为了显示关系字段。我现在已经为这个特定的用例添加了它。
  • @j10,这当然是一种方法,并且模式将支持它,通过应用程序的努力(例如您的类型转换)。我真的只是为字符串属性设计了这个,如果有很多类型,您可能需要考虑其他方法。
【解决方案2】:

您的 2d 解决方案的一个变体只是两个表(假设所有属性都是单一类型):

T1:|对象数据列|Object_id|

T2:|对象 id|attribute_name|属性值| (前 2 列的唯一索引)

与第三种解决方案结合使用时效率更高,例如所有公共字段都进入 T1。

不推荐将 >1 个属性填充到同一个 blob 中 - 你不能按属性过滤,你不能有效地更新它们

【讨论】:

  • 其实,这正是我再次阅读我的三个策略后想到的。听起来是最好的方法。
  • 嗨。这被称为实体-属性-值表,它是糟糕的设计programmers.stackexchange.com/questions/93124/…
  • @GabriBotha - 链接问题的答案绝不支持您的平淡无奇的断言,即这是一个“糟糕”的设计。这是一个具有特定缺陷的设计 - 就像所有设计一样 - 在特定情况下它是最好的方法。
【解决方案3】:

让我具体说明一下 DVK 所说的话。

假设值与表格的类型相同(祝你好运,我觉得你会需要它):

动态属性表 ---------------------- 身份证号码 键 VARCHAR 价值某种类型?

示例(汽车):

|身份证|关键 |价值 | -------------------------- | 1|“制造”|“福特”| | 1|'模型'|'边'| | 1|'颜色' |'蓝色' | | 2|‘制造’|‘雪佛兰’| | 2|‘型号’|‘马里布’| | 2|'MaxSpeed'|'110mph' |

因此,
实体 1 = { ('Make', 'Ford'), ('Model', 'Edge'), ('Color', 'Blue') }
并且,
实体 2 = { ('Make', 'Chevrolet'), ('Model', 'Malibu'), ('MaxSpeed', '110mph') }.

【讨论】:

  • 如果你说一台机器有黑色和黄色怎么办?
【解决方案4】:

如果您使用的是关系数据库,那么我认为您在列出选项方面做得很好。他们每个人都有自己的优点和缺点。您最有能力决定什么最适合您的情况。

序列化方法可能是最快的(取决于您的反序列化代码),但这意味着您将无法使用 SQL 查询数据。如果你说你不需要用 SQL 查询数据,那我同意@longneck,也许你应该使用键/值样式的数据库而不是关系数据库。

编辑 - 阅读更多您的 cmets,如果您主要关心的是速度,为什么要切换到数据库。您当前的 XML 实现有什么问题?

【讨论】:

  • 我猜,current XML implementation 无法应对不断增长的数据量。对于大量对象/实体,XML(s) 可以累积高达几 GB 的大小,......而且,XML(s) 不是可搜索的结构,以优化的方式。因此,要么使用带有索引的内存中自定义数据库,要么滥用现有的关系数据库。
【解决方案5】:

我曾经实现this scheme:

t_class (id RAW(16), parent RAW(16)) -- holds class hierachy.
t_property (class RAW(16), property VARCHAR) -- holds class members.
t_declaration (id RAW(16), class RAW(16)) -- hold GUIDs and types of all class instances
t_instance (id RAW(16), class RAW(16), property VARCHAR2(100), textvalue VARCHAR2(200), intvalue INT, doublevalue DOUBLE, datevalue DATE) -- holds 'common' properties

t_class1 (id RAW(16), amount DOUBLE, source RAW(16), destination RAW(16)) -- holds 'fast' properties for class1.
t_class2 (id RAW(16), comment VARCHAR2(200)) -- holds 'fast' properties for class2
--- etc.

RAW(16) 是 Oracle 的位置 GUIDs

如果你想选择一个对象的所有属性,你发出:

SELECT  i.*
FROM    (
        SELECT  id 
        FROM    t_class
        START WITH
                id = (SELECT class FROM t_declaration WHERE id = :object_id)
        CONNECT BY
                parent = PRIOR id
        ) c
JOIN    property p
ON      p.class = c.id
LEFT JOIN
        t_instance i
ON      i.id = :object_id
        AND i.class = p.class
        AND i.property = p.property

t_property 包含您通常不会搜索的内容(例如文本描述等)

Fast 属性实际上是数据库中的普通表,以提高查询效率。它们仅保存某个类或其后代的实例的值。这是为了避免额外的连接。

您不必使用快速表并将所有数据限制在这四个表中。

【讨论】:

  • 哇,这更进一步。但是,如果每个类类型都有一个表,那不会导致大量表吗?而你最后的 SQL 语句真的让我希望我订购的 MySQL 书能早日到货..
  • @Jörg:这是在 Oracle 中,这是 Oracle 语法。在MySQL 中,您需要以其他方式实现此功能:explainextended.com/2009/03/17/hierarchical-queries-in-mysql
  • 您只需要为“快速属性”创建表:当您需要在两个或多个属性上创建复合索引时。否则,您只能拥有4 基本表。
【解决方案6】:

听起来你需要一些 lick couchdb,而不是 RDBMS。

【讨论】:

  • 这听起来像是一个理想的解决方案。不幸的是,我主要处理的情况是我无法使用 MySQL 之外的东西,更不用说在服务器上安装另一个数据库了。
【解决方案7】:

如果您要在稍后编辑/操作/删除属性,我会选择一个真正的 n:m(第二个选项)。 (或尝试将其设为 2 个相同属性重复的表。但数据量会很大)

如果您不处理属性(只是捕获和显示数据),那么您可以继续并使用一些分隔符存储在一个字段中(确保分隔符不会出现在属性值中)

【讨论】:

    【解决方案8】:

    我假设你没有数字属性汤,但你的数据有一些顺序。

    否则,RDBMS 可能不是最合适的。 NO SQL 的某些东西可能会更好。

    如果您的对象是不同类型的,您通常应该为每种类型创建一个表。

    特别是如果您想使用主键连接它们。如果您有 Products、Orders、Customers 等表,而不仅仅是 Object 和 Attribute 表,这也有助于带来秩序和理智。

    然后看看你的属性。如果存在超过该类型类别中 50% 的对象,则将其设为对象表中的一列,并在不使用时使用 null。

    任何强制性的,当然都应该定义为NOT NULL 列。

    其余的,您可以拥有一个或多个“额外属性”表。

    您可以将属性名称与值一起放入表中,或者将它们规范化到单独的表中并仅使用值表中的主键。

    您可能还会发现您有数据组合。例如,一个对象类型的变体总是具有一组特定的属性,而同一对象类型的另一个变体具有另一组属性。

    在这种情况下,您可能需要执行以下操作:

    MainObjectTable:
      mainObjectId: PRIMARY KEY
      columns...
    MainObjectVariant1Table:
      mainObjectId: FOREIGN KEY TO MainObjectTable
      variant1Columns...
    MainObjectVariant2Table:
      mainObjectId: FOREIGN KEY TO MainObjectTable
      variant2Columns...
    

    我认为,从长远来看,付出的努力是有回报的,就是分析数据,找到对象和常用属性,并将其变成一个好的“对象/ERD/DB”模型。

    【讨论】:

      猜你喜欢
      • 2015-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多