【问题标题】:How to design database using varying number of columns in table如何使用表中不同数量的列来设计数据库
【发布时间】:2012-03-15 02:30:29
【问题描述】:

假设您正在创建一个系统来存储不同国家/地区的特征。将有相同的基本列,如名称、人口、首都等。但让我们说除此之外,您还想存储一些特定于国家/地区的信息,例如最高的山、最近的海洋、最著名的食物等。这些列对于每个列都是不同的国家。

如何使用 MySQL 之类的关系数据库来做到这一点。 我知道这更容易使用像 MongoDB 这样的无模式 NoSQL 数据库,其中每个国家/地区都可以存储为单独的文档。但是这样的事情可以使用关系数据库来完成吗?

【问题讨论】:

  • 您必须创建 2 个额外的表。第一个是项目列表 (id, title) = (1, 'Highest mountain'),第二个表用于存储国家/地区的值 - (itemId,countryId, value)
  • @gaurav:我认为 nosql 标签不适合 rdbms 相关问题...
  • @iDevlop - 谢谢,我删除了 nosql 标签。
  • 虽然我认为所有建议EAV 方法的答案(不知道那个术语,谢谢@iDevlop)都是正确的,但我大多数时候更喜欢一个包含大量空值的表。高度规范化的模式往往会对性能产生相当糟糕的影响,但这也取决于使用模式。也许组合是最好的方法:具有最常见属性的主表和用于附加属性值对的附加表。

标签: mysql database postgresql database-design


【解决方案1】:

您所描述的是数据结构的 Super-Type - Sub-TypeSuper-Type 是所有数据中常见的(在您的情况下为国家)。 子类型是每组数据(在您的情况下为国家/地区)所独有的。您将拥有一个超类型表几个子类型表。子类型表包含链接回超类型表的外键。

这使您可以按 super-type 查询所有内容,然后按 sub-type 进行下钻。

子类型想到的国家有:
高山
内陆
海景

您甚至可以按大陆将它们分出: 北美
南美洲
亚洲
大洋洲

