【问题标题】:SQL database problems with addressbook table design与地址簿表设计有关的 SQL 数据库问题
【发布时间】:2010-09-27 00:29:54
【问题描述】:

我现在正在为我的软件编写地址簿模块。到目前为止,我已经设置了数据库,它支持非常灵活的地址簿配置。

我可以为我想要的每种类型创建 n 个条目。类型在这里表示“电子邮件”、“地址”、“电话”等数据。

我有一个名为“contact_profiles”的表。

这只有两列:

id           Primary key
date_created DATETIME

还有一个叫做contact_attributes的表。这个有点复杂:

id       PK
#profile (Foreign key to contact_profiles.id)
type     VARCHAR describing the type of the entry (name, email, phone, fax, website, ...) I should probably change this to a SET later.
value    Text (containing the value for the attribute).

我现在可以链接到这些配置文件,例如从我的用户表中。但是从这里我遇到了问题。

目前,我必须为要检索的每个值创建一个 JOIN。 是否有可能以某种方式创建一个视图,它给我一个以类型为列的结果?

所以现在我会得到类似的东西

#profile type    value
1        email   name@domain.tld
1        name    Sebastian Hoitz
1        website domain.tld

但如果能得到这样的结果就好了:

#profile email           name            website
1        name@domain.tld Sebastian Hoitz domain.tld

我最初不想创建这样的表格布局的原因是,可能总是要添加一些东西,并且我希望能够拥有多个相同类型的属性。

那么你知道是否有可能动态转换它吗?

如果您需要更好的描述,请告诉我。

