2

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

我可以为我想要的每种类型创建 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

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

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

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

4

6 回答 6

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 不是正确的解决方案。以下是一些替代方案:

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

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

于 2008-12-15T18:25:53.793 回答
1

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

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“email”、“website”等的表。然后将contact_attribute_types.id整数值存储在contact_attributes表中。

于 2008-12-15T18:20:47.250 回答
0

您将需要生成如下查询:

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 将所有值连接为逗号分隔的字符串,或者您可以编写一个。

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

于 2008-12-15T18:24:06.573 回答
0

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

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

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

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

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

编辑:

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

于 2008-12-15T18:35:55.880 回答
0

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

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

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

于 2009-05-09T00:31:07.373 回答
0

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

于 2010-05-17T12:29:34.257 回答