【问题标题】:Selecting distinct value pairs in EAV在 EAV 中选择不同的值对
【发布时间】:2012-01-25 01:44:46
【问题描述】:

我正在开发一个用户数据库,其中配置文件数据已从简单表更改为实体-属性-值表。

结构和以前一样:

userid (int)
address 1 (varchar)   
city (varchar)
country (varchar)

现在是这样的:

userid (int)
key (varchar)  
value (varchar) 

例如

userid key      value
150    city     London
150    country  UK
151    city     New York
151    country  USA
152    country  Mexico   

我需要获取不同的城市/国家对列表以及每个国家/地区所有用户的计数:

city      country  count
London    UK       18
New York  USA      25

不能保证每个用户都存在每个键值对,即可能存在城市、国家或两者,或两者都没有,以及任意数量的其他键值对。

这对旧结构很简单,但我什至想不出如何开始,希望能得到一些指点

【问题讨论】:

  • 医生,医生,我用这把锤子打自己的时候很痛……那就别再用它打自己了。
  • 相当。如果可以选择的话,我会保持原样...
  • 是的,relational 数据库中的 EAV 通常是一个错误(我相信一些 NoSQL 选项是针对此的,效果很好)。除此之外,这并没有解决(也许不是那么明显)城市和国家之间不匹配的问题。或者你似乎没有一个好的和快速的方法来获得独特的userIds(调用DISTINCT不是最好的选择)。

标签: sql entity-attribute-value


【解决方案1】:

您最好的解决方案是回到传统表,因为 EAV 使大多数查询变得比应有的困难得多 - 在这里见证您的问题。您将进行自联接,直到您厌倦它们,重新制造允许您执行合理查询的表结构。

每个用户 ID 的城市和国家:

SELECT a.userID, a.value AS city, b.value AS country
  FROM EAV AS a
  JOIN EAV AS b ON a.UserID = b.UserID
 WHERE a.key = 'city'
   AND b.key = 'country';

所以,你最终得到:

SELECT city, country, count(*)
  FROM (SELECT a.userID, a.value AS city, b.value AS country
          FROM EAV AS a
          JOIN EAV AS b ON a.UserID = b.UserID
         WHERE a.key = 'city'
           AND b.key = 'country'
       ) AS c
 GROUP BY city, country;

如果有人可能有两个城市或两个国家/地区记录,这将为您提供一个笛卡尔积,该用户的行数与该用户的城市和国家/地区记录数的乘积一样多。

这非常刻意和有意识地忽略了有城市但没有国家或国家但没有城市的用户(更不用说那些两者都没有的用户了)。扩展解决方案来处理这些问题只是有点痛苦——我认为你最终会得到一个 3 路 UNION,尽管你可能能够设计出具有多个左外连接的东西。但是,可以将数据输入 EAV 系统而无需进行必要的限制以确保用户有一个城市和国家,这只是拒绝 EAV 的众多原因之一。

很抱歉你把这个强加给你了。我建议将http://careers.stackoverflow.com/ 视为摆脱痛苦的一种方式,因为这只是它的开始。


与没有城市或国家或两者的用户打交道。我认为这或多或少会做到这一点:

SELECT a.userID, b.value AS city, c.value AS country
  FROM (SELECT DISTINCT UserID FROM EAV) AS a
  LEFT JOIN EAV AS b ON a.UserID = b.UserID
  LEFT JOIN EAV AS c ON a.UserID = c.UserID
 WHERE b.key = 'city'
   AND c.key = 'country';

只要该用户没有多个城市或国家/地区记录,这应该为每个用户提供一条记录。 a 扫描为您提供 EAV 表中存在的唯一用户 ID 的列表;两个外连接为每个此类用户 ID 提供相应的城市和相应的国家/地区,如果给定的用户 ID 没有城市记录或国家/地区记录(或两者),则生成空值。

【讨论】:

  • 当您不知道将来会添加多少键和/或键的数量很大时,EAV 非常棒。
  • 谢谢乔纳森,我完全同意,并且非常不喜欢这种做事方式,尤其是在实际存储的属性集有限的情况下。我相信 Facebook 就是这样做的,也许有很多问题需要回答?我会把你的建议付诸实践,看看性能上的影响是否足以说服我的老板把它改回来......
【解决方案2】:

re:我需要得到一个不同的城市/国家对列表

SELECT DISTINCT country,city
FROM
(SELECT DISTINCT userid, VALUE AS country FROM TABLE WHERE KEY = 'country') country INNER JOIN
(SELECT DISTINCT userid, VALUE AS city FROM TABLE WHERE KEY = 'city') city ON
country.userid = city.userid

--count of all users for each country
SELECT VALUE AS country, 
COUNT(DISTINCT userid) AS user_count 
FROM TABLE 
WHERE KEY = 'country'
GROUP BY 
VALUE

【讨论】:

    猜你喜欢
    • 2013-10-13
    • 1970-01-01
    • 1970-01-01
    • 2018-07-24
    • 2020-12-13
    • 2020-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多