【讨论】:

    【解决方案2】:

    这里我们有三个策略:

    1. 全元设计,将国家的可空属性值放入值收集表中。例如:

      country(country_id, non-null-attr-1, non-null-attr-2, non-null-attr-....) meta_attr(attr_id, attr_desc)(如果需要 I18N 可能更复杂) attr_value(country_id, attr_id, attr_value)

    2. 部分元设计,使用表的子类来引用国家的主表。如果您可以将某个数据实例分类为非空属性的集合,则这种方法是可用的。例如:

      country(country_id, non-null-attr-1, non-null-attr-2, non-null-attr-....) specific_type_country(country_id, non-null-attr-1, non-null-attr-2, non-null-attr-...)

    3. 国家主表中的所有属性,此方法仅在您不需要从系统中将新属性添加到国家/地区时才可行。例如:

      country(country_id, non-null-attr-1, non-null-attr-2, non-null-attr-...., nullable-attr-1, nullable-2, nullable-attr-.. .)

    当我在这样的场景下进行设计时,我曾经考虑过在这些数据上运行的查询的性能。

    如果查询是所有可能属性的国家列表,则使用 No.3 更好。

    如果查询以特定类别的国家/地区为目标,例如,国家/地区列表具有最近的海洋(此属性不能为空)。 2号更好。

    如果查询一次需要一个国家的详细信息,No.1 更好。

    当然,您可以混合使用以上三种策略中的任何一种,为您可能的查询设计一个合适的解决方案。


    假设任何查询都需要“最有名的食物”(可为空),将此属性放入国家/地区的主表中。

    假设在少数查询中需要“最近的海洋”,将该属性放入国家表的子类中。

    假设最多只检索一行的查询(比如按主键查询)需要“最高山名”、“最高山平均气温”,将该属性放入元表中。

    【讨论】:

      【解决方案3】:

      我们以山为例:

      CREATE TABLE `countries` (
        `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
        `name` VARCHAR(255),
        PRIMARY KEY (`id`)
      ) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
      
      CREATE TABLE `mountains` (
        `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
        `country_id` INT(11) UNSIGNED NOT NULL,
        `name` VARCHAR(255),
        `height` INT(10) NOT NULL,
        PRIMARY KEY (`id`),
        KEY `fk_country_id` (`country_id`)
      ) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
      

      您可以创建一个SELECT-query,以获取每个国家/地区的最高山峰,方法是:

      SELECT c.name, m.name, MAX(m.height) as height
      FROM mountains m
      JOIN countries c
      ON c.id = m.country_id
      GROUP BY m.country_id;
      

      【讨论】:

      • 你会为每个实体创建单独的表吗?山,河,海,食物等等?如果您只有少数实体,这是一个好方法。但在这种情况下,它不是。恕我直言。
      • 我不想对此投反对票,但在我看来这种设计非常糟糕。我知道这是一个老话题,但万一像我这样的人以后找到这个具体的答案:不要这样做。
      • @MichaelOzeryansky:也许向我们展示为什么您认为这种设计不好是个好主意。我也喜欢从错误中吸取教训。
      • 此方法需要为每个属性创建表。属性是一种类型,应该单独使用列而不是表。该问题指出,每个国家/地区可能都没有该财产。因此,在基于列的设计中,该单元格的值应该为空。您的设计也没有考虑构建查询的因素。 dba 必须为每个表创建一个连接语句。例如,一个国家/地区的 20 个属性将有 20 个连接,并且由于每个表都是它自己的文件,因此 MySQL 需要使用大量内存和 CPU 周期来计算连接条件。
      【解决方案4】:

      我有一个类似的应用程序需要 mySQL,我发现对我们来说最灵活的选择是将数据拆分为多个表,例如我们可能有一个名为 country_register 的表

      country_id (int primary key) | country_name 
      

      然后我们有另一个名为say country_data 的表

      tbl_id (int primary key) | country_id (int foreign key) | country_property (varchar index) | country_data (text indexed as fulltext)
      

      基本上country_property 是您获取数据的参考,因此它可以是例如“人口”,然后country_data 将拥有您想要的实际数据。

      然后您将使用JOIN,每一行都将包含您需要的所有数据。这是我所知道的使用 mySQL 的最灵活的结构,它适用于这些类型的任务。

      我希望这会有所帮助。

      【讨论】:

        【解决方案5】:

        如果每个国家/地区的列确实不同,则创建一个名为 country_field 的新表,其中包含以下列

        • country_id(FK 到您的国家/地区表)
        • field_name varchar
        • field_value varchar

        将您的国家/地区特定属性存储在此表中,每个国家/地区特定字段占一行。

        【讨论】:

          【解决方案6】:

          可以。正如我今天通过询问another question on SO 了解到的,这称为 EAV(用于实体-属性-值模型)。我在wikipedia找到了一个有趣的解释。

          【讨论】:

            【解决方案7】:

            只有文本字段,您需要两个额外的表格:

            • 属性(包含属性名称,例如“最高山”)
            • country_properties(包含国家/地区属性对的值:例如:国家/地区的 ID“奥地利”、财产的 ID“最高的山”、“山的名称”)

            或者,如果只有几个属性,只需将 NULL 存储为未知值。

            【讨论】:

            • 如果您还有整数或浮点值,您可以将 fieldType 列存储在 properties 表中,然后您可以将 3 列添加到 values 表中作为 (propId, countryId, charValue, intValue,浮动值)
            【解决方案8】:

            您真的不应该使用标准的关系数据库来执行此操作。而是将额外数据存储在单独的表中,并使用引用国家/地区表的外键。

            只有有时才使用的列通常违反了关系完整性。有时出于性能原因,这是必要的,但如果您不关心这一点,我强烈建议您使用最合适的关系模型。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-10-25
              • 1970-01-01
              • 1970-01-01
              • 2011-03-15
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多