正如 Marty McVry 所说,这是一个有趣的问题。这是我的解决方案。请注意,我冒昧地重命名了您的表格...
架构:
CREATE TABLE user (id int, username varchar(200), info varchar(200));
CREATE TABLE attr (id int, user_id int, a_name varchar(200), a_value varchar(200));
CREATE TABLE pref (id int, user_id int, pa_name varchar(200), pa_value varchar(200));
查询:
SELECT looker.username AS looker_name,
CONCAT("likes ",match_candidate.username,"\'s") AS candidate_name,
GROUP_CONCAT(" ", CONCAT(attr.a_value," ", attr.a_name)) AS attributes,
COUNT(attr.a_name) AS match_count
FROM pref
INNER JOIN attr ON attr.a_name = pref.pa_name
INNER JOIN user AS match_candidate ON match_candidate.id = attr.user_id
INNER JOIN user AS looker ON looker.id = pref.user_id
WHERE (pref.pa_value IS NULL OR pref.pa_value = attr.a_value) AND
pref.user_id = 101 AND looker.id <> match_candidate.id
GROUP BY candidate_name
ORDER BY match_count DESC;
解释:
我们获取 pref 表并将其与 attr 表连接以获取匹配的属性名称。attr 表可以与 user 表连接以获取候选人的名称。最后,我们可以连接到“观察者”,即希望他/她的偏好匹配的用户。这是通过与用户表的第二次连接完成的,但这次当然是在 pref.user_id 上。现在我们需要整理出不匹配的属性值。我们通过强制所有结果行具有 NULL 作为首选项属性值或该属性值等于匹配候选者的属性值(属性名称已与连接条件匹配)来做到这一点。要获得特定“查找者”的匹配候选者,我们还必须将其包含在WHERE
子句 ( pref.user_id = 101
) 中。这里101
只是一个用户 ID 的示例。最后,我们必须确保我们不会将观察者与他/她自己匹配(以防自己的偏好与自己的属性匹配)。SELECT
字段只是一些玩弄GROUP_CONCAT
和来组合我们使用的CONCAT
匹配属性GROUP BY
。
我做了一个sqlfiddle来玩。所以在我的示例中,我使用字符串作为属性名称和值。pref 值中的 NULL 意味着没有偏好意味着候选者的所有属性值都匹配。
旁注:您可以对 attr 和 pref 使用同一个表,并添加一个定义其类型的字段。
更新旁注:我现在更新了这个答案。如果您检查了早期版本的小提琴,请再次点击链接,因为它可能已经改变。