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 テーブルと結合して、候補の名前を取得できます。最後に、「見ている人」、つまり自分の好みを一致させたいユーザーに接続できます。これは、ユーザー テーブルとの 2 番目の結合によって行われますが、今回はもちろん pref.user_id で行われます。ここで、一致しない属性値を整理する必要があります。これを行うには、すべての結果行が設定属性値として NULL を持つか、その属性値が一致する候補の属性値と等しい (結合条件によって属性名が既に一致している) ことを強制します。特定の「looker」の一致する候補を取得するには、これもWHERE
句 ( pref.user_id = 101
) に含める必要があります。ここ101
は、1 人のユーザー ID の単なる例です。最後に、見ている人を自分自身と一致させないようにする必要があります (自分の好みが自分の属性と一致する場合)。フィールドは、
使用する一致する属性を組み合わせるために、SELECT
少しいじっています。GROUP_CONCAT
CONCAT
GROUP BY
私は遊ぶためにsqlfiddleを作りました。そのため、私の例では、文字列を属性名と値として使用しました。pref 値の NULL は、優先度がないことを意味し、候補のすべての属性値が一致することを意味します。
補足: attr と pref に同じテーブルを使用し、そのタイプを定義するフィールドを追加できます。
追記:この回答をかなり更新しました。以前のバージョンのフィドルをチェックアウトした場合は、リンクが変更されている可能性があるため、もう一度リンクをたどってください。