需要SQL查询的帮助

3

假设我有两个表:

Person

 - Id
 - Name

PersonAttribute

 - Id
 - PersonId
 - Name
 - Value

假设每个人都有两个属性(比如性别和年龄),一个样本记录可能是这样的:

Person->Id = 1
Person->Name = 'John Doe'

PersonAttribute->Id = 1
PersonAttribute->PersonId = 1
PersonAttribute->Name = 'Gender'
PersonAttribute->Value = 'Male'

PersonAttribute->Id = 2
PersonAttribute->PersonId = 1
PersonAttribute->Name = 'Age'
PersonAttribute->Value = '30'
问题:如何查询以获得以下结果: '约翰·多', '男', '30'
8个回答

4
SELECT p.name, p1.Value, p2.Value 
     FROM Person p, PersonAttribute p1, PersonAttribute p2 
     WHERE p.Id = p1.PersonId AND p.Id = p2.PersonId 
        AND p1.Name = 'Gender' AND p2.Name = 'Age'

1
这种方法无法扩展以获取N个通用属性及其值。 - Timothy

3

我认为你需要重新设计你的模式。 为什么不这样做呢?

Person

 - Id
 - Name
 - Gender
 - Birthday
...

2
完全同意。看起来年龄是存储在PersonAttribute->Value字符串列中的,以及像“男性”这样的东西(正如你所指出的,这应该被推导出来)。 - Martin Smith
1
@stackoverflownewbie - 坏的架构是当你请求帮助而不是编写一个简单的查询时。:-) 你的设计是你的事情,但你会遇到持续的问题。 - msi77
1
@Tim Drisdelle - 如果您使用关系型数据存储,您需要遵循规范化规则,我认为。另一个问题是如何将关系模型映射到OP实体。 - msi77
1
msi77说得很对,这个数据库看起来像是另一个即将崩溃的EAV灾难。@StackOverflowNewbie,请谷歌并学习规范化。或者,当“John Doe”有100个属性或一千个属性时,考虑如何编写查询语句。 - Brock Adams
@Brock Adams - 这就是关键所在... 如果它被设计成这样,那么很可能是为了支持一个查询,当"John Doe"有100个属性时,所有这些属性都有用户生成的名称和值。规范化将不起作用。 - Timothy
显示剩余4条评论

2

从Person表中选择Name列,从PersonAttribute表中选择Gender和Age列,连接条件是Person表的Id等于PersonAttribute表的Id并且Gender列的值为"Gender",Age列的值为"Age"。


2

+1 当我看到那样的模式时,我总是想起那篇文章。 - Martin Smith
+1 这绝对是一个棘手的问题。从概念上来说,它非常出色,但实现起来却很痛苦。 - Timothy

1

这并不是一件容易的事情。

透视表的概念(已经被另一个答案提到)基本上就是你要找的东西,但是透视表需要你知道你想要使用的列的名称。当你想要利用这种表格设计的强大功能时,这显然是一个问题!

在我的以前的工作中,我只选择了X个列,比如20-30个,如果它们不存在,那么行集就包括一堆空值。没什么大不了的。

select piv.name, 
    max(case piv.a_name when 'Gender' then piv.a_value else null end) as Gender,
    max(case piv.a_name when 'Age' then piv.a_value else null end) as Age,
    max(case piv.a_name when 'Hobby' then piv.a_value else null end) as Hobby
from 
(select p.name as name, pa.name as a_name, pa.value as a_value 
from person p, personattribute pa
where p.id = pa.personid) piv
group by piv.name

这将生成以下输出:

   name    | gender | age |  hobby  
-----------+--------+-----+---------
 Bob Swift | Male   |     | Reading
 John Doe  | Male   | 30  | 
(2 rows)

这非常接近你要找的东西。我会将其余部分留给你的应用程序层。

我还强烈建议您在返回值中包含属性名称(NAME),以为VALUE提供上下文。

这些所谓的实体-属性设计类型通常不得不依赖于特定服务器函数、存储过程和硬编码查询的组合。


1

不考虑设计,你总是可以PIVOT结果,但你需要提前知道选择了多少个属性。


0
SELECT Name, g.Value, a.Value
FROM Person, 
PersonAttribute g INNER JOIN ON g.Name = "Gender", 
PersonAttribute a INNER JOIN ON a.Name = "Age"

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,