我正在为我的软件编写一个地址簿模块。到目前为止,我已经设置了数据库,它支持非常灵活的地址簿配置。
我可以为我想要的每种类型创建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。 是否有可能以某种方式创建一个View,它给我一个类型为列的结果?
所以现在我会得到像
这样的东西#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
我最初不想像这样创建表格布局的原因是,可能总是要添加的东西,我希望能够拥有相同类型的多个属性。
所以你知道是否有可能动态转换它?
如果您需要更好的描述,请告诉我。
答案 0 :(得分: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
方式连接。
SELECT * FROM contact_profiles c
LEFT OUTER JOIN contact_attributes ca ON (c.id = ca.profile);
如果您需要这种灵活性,如果不使用EAV设计,您在评论中询问该怎么办?如果您确实需要无限的元数据灵活性,SQL不是正确的解决方案。以下是一些替代方案:
TEXT
BLOB,其中包含以XML或YAML格式构建的所有属性。EAV和任何这些替代解决方案都是很多工作。如果您真的需要在数据模型中具有这种程度的灵活性,那么您应该非常仔细地考虑,因为如果您可以将元数据结构视为相对不变的话,这将非常简单。
答案 1 :(得分: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
表中。
答案 2 :(得分: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将所有值连接为逗号分隔的字符串,或者您可以编写一个。
出于您已经提到的原因,通常最好避免使用这种数据模型!
答案 3 :(得分:0)
您为每种联系类型
创建一个视图如果您需要从整个表中提取的所有信息,当您需要特定联系人类型的子集时,可以从视图中提取。
我创建了一个存储过程,它将intent {all,phone,email,address}作为参数之一,然后派生数据。我的所有应用程序代码都会调用此存储过程来获取数据。此外,当添加新类型时(应该很少,您可以创建另一个视图并仅修改此sproc)。
我为多个小型/中型系统实施了类似的设计,没有任何问题。
我错过了什么吗?这看起来微不足道?
编辑:
我看到了我所缺少的东西......你正试图在同一时间进行规范化和非规范化。我不确定你的其他业务规则是什么用于提取记录。您可以为配置文件提供多个或空值的电话/电子邮件/地址等。我会保持您的数据格式相同,并再次使用sproc来创建您想要的特定视图。随着业务需求的变化,您可以单独保留数据,只需创建另一个sproc即可访问它。
答案 4 :(得分:0)
对于这个问题,没有一个正确的答案,因为您需要知道,对于您的特定组织或应用程序,业务想要收集多少联系方法,他们想要的当前状态信息,以及他们愿意投入多少灵活性。
当然,这里的很多人都可以对一般企业想做什么做出一些好的猜测,但真正的答案是找出你的项目,你的用户对什么感兴趣。
BTW,关于“最佳”性的所有架构问题都需要这种成本,收益和风险分析。答案 5 :(得分:0)
现在面向文档的数据库越来越受欢迎,人们可以使用其中一种将所有这些信息存储在一个条目中 - 从而删除所有这些额外的连接和查询。