【问题讨论】:

    标签: sql data-structures entity-attribute-value database-relations


    【解决方案1】:

    现在面向文档的数据库方法越来越流行,人们可以使用其中一种将所有这些信息存储在一个条目中,从而删除所有那些额外的连接和查询。

    【讨论】:

      【解决方案2】:

      对于这个问题没有一个正确的答案,因为人们需要知道,对于您的特定组织或应用程序,企业想要收集多少这些联系方式,他们想要多最新信息,以及他们愿意投资多少灵活性。

      当然,这里的许多人可以很好地猜测普通企业想要做什么,但真正的答案是找出你的项目,你的用户对什么感兴趣。

      顺便说一句,所有关于“最佳”的架构问题都需要这种成本、收益和风险分析。

      【讨论】:

        【解决方案3】:

        您为每个联系人类型创建一个视图

        当您想要从整个表格中提取所有信息时,当您想要特定联系人类型的子集时,您可以从视图中提取。

        我将创建一个将意图 {all, phone, email, address} 作为参数之一的存储过程,然后派生数据。我所有的应用程序代码都会调用这个存储过程来获取数据。此外,当添加新类型时(这应该很少见,您创建另一个视图并仅修改此存储过程)。

        我已经为多个小型/中型系统实施了类似的设计,并且没有遇到任何问题。

        我错过了什么吗?这似乎微不足道?

        编辑:

        我明白我遗漏了什么...您正在尝试同时进行规范化和非规范化。我不确定您提取记录的其余业务规则是什么。您可以拥有电话/电子邮件/地址等具有多个或空值的配置文件。我会保持您的数据格式相同,并再次使用存储过程来创建您想要的特定视图。随着您的业务需求发生变化,您可以不理会数据,只需创建另一个存储区即可访问它。

        【讨论】:

          【解决方案4】:

          您重新发明了一个名为Entity-Attribute-Value 的数据库设计。这种设计有很多弱点,包括您发现的弱点:很难以传统格式重现查询结果,每个属性只有一列。

          这是你必须做的一个例子:

          SELECT c.id, c.date_created,
           c1.value AS name,
           c2.value AS email,
           c3.value AS phone,
           c4.value AS fax,
           c5.value AS website
          FROM contact_profiles c
           LEFT OUTER JOIN contact_attributes c1
            ON (c.id = c1.profile AND c1.type = 'name')
           LEFT OUTER JOIN contact_attributes c1
            ON (c.id = c1.profile AND c1.type = 'email')
           LEFT OUTER JOIN contact_attributes c1
            ON (c.id = c1.profile AND c1.type = 'phone')
           LEFT OUTER JOIN contact_attributes c1
            ON (c.id = c1.profile AND c1.type = 'fax')
           LEFT OUTER JOIN contact_attributes c1
            ON (c.id = c1.profile AND c1.type = 'website');
          

          您必须为每个属性添加另一个LEFT OUTER JOIN。您在编写查询时必须知道属性。您必须使用LEFT OUTER JOIN 而不是INNER JOIN,因为没有办法强制属性(相当于简单地声明一个列NOT NULL)。

          在存储属性时检索它们的效率要高得多,然后编写应用程序代码来循环遍历结果集,为每个属性构建一个对象或关联数组。这种方式不需要知道所有的属性,也不需要执行n-way join。

          SELECT * FROM contact_profiles c
            LEFT OUTER JOIN contact_attributes ca ON (c.id = ca.profile);
          

          您在评论中询问如果您需要这种级别的灵活性,如果不使用 EAV 设计该怎么办?如果您确实需要无限的元数据灵活性,SQL 不是正确的解决方案。以下是一些替代方案:

          • 存储一个TEXT BLOB,包含以 XML 或 YAML 格式结构化的所有属性。
          • 使用语义数据建模解决方案,例如 Sesame,其中任何实体都可以具有动态属性。
          • 放弃数据库并使用平面文件。

          EAV 和任何这些替代解决方案都需要大量工作。如果您真的需要数据模型中的这种程度的灵活性,您应该非常仔细地考虑,因为如果您可以将元数据结构视为相对不变,它会变得非常简单。

          【讨论】:

          • 谢谢,这给了问题一个名字! :) 我很想使用本地关联数组,但是如果我有一个要添加联系信息的条目列表怎么办?我应该为要显示的所有列表条目创建一个临时数组吗?
          • 如果您需要更新条目,您需要一次更新一个。从数据库加载到数组中,更改属性,然后保存到数据库。如果这样做,还需要跟踪属性删除;您不能只取消设置数组元素。
          【解决方案5】:

          您需要生成如下查询:

          select #profile,
                 max(case when type='email' then value end) as email,
                 max(case when type='name' then value end) as name,
                 max(case when type='website' then value end) as website
          from mytable
          group by #profile
          

          但是,每个#profile 的每种类型只会显示一个值。您的 DBMS 可能有一个函数可以用来代替 MAX 将所有值连接为逗号分隔的字符串,或者您可以编写一个。

          由于您已经提到的原因,通常最好避免使用这种数据模型!

          【讨论】:

          • 但是,当您希望在输入数据时具有这种灵活性时,还有其他选择吗?
          • Tony 的解决方案还假定 NULL 排序低于任何非 NULL 值。并非所有 SQL 实现都是如此。
          • 所以也许使用 MIN 而不是 MAX?
          【解决方案6】:

          如果您限制自己在此查询中为每个人显示单个电子邮件、姓名、网站等,我会使用子查询:

          SELECT cp.ID profile
            ,cp.Name
            ,(SELECT value FROM contact_attributes WHERE type = 'email' and profile = cp.id) email
            ,(SELECT value FROM contact_attributes WHERE type = 'website' and profile = cp.id) website
            ,(SELECT value FROM contact_attributes WHERE type = 'phone' and profile = cp.id) phone
          FROM contact_profiles cp
          

          如果您使用的是 SQL Server,还可以查看 PIVOT。

          如果您想显示多封电子邮件、电话等,请考虑每个配置文件的数量必须相同,否则您将有空白。

          我还会考虑类型列。创建一个名为contact_attribute_types 的表,该表将包含“电子邮件”、“网站”等。然后将contact_attribute_types.id 整数值存储在contact_attributes 表中。

          【讨论】:

          • 区分类型的好点。我忘了那个!谢谢:)
          猜你喜欢
          • 2010-09-14
          • 1970-01-01
          • 1970-01-01
          • 2021-07-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-21
          • 2011-11-30
          相关资源
          最近更新 更多