У меня есть эта проблема. Данный a users
таблица, которая состоит из имени пользователя пользователей в социальной сети и friends
таблица, которые содержат имя пользователя и friendname пользователя как ниже...
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';
Это в основном проверяет, если пользователь, если друг моего друга т.е. просто возвращает пустой указатель если ложь.
Попытка выяснить, как я могу расшириться для прохождения через всей моей сети друга. Я имею в виду не только друга своего друга.
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
, который будет использовать индекс и будет намного более эффективным, особенно если вы ограничите уровень некоторым разумным значением.