给出两个表和值:
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,则不返回任何结果。
你有一些提示吗?
提前致谢:)
答案 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保护。
如果您有充分的理由使用键/值方法,那么您的查询有三种选择:
对于您要搜索的每个字词,请在查询中加入一次键/值表(请参阅上面的Philip的回答)。
使用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')
如果您的SQL方言支持,请使用某种INTERSECT或SUBTRACT运算符。