如何通过连接按行查询值?

时间:2010-03-03 16:58:31

标签: sql join

给出两个表和值:

Tables
People- Columns: [ID]PK Name
Field - Columns: [ID FieldName]PK FieldValue

Values
People
ID Name
1  Mary
2  John
3  Tony

Field
ID FieldName  FieldValue
1  Age        20
1  Country    USA
2  Age        21
2  Country    USA
3  Age        20
3  Country    USA

我想要一个查询,它给出了来自美国和20岁的人的名字。结果应该是Mary和Tony。

SELECT name FROM people p
INNER JOIN field f ON f.ID = f.ID
WHERE
(FieldName = 'Age' AND FieldValue= '20') OR
(FieldName = 'Country' AND FieldValue= 'USA'));

该查询的问题是where子句之间的“OR”。我会得到3个人,而不仅仅是玛丽和托尼。如果我使用AND,则不返回任何结果。

你有一些提示吗?

提前致谢:)

3 个答案:

答案 0 :(得分:3)

这个怎么样?

SELECT name
FROM people p
JOIN field fAge ON p.ID = fAge.ID
   AND fAge.FieldName = 'Age'
JOIN field fCountry ON p.ID = fCountry.ID
   AND fCountry.FieldName = 'Country'
WHERE fAge.FieldValue = '20'
AND fCountry.FieldValue = 'USA'

从而将Country和Age数据基本上视为单独的表。因为似乎field表用于存储不同类型的数据。

对于每种类型的数据,在此表上使用不同的数据库Views可能很有用。这将进一步简化上述查询。

答案 1 :(得分:1)

遗憾的是,您在此处所做的是实体 - 属性 - 值(EAV)反模式。 (反模式对于“通常是一个坏主意的东西来说是一个奇特的词。”)这通常与SQL最佳实践相反,有一个像你这样的“字段”表,这就是为什么它有一个以它命名的反模式。 “实体”是指您的ID列(或通过关联,是人员记录),“属性”对应于FieldName,“Value”对应于FieldValue。

有关详细信息,请查看this excellent set of slides(这应该直接链接到EAV部分;如果没有,请跳到幻灯片16)。

解决方案是重构数据库,以便Age和Country是People表中的字段。然后可以进行更简单的查询:SELECT name FROM people WHERE Age = 20 AND Country = 'USA';

答案 2 :(得分:1)

您没有说您是否考虑并拒绝了单个表中名称,年龄和国家/地区的“标准”设计。这样可以大大简化查询,并为Age值提供类型和范围保护,并为Country值提供FK / PK保护。

如果您有充分的理由使用键/值方法,那么您的查询有三种选择:

  1. 对于您要搜索的每个字词,请在查询中加入一次键/值表(请参阅上面的Philip的回答)。

  2. 使用WHERE EXISTS条件:

    SELECT Name FROM People WHERE
       EXISTS (SELECT * FROM Field WHERE ID = People.ID AND FieldName = 'Country' AND Fieldvalue = 'USA')
       AND EXISTS (SELECT * FROM Field WHERE ID = People.ID AND FieldName = 'Age' AND Fieldvalue = '20')
    
  3. 如果您的SQL方言支持,请使用某种INTERSECT或SUBTRACT运算符。