最初に、私の最初の回答を実装しないでください。代わりに、スキーマを変更または制約する必要があります。私の提案については、以下を参照してください。
あなたのスキーマを理解しているように、それは次のとおりです。
create table Friends (
user_Id1 int,
user_Id2 int,
status int);
フレンド関係があるときはいつでも、id の 1 つが位置 1 にあり、1 が位置 2 にあります。
ここで、私の ID が 1212 であると仮定すると、友人の ID のリストは次のようになります。
select user_Id1 as my_friends_userId
from Friends f
where f.user_Id2 = '1212'
and status = 1
union
select user_Id2 as my_friends_userId
from Friends f
where f.user_Id1 = '1212'
and status = 1;
私の友達の友達のIDのリストは次のとおりです。
select f1.user_id1 as friends_of_friends
from Friends f1
where f1.user_Id2 in (select user_Id1 as my_friends_userId
from Friends f
where f.user_Id2 = '1212'
and status = 1
union
select user_Id2 as my_friends_userId
from Friends f
where f.user_Id1 = '1212'
and status = 1)
union
select user_id2 as friends_of_friends
from Friends f1
where f1.user_Id1 in (
select user_Id1 as my_friends_userId
from Friends f
where f.user_Id2 = '1212'
and status = 1
union
select user_Id1 as my_friends_userId
from Friends f
where f.user_Id1 = '1212'
and status = 1);
そして、自分の友達とブロックした友達の除外を追加するには、次のようにします。
select f1.user_id1 as friends_of_friends
from Friends f1
where f1.user_Id2 in (select user_Id1 as my_friends_userId /* sub-query for friends of friends */
from Friends f
where f.user_Id2 = '1212'
and status = 1
union
select user_Id2 as my_friends_userId
from Friends f
where f.user_Id1 = '1212'
and status = 1)
and f1.user_id1 not in /* exclusion of my own friends */
(select user_Id1 as my_friends_userId
from Friends f
where f.user_Id2 = '1212'
and status = 1
union
select user_Id2 as my_friends_userId
from Friends f
where f.user_Id1 = '1212'
and status = 1
)
and f1.user_id1 != '1212' /* exclusion of myself. */
and f1.user_id1 not in (select user_Id1 as my_friends_userId /* exlusion of people I've blocked. */
from Friends f
where f.user_Id2 = '1212'
and status = 3
union
select user_Id2 as my_friends_userId
from Friends f
where f.user_Id1 = '1212'
and status = 3
)
union /* Now do it all over again for user_id2 */
select f2.user_id2 as friends_of_friends
from Friends f2
where f2.user_Id1 in (select user_Id1 as my_friends_userId
from Friends f
where f.user_Id2 = '1212'
and status = 1
union
select user_Id2 as my_friends_userId
from Friends f
where f.user_Id1 = '1212'
and status = 1)
and f2.user_id2 not in
(select user_Id1 as my_friends_userId
from Friends f
where f.user_Id2 = '1212'
and status = 1
union
select user_Id2 as my_friends_userId
from Friends f
where f.user_Id1 = '1212'
and status = 1
)
and f2.user_id2 != '1212'
and f2.user_id2 not in (select user_Id1 as my_friends_userId
from Friends f
where f.user_Id2 = '1212'
and status = 3
union
select user_Id2 as my_friends_userId
from Friends f
where f.user_Id1 = '1212'
and status = 3
)
これらの条件のそれぞれについて、初めてマークを付けました。これで、このために私がしなければならなかった の混乱を見ることができますunion
。(おそらくそうあるべきですunion distinct
)
group-concat を使用して in-clause を作成しないでください。ここの長さにもかかわらず、それはより高速です。
個々のピースが何をするのか尋ねることができます。しかし、繰り返しますが、私のアドバイスはDON'T DO THISです。これが、前もって優れたテーブル設計を行うと、物事がずっと簡単になる理由です。
参照用および結果表示用の SQL Fiddle: http://sqlfiddle.com/#!2/e6376/13
編集:このスキーマをどのように変更するかについて追加するだけです。
あなたのアプリで、友人間の関係が Google の関係 (非対称の関係が許可されている) なのか、Facebook の関係 (対称の関係のみが許可されている) なのかは不明です。
どちらの場合も、スキーマを次のように変更します。
create table Friends (
individual_userId int,
friend_userId int,
status int);
Google の場合、これで完了です。Facebook の場合、私はこの構造を使用しますが、関係ごとに 2 つの行がテーブルに入る必要があります。したがって、'1212' が '0415' を持つ Facebook の友達である場合、('1212', '0415') & ('0415','1212') の (individual_userid, friend_userId) 行があります。これが機能し、維持されていることを確認するには、挿入/削除用のストアド プロシージャを使用して、両方の行が追加および削除されていることを確認する必要があります。(更新はありません。これらは一意の ID です。)
これらの関係が維持され、関係を開始した友人が常に individual_userId に存在することが確実である場合、私の最終的なクエリは次のようになります。
select f1.friend_userId as friends_of_friends
from Friends f1
where f1.individual_userId in ( /* retrieve my friend list */
select friend_userId as my_friends_userId
from Friends f
where f.individual_userId = '1212'
and status = 1)
and f1.friend_userId not in ( /* exclusion of my own friends */
select friend_userId as my_friends_userId
from Friends f
where f.individual_userId = '1212'
and status = 1
)
and f1.friend_userId not in ( /* exlusion of people I have blocked. */
select friend_userId as my_friends_userId
from Friends f
where f.individual_userId = '1212'
and status = 3
)
and f1.friend_userId != '1212' /* exclusion of myself. */
これは対処がはるかに簡単です。これを一連の結合として書き直すこともできますが、最初のステップとして、このようなin
andnot in
句を使用する方が読みやすいと思います。
改訂された sqlfiddle: http://sqlfiddle.com/#!2/92ff2/1
(大規模なデータセットでテストする必要がありますが、私の直感では、結合が高速になると言っていますが、このようなコードの場合、最初に速度を最適化するよりも、学習/正しい答えを得ることの方が重要だと思います。)