我想根据共同好友的数量来推荐用户,就像这样。
Suggested Friends:
Amy Adams (42 mutual friends)
Brian Bautista (21 mutual friends)
Chris Cross (6 mutual friends)
注意:我已经阅读了几个类似的帖子和答案,但是解决方案非常依赖于表结构,而且我无法解决我们的 Friendships 表的设置方式。见下文。
USERS TABLE
id
name
FRIENDSHIPS TABLE
id
initiatingUserId (FK to Users Table)
targetUserId (FK to Users Table)
status ('active', 'denied', 'pending')
如您所见,每个好友都占一行。发送好友请求的用户为发起用户,接受好友请求的用户为目标用户。这两列都有索引。
如何有效地获取当前用户还不是好友的用户列表,以及他们共同的好友数量?
这是一个示例数据集,根据@Strawberry 的要求...
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`firstName` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`lastName` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `friends` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`initiatingUserId` int(10) unsigned NOT NULL,
`targetUserId` int(10) unsigned NOT NULL,
`status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
PRIMARY KEY (`id`),
UNIQUE KEY `compositeInitiatingUserIdTargetUserIdIndex` (`initiatingUserId`,`targetUserId`),
KEY `friends_initiating_user_id` (`initiatingUserId`),
KEY `friends_target_user_id` (`targetUserId`),
CONSTRAINT `friends_ibfk_1` FOREIGN KEY (`initiatingUserId`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `friends_ibfk_2` FOREIGN KEY (`targetUserId`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO `users` (`id`, `firstName`, `lastName`) VALUES
(1, 'A', 'A'),
(2, 'B', 'B'),
(3, 'C', 'C'),
(4, 'D', 'D'),
(5, 'E', 'E'),
(6, 'F', 'F'),
(7, 'G', 'G');
INSERT INTO `friends` (`id`, `initiatingUserId`, `targetUserId`, `status`) VALUES
( 1, 1, 2, 'active'),
( 2, 1, 3, 'active'),
( 3, 4, 1, 'active'),
( 4, 2, 3, 'active'),
( 5, 2, 4, 'active'),
( 6, 5, 2, 'active'),
( 7, 2, 6, 'active'),
( 8, 2, 7, 'active'),
( 9, 3, 5, 'active'),
(10, 6, 3, 'active'),
(11, 4, 5, 'active'),
(12, 6, 7, 'active');
假设我们为用户 ID 1 提取建议用户,所需的输出是:
+----------------+-------------+
| strangerUserId | mutualCount |
+----------------+-------------+
| 5 | 2 |
+----------------+-------------+
| 6 | 2 |
+----------------+-------------+
| 4 | 1 |
+----------------+-------------+
| 7 | 1 |
+----------------+-------------+
我确实有一个解决方案,但它假设我们已经拥有用户朋友的 ID,我不确定它的运行速度:
select
count(*) as 'mutualCount',
case when f.initiatingUserId in (2, 3) then f.targetUserId else f.initiatingUserId end as strangerUserId
from friends f
join users initiating on f.initiatingUserId=initiating.id
join users target on targetUserId=target.id
and (
(
initiatingUserId in (2, 3)
and targetUserId not in (1, 2, 3)
)
or
(
targetUserId in (2, 3)
and initiatingUserId not in (1, 2, 3)
)
)
group by strangerUserId
order by mutualCount desc;
答案 0 :(得分:0)
我想出了这个快速(仅限 MySql)解决方案。
<块引用>注意:此查询未针对您现有的索引进行优化,总体而言,在较大的数据集上可能会出现性能问题。如果您需要添加一些额外的条件,也很难修改查询。我会使用查询的各个部分先拉朋友列表,然后是非朋友列表,然后运行单独的查询来确定共同朋友的数量。
因此,这里是拉取 userId '1' 的共同好友计数的非好友列表的查询。您必须使用动态变量替换查询中的“1”硬编码 userId 值:
SELECT COUNT(fnf.friendOfNonFriendId) AS mutualFriednCount, fnf.nonFriendId
FROM
-- Subquery to pull the list of non-friends and their friends
(SELECT
IF(f.initiatingUserId = nf.nonFriendId, f.targetUserId, f.initiatingUserId) AS friendOfNonFriendId,
nf.nonFriendId
FROM friends f
JOIN
(SELECT u.id AS nonFriendId
FROM users u
LEFT JOIN (
SELECT IF(f.initiatingUserId = 1, f.targetUserId, f.initiatingUserId) AS friendId
FROM friends f
WHERE f.status = 'active' AND (f.initiatingUserId = 1 OR f.targetUserId = 1)) f
ON f.friendId = u.id
WHERE f.friendId IS NULL AND u.id != 1) nf ON nf.nonFriendId = f.initiatingUserId
OR nf.nonFriendId = f.targetUserId) fnf
-- Joining the subquery with the list of friends of a user with ID 1 to filter the above subquery to only mutual friends
JOIN
(SELECT IF(f.initiatingUserId = 1, f.targetUserId, f.initiatingUserId) AS friendId
FROM friends f
WHERE f.status = 'active' AND (f.initiatingUserId = 1 OR f.targetUserId = 1)) f
ON f.friendId = fnf.friendOfNonFriendId
GROUP BY fnf.nonFriendId