【问题标题】:SELECT random rows with a condition from 2 different table in PostgreSQL从 PostgreSQL 中的 2 个不同表中选择具有条件的随机行
【发布时间】:2022-01-26 00:07:56
【问题描述】:

我有 2 张不同的桌子;第一个是“用户”,第二个是“关系”。

Users 有 'username' 列,Relations 有 'username' 和 'friendsname'。 对于每个友谊实例,都会在该表中插入 2 行。例如,如果 John 将 Janna 添加为朋友,这意味着 John 也被 Janna 添加了。

我正在尝试在 Facebook 或 Twitter 中查找朋友建议之类的内容。 随机用户将从 Users 表中选择,并将在 Relations 表中检查他们是否为朋友。这将持续进行,直到找到 5 个成功的非好友匹配并一次全部返回(如果可能)。

我可以自己选择所有行并在服务器中迭代/搜索,但我不认为这是 PostgreSQL 无法处理的事情。对于这个级别“相对”微不足道的功能,我的方式似乎过于昂贵。

有什么简单优雅的方式来处理这个任务吗?

提前致谢,祝您周日/圣诞夜愉快。

更新: 我正在附加一些模拟数据,抱歉耽搁了:

create table relations (
    rel_id bigserial primary key not null ,
    username VARCHAR(50) not null ,
    friendname VARCHAR(50) not null ,
    since DATE not null
);
insert into relations (rel_id, username, friendname, since) values (1,'user1', 'user2', '06/01/2021');
insert into relations (rel_id, username, friendname, since) values (2,'user2', 'user1', '06/01/2021');
insert into relations (rel_id, username, friendname, since) values (3,'user1', 'user3', '16/10/2021');
insert into relations (rel_id, username, friendname, since) values (4,'user3', 'user1', '16/10/2021');
insert into relations (rel_id, username, friendname, since) values (5,'user3', 'user5', '16/01/2020');
insert into relations (rel_id, username, friendname, since) values (6,'user5', 'user3', '16/01/2020');

create table Users (
                           user_id bigserial primary key not null ,
                           username VARCHAR(50) not null

);
insert into Users (user_id, username) values (1,'user1');
insert into Users (user_id, username) values (2,'user2');
insert into Users (user_id, username) values (3,'user3');
insert into Users (user_id, username) values (4,'user4');
insert into Users (user_id, username) values (5,'user5');
insert into Users (user_id, username) values (6,'user6');
insert into Users (user_id, username) values (7,'user7');

正如预期的结果查询可能会建议 User1 的 User4-5-6 和 7 作为朋友。因为根据表 user1 仅与用户 2 和 user3 是朋友。考虑到他不能和自己成为朋友,我们也不希望在结果中看到 user1。

【问题讨论】:

  • 请贴出上述数据的预期结果。
  • 我希望现在已经足够了。如有误解,请随时提醒。

标签: sql postgresql


【解决方案1】:

下一个选择将准确返回 5 个不是 John 的朋友的朋友名(在这种情况下)。必须应用第二个过滤器,以不为 John 返回 John。

SELECT DISTINCT(friendsname) FROM relations 
WHERE username <> 'John' AND friendsname <> 'John'
LIMIT 5

要为此添加随机因素,您可以通过以下方式修改选择:

SELECT * FROM (
SELECT DISTINCT(friendsname) FROM relations 
WHERE username <> 'John' AND friendsname <> 'John'
) as s
ORDER BY random()
LIMIT 5

请注意,random() 排序是一项繁重的操作,因为它会进行扫描。

更新 要从用户表而不是关系表中进行选择,查询可能如下所示:

SELECT * FROM users AS u
WHERE u.username NOT IN (SELECT friendname FROM relations WHERE username = 'user3') AND u.username <> 'user3'
ORDER BY random()
LIMIT 3

有两个选择没有加入,只是过滤,它可能表现得很好。我没有在更大的桌子上测试它。

【讨论】:

  • 感谢您的回复,但我需要使用“用户”表进行随机选择。关系表仅用于检查。
  • @Alteran 我已更新查询以使用“用户”表进行随机选择
【解决方案2】:

不使用游标,你可以做的一件事是通过交叉连接找到所有可能的友谊,然后删除现有的,最后从结果集中随机选择 5 行,像这样

   select usernm, friend 
from
(
select distinct greatest(a.username,b.username) usernm,least(a.username,b.username) friend 
from users a
cross join users b
where a.username <> b.username
except
select distinct greatest(username, friendname) usernm, least(username, friendname) friend
from relations
) x
order by random() limit 5

使用最大和最小是为了确保我们不会重复相同的友谊。交叉连接中的“where a.username b.username”也是为了不引入“自我友谊”。

db 小提琴: https://www.db-fiddle.com/f/gpYMWHa2dK7BcZpxjHqwGa/0

【讨论】:

  • 不知何故我无法让它执行。考虑到更新,您能否再次检查。
  • 感谢您的示例,我修复了查询,请查看 db fiddle 进行测试,如果您需要,请告诉我们
  • 我不知道如何,但它似乎工作。虽然它只建议一种方式。例如,将 User5 建议给 user2,而不是其他方式。给定/特定用户名的结果也是预期的。我需要向“用户 3”推荐一些朋友。你能解释一下这里的最大或最小工作吗?据我所知,它用于可比较的整数或时间。由于我无法理解此代码背后的逻辑,因此我无法为某些用户提取解释它。
  • 我实际上是通过使用最大和最小来避免这两种方法,这对于字符串按字母顺序排列。最大(a, b) = b 和最小(a, b) = a。
  • 如果我们不这样做,查询可能会以两种方式返回相同的友谊,这没有意义,因为我们知道如果它不存在于一种方式,它就不会或者反过来。拥有 5 种新朋友后,您可以双向推荐它们。
猜你喜欢
  • 2013-10-30
  • 1970-01-01
  • 1970-01-01
  • 2012-10-13
  • 1970-01-01
  • 2021-11-12
  • 1970-01-01
  • 2022-01-17
  • 2013-11-18
相关资源
最近更新 更多