简体   繁体   中英

SELECT random rows with a condition from 2 different table in PostgreSQL

I have 2 different tables; First one is for 'Users' and the second is 'Relations'.

Users has 'username' column and Relations has 'username' and 'friendsname'. For every friendship instance 2 rows are inserted to this table. eg If John adds Janna as friend this means John is added by Janna likewise.

I am trying to find out something like friend suggestion as in Facebook or Twitter. Random user will be selected from Users table and that will be checked in Relations table whether they are friend or not. And this will be done continously until find 5 successful non-friend match and return all at once(if it's possible).

I can do this with Select all rows and iterate/search in the server by myself but i don't think this is something PostgreSQL can not handle. Also my way seems too expensive task for this level "relatively" trivial feature.

Is there any easy and elegant way to handle this task?

Thanks in advance and have a good sunday/Christmas night.

UPDATE: I am appending some mock data, sorry for the delay:

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');

Without using cursors, one thing you can do is find all possible friendships with a cross join, then remove the existing ones and finally pick 5 random rows of the resulting set, something like this (disclaimer, not tested):

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

the use of greatest and least is to ensure that we don't repeat the same friendship. Also the "where a.username <> b.username" in the cross join is to not introduce "self friendships".

The next select will return exactly 5 friendsname which are not friends of John (in this case). The second filter must be applied, to not return John for John.

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

To add a random factor to this you can modify the select this way:

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

Notice, that the random() ordering is a heavy operation, because it does a scan.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM