改訂
このクエリは、friend テーブル内の行の "一方向" の関係を "双方向" の関係と見なします。つまり、友人関係:('abc','xyz')
は逆関係: と同等であると見なされます('xyz','abc')
。(注: 両方の行がテーブルに表示されないという保証はありません。そのため、注意する必要があります。UNION
演算子は便利に重複を排除します。)
このクエリは、次の仕様を満たす必要があります。
SELECT mf.id
, mf.name
FROM (
SELECT fr.user_id AS user_id
, fr.friend_id AS friend_id
FROM friend fr
JOIN users fru
ON fru.id = fr.user_id
WHERE fru.name IN ('abc','xyz')
UNION
SELECT fl.friend_id AS user_id
, fl.user_id AS friend_id
FROM friend fl
JOIN users flf
ON flf.id = fl.friend_id
WHERE flf.user IN ('abc','xyz')
) f
JOIN users mf
ON mf.id = f.friend_id
GROUP BY mf.id, mf.name
HAVING COUNT(1) = 2
ORDER BY mf.id, mf.name
SQLフィドルはこちらhttp://sqlfiddle.com/#!2/b23a5/2
これに到達する方法の詳細な説明を以下に示します。以下の元のクエリは、フレンド テーブルの行が「一方向」の関係を表していると想定していました。つまり、「'abc' ff 'xyz'
」は「 」を意味するものではありません'xyz' ff 'abc'
。しかし、OP からの追加のコメントは、そうではないことを示唆していました。
に一意の制約がある場合friend(user_id,friend_id)
、結果を取得する 1 つの方法は、各ユーザーのすべての友達を取得し、その友達の行数を取得することです。friend_id
カウントが 2 の場合、特定のユーザーが「abc」と「xyz」の両方に表示されることがわかります。
SELECT mf.id
, mf.name
FROM friend f
JOIN users uu
ON uu.id = f.user_id
JOIN users mf
ON mf.id = f.friend_id
WHERE uu.name IN ('abc','xyz')
GROUP BY mf.id, mf.name
HAVING COUNT(1) = 2
ORDER BY mf.id, mf.name
(このアプローチを拡張して、IN リストにさらに多くのユーザーを含め、COUNT(1) と比較する値を変更することにより、3 人以上のユーザーの共通の友人を見つけることもできます。
指定した結果セットを返すクエリはこれだけではありません。他にも入手方法があります。
同等の結果を得る別の方法:
SELECT u.id
, u.name
FROM ( SELECT f1.friend_id
FROM friend f1
JOIN users u1
ON u1.id = f1.user_id
WHERE u1.name = 'abc'
) t1
JOIN ( SELECT f2.friend_id
FROM friend f2
JOIN users u2
ON u2.id = f2.user_id
WHERE u2.name = 'xyz'
) t2
ON t2.friend_id = t1.friend_id
JOIN users u
ON u.id = t1.friend_id
ORDER BY u.id, u.name
ノート
これらのクエリは、ユーザー「abc」が「xyz」(WHERE 句で指定された 2 つのユーザー名) のフレンドであるかどうかをチェックしません。「abc」と「xyz」の共通の友人を見つけるだけです。
ファローアップ
上記のクエリは、指定された要件、および質問で提供されているすべての例とテスト ケースを満たしています。
このリレーションシップ テーブルの行を、単なる「一方向」のリレーションシップではなく、「双方向」のリレーションシップと見なしたいように思えます。友人関係 ('abc','xyz') を ('xyz','abc') と同等のものと見なしたいようです。
それを取得するには、クエリで逆行を作成するだけで済みます。これにより、クエリが簡単になります。これらの行 ('abc','xyz') と ('xyz','abc') の両方が既に存在する場合、それらを反転するときにそれらの重複を作成しないように注意する必要があります。
逆行を作成するには、次のようなクエリを使用できます。(users テーブルへの JOIN がなく、id 値のみを使用する場合は、これを見るのが簡単です。
SELECT fr.user_id
, fr.friend_id
FROM friend fr
WHERE fr.user_id IN (1,2)
UNION
SELECT fl.friend_id AS user_id
, fl.user_id AS friend_id
FROM friend fl
WHERE fl.friend_id IN (1,2)
user_id および friend_id テーブルに述語を含めない方が簡単ですが、これを実現するには非常に大きな (そして高価な) 行セットになる可能性があります。