Oracle SQL, как написать SQL-оператор, который проверяет, есть ли пользователь в моей сети (то есть друзья или друзья друзей) - PullRequest
8 голосов
/ 19 февраля 2010

У меня есть эта проблема. Имеется таблица users, которая состоит из имени пользователя в социальной сети и таблицы friends, которая содержит имя пользователя и имя пользователя, как показано ниже ...

username friendname

John        Thomas
Chris       James

... Я пытаюсь написать оператор SQL, который будет, если пользователь находится в моей сети. Другими словами этот пользователь друг или друг друзей?

Я танцевал вокруг этой проблемы и мог только придумать этот запрос:

SELECT f2.username, f2.friendname 
FROM friends f2 
WHERE f2.username IN (
      SELECT f1.friendname 
      FROM friends f1 
      WHERE f1.username = 'Thomas') 
AND f2.friendname <> 'user1' 
AND f2.friendname = 'user2';    

По сути, он проверяет, является ли пользователь другом моего друга, т. Е. Просто возвращает ноль, если ложь.

Попытка выяснить, как я могу расшириться, чтобы пройти через всю мою сеть друзей. Я имею в виду не просто друг моего друга.

Ответы [ 2 ]

5 голосов
/ 19 февраля 2010
SELECT  *
FROM    (
        SELECT  username
        FROM    friends
        START WITH
                username = 'myname'
        CONNECT BY
                friendname = PRIOR username
                AND level <= 3
        )
WHERE   username = 'friendname'
        AND rownum = 1

Обновите уровень по мере необходимости: вы можете искать друзей третьего уровня и т. Д.

Если отношения дружбы симметричны, вы должны сделать следующий запрос:

WITH    q AS
        (
        SELECT  username, friendname
        FROM    friends
        UNION ALL
        SELECT  friendname, username
        FROM    friends
        ),
        f AS
        (
        SELECT  friendname, level
        FROM    q
        START WITH
                username = 'Thomas'
        CONNECT BY NOCYCLE
                username = PRIOR friendname
        )
SELECT  *
FROM    f
WHERE   friendname = 'Jo'
        AND rownum = 1

Этот запрос может быть выполнен намного быстрее, если вы денормализуете свою таблицу: сохраняйте две записи на дружбу, например:

CREATE TABLE dual_friends (orestes NOT NULL, pylades NOT NULL, CONSTRAINT pk_dualfriends_op PRIMARY KEY (orestes, pylades)) ORGANIZATION INDEX
AS
SELECT  username, friendname
FROM    friends
UNION ALL
SELECT  friendname, username
        FROM    friends

Тогда вы можете просто заменить CTE выше на dual_friends:

WITH    f AS
        (
        SELECT  pylades, level
        FROM    dual_friends
        START WITH
                orestes  = 'Thomas'
        CONNECT BY NOCYCLE
                orestes = PRIOR pylades
                AND level <= 3
        )
SELECT  *
FROM    f
WHERE   pylades = 'Jo'
        AND rownum = 1

, который будет использовать индекс и будет гораздо более эффективным, особенно если вы ограничите уровень каким-либо разумным значением.

1 голос
/ 19 февраля 2010

Вы можете использовать connect by

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