2

我正在为我的软件写一个地址簿模块。我已经建立了数据库,它支持非常灵活的地址簿配置。SQL数据库地址簿表设计问题

我可以为每个我想要的类型创建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 [email protected] 
1  name Sebastian Hoitz 
1  website domain.tld 

但它会很高兴得到这样的结果:

#profile email   name   website 
1  [email protected] Sebastian Hoitz domain.tld 

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

那么你知道是否有任何可能将其动态转换?

如果您需要更好的描述,请让我知道。如果您使用的SQL Server

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 

,您:

回答

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加入。

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

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

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

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

1

如果你是限制自己在这个查询显示单个电子邮件,名称,网站等,对每个人,我会使用子查询也可以看看PIVOT

如果您想要显示多个电子邮件,电话等,请考虑每个配置文件必须具有相同的编号或空白。

我也会列出类型列。创建一个名为contact_attribute_types的表格,该表格可容纳“电子邮件”,“网站”等。然后,您将contact_attribute_types.id整数值存储在contact_attributes表中。

+0

分析类型的好处。我忘了那个!谢谢:) – 2008-12-15 18:29:12

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每种类型的一个值。你的数据库管理系统可能有一个你可以用来代替MAX的函数来连接所有的值作为逗号分隔的字符串,或者你可以写一个。

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

+0

但是,如果您希望在您可以输入的数据中具有这种灵活性,那么使用此数据模型还有其他选择吗? – 2008-12-15 18:26:28

+0

托尼的解决方案也假设NULL排序低于任何非NULL值。在所有SQL实现中都不是这样。 – 2008-12-15 18:28:33

0

您为每个联系人创建类型

当你想你从整个表中提取信息,当你想要一个特定的接触式的一个子集,你从视图拉图。

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

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

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

编辑:

我看到我失踪了......你想成为标准化,并在同一时间规格化。我不确定您的其他业务规则是用于提取记录的。你可以为电话/电子邮件/地址等多个或空值的配置文件。我会保持你的数据格式相同,并再次使用存储区创建你想要的特定视图。随着您的业务需求发生变化,您可以将数据放在一边,然后创建另一个sproc来访问它。

0

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

当然,这里的很多人可以对平均业务要做什么有一个很好的猜测,但真正的答案是找出你的项目,你的用户感兴趣的是什么。

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

0

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