"друзья друзей" как sql query - PullRequest
       6

"друзья друзей" как sql query

6 голосов
/ 26 апреля 2011

Я создаю приложение, в котором пользователи могут соединяться друг с другом (что-то вроде друзей в любой социальной сети).

Я сохраняю эти соединения в таблице со следующей структурой:

id_user1 | id_user2 | is_accepted | is_blocked | created_at

Соединения между пользователями являются двунаправленными, поэтому при подключении двух пользователей в таблице остается только одна запись.Не имеет значения, если user_id находится в id_user1 или id_user2 столбце.

Теперь мне нужно написать SQL-запрос, чтобы получить «друзей друзей» определенного пользователя, которые еще не являются друзьямипользователь.Также пользователь должен быть принят и не заблокирован.

В резюме вот шаги, которые мне нужно выполнить.

  1. Найти все идентификаторы пользователей, связанные с пользователем Iхочу (id_user1 = current_user или id_user2 = current_user и is_accepted и !blocked)

  2. foreach из возвращенных user_ids -> получить всех связанных пользователей (игнорировать ассоциации с текущим пользователем)(убедитесь, что это также accepted и !blocked).

Как я могу выполнить такой запрос?.

Спасибо за вашу помощь.

Ответы [ 4 ]

4 голосов
/ 27 апреля 2011

По причинам, упомянутым другими, и потому что я видел, как это работает лучше в других системах, я бы пошел с рядом для каждого направления.

primary_user_id | related_user_id | is_accepted | is_blocked | created_at

Затем вы также можете создать кластеризованный индекс для user_id, который более чем сместит накладные расходы на удвоение количества строк.

Ваш первый запрос будет выглядеть примерно так:

SELECT f1.related_user_id
FROM friends f1
WHERE f1.primary_user_id = @current_user
AND f1.is_accepted = 1 AND f1.is_blocked = 0
AND EXISTS (
  SELECT *
  FROM friends f2
  WHERE f1.related_user_id = f2.primary_user_id
  AND f2.related_user_id = @current_user
  AND f2.is_accepted = 1 AND f2.is_blocked = 0

Не уверен, что вы можете выполнять табличные функции в MySql. Если это так, то оберните это в функцию, чтобы упростить ваш второй запрос.

2 голосов
/ 26 апреля 2011
SELECT  CASE f2.id_user1 WHEN CASE f1.id_user1 WHEN $user THEN f1.id_user2 ELSE f1.id_user1 END THEN f2.id_user2 ELSE f2.id_user1 END
FROM    friends f1
JOIN    friends f2
ON      f2.id_user1 = CASE f1.id_user1 WHEN $user THEN f1.id_user2 ELSE f1.id_user1 END
        OR f2.id_user2 = CASE f1.id_user1 WHEN $user THEN f1.id_user2 ELSE f1.id_user1 END
WHERE   (f1.id_user1 = $user OR f1.id_user = $user)
        AND f1.is_accepted = 1
        AND f2.is_accepted = 1
        AND f1.is_blocked = 0
        AND f2.is_blocked = 0
        AND NOT (f1.id_user1, f1.id_user2) = (f2.id_user1, f2.id_user2)

Обратите внимание, что лучше хранить пользователей с наименьшим первым, с наибольшим вторым.В этом случае запрос будет более простым.

1 голос
/ 26 апреля 2011

При работе с таблицей «одна запись на дружбу» все запросы будут раздутыми и подверженными ошибкам, потому что вам придется часто писать «id_user1 = ... или id_user2 = ...».Я бы создал представление

CREATE VIEW bidifreinds (id_user1, id_user2, is_accepted, is_blocked, created_at) AS
SELECT id_user1, id_user2, is_accepted, is_blocked, created_at FROM friends
UNION
SELECT id_user2, id_user1, is_accepted, is_blocked, created_at FROM friends

Это сделает жизнь намного проще.

Тогда вы сможете написать

SELECT f1.id_user1, f2.id_user2
FROM friends f1, friends f2
WHERE f2.id_user1 = f1.id_user2
  AND f1.is_accepted
  AND NOT f1.is_blocked
  AND f2.is_accepted
  AND NOT f2.is_blocked

И я надеюсь, что вы не используете MySQLпотому что MySQL очень медленно запрашивает представления.

0 голосов
/ 26 апреля 2011
select id_user1 from friends where is_accepted = 1 and is_blocked = 0 and id_user2 in    
(select id_user1 from friends where is_accepted = 1 and is_blocked = 0 and id_user2 = :a_user: 
    union 
    select id_user2 from friends where is_accepted = 1 and is_blocked = 0 and id_user1 = :a_user:)
union
select id_user2 from friends where is_accepted = 1 and is_blocked = 0 and id_user1 in
(select id_user1 from friends where is_accepted = 1 and is_blocked = 0 and id_user2 = :a_user: 
    union 
    select id_user2 from friends where is_accepted = 1 and is_blocked = 0 and id_user1 = :a_user:)

Вы можете добавить whereClause для исключения: a_user: из набора результатов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...