0

SQLテーブルから友達の友達(私の友達ではない)を見つける必要がありますが、問題は、すでに私の友達またはブロックされている友達であるユーザーを除外することに固執していることです。

ここにクエリがあります

SELECT 
  IF(Friends.User_Id1 IN (1111,2222),
       Friends.User_Id2,
       Friends.User_Id1) AS 'Friend_Id', 
  CONCAT(User_FirstName," ",User_LastName) AS User_FirstName,
  User_ProfilePic 
FROM Users
JOIN Friends ON
  IF(Friends.User_Id1 IN (1111,2222),
       Friends.User_Id2,
       Friends.User_Id1) = Users.User_Id
WHERE 
 (Friends.User_Id2 IN (1111,2222) OR Friends.User_Id1 IN (1111,2222)) AND 
 (Friends.User_Id2 != MY_ID AND Friends.User_Id1 != MY_ID AND Friends.Status = 1)
LIMIT 10;

上記の場合、1111 と 2222 が私の友達で、すべての友達を取得しようとしていますが、問題はありませんが、私が欲しいのは:

  1. 1111 と 2222 のいずれかと友達であり、リストに表示されているユーザーは既に私の友達です。彼らはすでに別の友達リストに入っているので、ここには入れたくありません。
  2. 私がブロックしたユーザー、つまり MY_ID と friends_friend_id = 3 の Friends.Status です。このケースにも 1 つあり、ユーザー ID 3333 は 2222 の友人であり、私は彼を既にブロックしていましたが、彼はリストに存在しています。

IN(1111,2222)友達の数が間違いなく増えるため、経由での検索が将来的に何らかの問題につながる場合は、私を案内してください. group_concat上記のクエリの前に使用する友人のカンマ区切りのリストがあります。これはすべてストアド プロシージャにあります。

問題を明確に説明したことを願っています。

4

2 に答える 2

3

最初に、私の最初の回答を実装しないでください。代わりに、スキーマを変更または制約する必要があります。私の提案については、以下を参照してください。

あなたのスキーマを理解しているように、それは次のとおりです。

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. */

これは対処がはるかに簡単です。これを一連の結合として書き直すこともできますが、最初のステップとして、このようなinandnot in句を使用する方が読みやすいと思います。

改訂された sqlfiddle: http://sqlfiddle.com/#!2/92ff2/1

(大規模なデータセットでテストする必要がありますが、私の直感では、結合が高速になると言っていますが、このようなコードの場合、最初に速度を最適化するよりも、学習/正しい答えを得ることの方が重要だと思います。)

于 2012-03-05T17:08:53.607 に答える
1

マイクの答えが解釈するように、そして私もそうですが、あなたのテーブル構造は次のようなものです。

create table Friends (
   user_Id1 int,
   user_Id2 int,
   status int);

あなたが望んでいるように見えるのは、あなたの友人であるか、あなたの友人と直接関連している友人のいずれかである友人の明確なリストです(つまり、あなたから1度の隔たり)。それでは、このシナリオに取り掛かりましょう。

あなたは個人ID1111で、友達2222と3333がいます。

人2222には、1111(あなた)、3333(あなたの他の友人)、および4444(新しい人)の友人がいます。

人3333には、1111(あなた)、4444(人3333の友人と同じ-偶然)、および5555の友人がいます。

さて、2次の隔たり(あなたが探しているものではありません)は、その人4444が6666、7777、8888の友人を持っているということです。あなたはこれらの他の人(6666、7777、8888)を気にしません

あなたはあなたではない友達の全リストを探していて、友達2222、3333、4444、5555を見たいだけです。

私はあなたの友達だけのリストから始めて、彼らの友達を獲得するための基礎としてそれを使用します。最も内側のクエリは、(あなたの友達が誰であるかをハードコーディングせずに)あなたの明確な友達を取得することです。次に、そこから、すべての友達を取得します。それらがたまたま類似している場合、「DISTINCT」はそれを除外します。それらが選択されたら、「AllFriends」を代表する直接の友達のUNIONを入手してください。IF()を使用することにより、「他の」人が参加資格に基づいている人が必要です。参加した人がポジション1にいる場合は、他の人が必要です。その逆も同様です。友達の明確なリストを作成したら、それをユーザーテーブルに参加させて、名前、写真、その他のプロフィール情報を取得します。

select
      AllFriends.FinalFriendID,
      CONCAT(U.User_FirstName, " ", U.User_LastName) AS User_FirstName, 
      U.User_ProfilePic 
   from
       ( select DISTINCT
               IF( F2.User_ID1 = YourDirectFriends.PrimaryFriend, F2.User_ID2, F2.User_ID1 ) 
                  as FinalFriendID
            from
               ( select DISTINCT 
                        IF( F.User_ID1 = YourID, F.User_ID2, F.User_ID1 ) as PrimaryFriendID
                   from
                      Friends F
                   where
                         F.user_ID1 = YourID
                      OR F.User_ID2 = YourID ) YourDirectFriends
                JOIN Friends F2
                   ON    YourDirectFriends.PrimaryFriendID = F2.User_ID1
                      OR YourDirectFriends.PrimaryFriendID = F2.User_ID2
         UNION 
         select DISTINCT
               IF( F.User_ID1 = YourID, F.User_ID2, F.User_ID1 ) as FinalFriendID
            from
               Friends F
            where
                  F.user_ID1 = YourID
               OR F.User_ID2 = YourID ) ) as AllFriends
        JOIN Users U
           on AllFriends.FinalFriendID = U.User_ID

そうそう、該当する場合は「ステータス」の修飾子と制限要件を追加します。

于 2012-03-05T17:46:17.713 に答える