Recommended friends one degree separation - sql

I have a table structure
(Xref) IDUser1 and IDUser2 and table IDUser, UserName.
Xref table of friends is bidirectional (1, 2) and (2, 1).
What is the best way to get friends of my friends (that is not my friend) who has at least one of my friend as the mutual friend?

SELECT DISTINCT friendL2.*
FROM [User] me
LEFT JOIN Xref relation1
ON relation1.IDUser1 = me.IDUser
LEFT JOIN Xref relation2
ON relation2.IDUser2 = me.IDUser
INNER JOIN [User] friendL1
ON relation1.IDUser2 = friendL1.IDUser
OR relation2.IDUser1 = friendL1.IDUser
LEFT JOIN Xref relation3
ON relation3.IDUser1 = friendL1.IDUser
LEFT JOIN Xref relation4
ON relation4.IDUser2 = friendL1.IDUser
INNER JOIN [User] friendL2
ON relation3.IDUser2 = friendL2.IDUser
OR relation4.IDUser1 = friendL2.IDUser
WHERE friendL2.IDUser != friendL1.IDUser -- This will tell that this guy can't be your friend
AND me.IDUser = #me
Just replace #me with your current id
You can also modify the last line by the next one if you wish to filter by UserName
AND me.Username = #me

IDUser1 | IDUser2
1 | 2
2 | 1
|
Sory for incomplete question, this is my table Xref, and I have another table User (IDUser, UserName). User with IDUser 1 is friend of IDUser 2 and vice versa.
I have this query for friends of friends
SELECT DISTINCT u.IDUser1,p.Username FROM Xref AS u
inner join User as p on p.IDuser1=u.IDUser1
INNER JOIN (SELECT g.IDUser2 FROM Xref AS g WHERE g.IDUser1 = #IDUsern1
) AS f ON
(u.IDUser2=f.IDUser2 AND u.IDUser1 <> #IDUser2)
WHERE U.IDUser1 NOT IN (Select Xref.IDUser2 from xref
inner join Users on Users.IDUser1=Xref.IDuser1
where xref.IDUser2=#IDUser)
this works great, but I need users who are not my friends but have at least one of my friend as mutual friend

Related

Dynamically select the table to join in Postgres with case statements

My notifications table has a column called action_id and trigger_type. I want to INNER JOIN action_id with another table (Like users or posts) depending on the trigger_type. I wrote the following query but it throws an error.
Table structure
users
display_name
username
id
John
Doe
1
Larry
Doe
2
posts
post_title
post_body
id
user_id
Hello
Hello world
1
2
comments
comment_text
post_id
id
user_id
Hello
1
1
1
notifications
read
trigger_id
id
target_id
action_id
trigger_type
false
1
1
2
1
0
false
1
2
2
1
1
trigger_type = 0 means its a like 1 means its a comment
SELECT notifications.*, users.display_name, users.username, users.profile_pic, posts.title
FROM notifications
INNER JOIN users ON users.id = notifications.trigger_id
(
CASE notifications.trigger_type
WHEN 0 THEN INNER JOIN users ON users.id = notifications.action_id
WHEN 1 THEN INNER JOIN posts ON posts.id = notifications.trigger_id
)
You cannot conditionally join like that. Instead, use left join like this:
SELECT n.*,
-- whatever columns you want from the trigger user go here
un.display_name, un.username, un.profile_pic, p.title
FROM notifications n JOIN
users u
ON u.id = n.trigger_id LEFT JOIN
users un
ON un.id = n.action_id AND n.trigger_type = 0 LEFT JOIN
posts p
ON p.id = n.action_id AND n.trigger_type = 1;

Retrieving data information from another table Left Join SQL

User A liked User C Post
Table Users:
id_user name
1 A
2 B
3 C
Table Posts:
id_post post user_post
15 hi 2
19 how are you 3
23 hello 2
Table likes:
id_like id_liker liked_post_id liked_user_id
45 1 19 3
From table likes I show results
User 1 liked post 19 that belongs to user 3
SELECT liked_user_id,_liker,liked_post_id
FROM likes
WHERE liked_user_id = 3
How do I show the next
User A liked User C Post which is "How are you"
I tried but I get an error:
SELECT name
FROM users as u
LEFT JOIN
(SELECT id_post, post, user_post
FROM posts as p
LEFT JOIN
(SELECT liked_user_id, id_liker, liked_post_id
FROM likes
WHERE liked_user_id = 3
) AS b ON u.id_post = b.liked_post_id
) AS c ON u.id_user = c.user_post
SELECT u.`name`, v.`name`, p.post
FROM likes
LEFT JOIN Users as u on likes.id_liker=u.id_user
LEFT JOIN Users as v on likes.liked_user_id=v.id_user
LEFT JOIN Posts as p on likes.liked_post_id=p.id_post
WHERE likes.liked_user_id=3
SELECT liker.name AS liker_name
,liked.name AS liked_name
,p.post
FROM likes AS l
LEFT JOIN Users AS liker ON liker.id_user = l.id_liker
LEFT JOIN Users AS liked ON liked.id_user = l.liked_user_id
LEFT JOIN Posts AS p ON p.id_post = l.liked_post_id
AND l.liked_user_id = p.user_post
WHERE l.liked_user_id = 3

Query sql to get the first occurrence in a many to many relationship

I have a User table that has a many to many relationship with Areas. This relationship is stored in the Rel_User_area table. I want to show the user name and the first area that appears in the list of areas.
Ex.
User
id | Name
1 | Peter
2 | Joe
Area
id | Name
1 | Area A
2 | Area B
3 | Area C
Rel_User_area
iduser | idarea
1 | 1
1 | 3
2 | 3
The result I want:
User Name | Area
Peter |Area A
Joe |Area C
Using the minimum area id to determine "First" you could use a correlated subquery (A subquery that refers to field(s) in the main query to filter results):
SELECT user.name, area.name
FROM
user
INNER JOIN Rel_User_Area RUA ON user.id = RUA.iduser
INNER JOIN Area ON RUA.idarea = area.id
WHERE area.id = (SELECT min(idarea) FROM Rel_User_Area WHERE iduser = RUA.iduser)
There's other ways of doing this that may be RDBMS specific. Like in Teradata I would use a QUALIFY clause that doesn't exist in MySQL, SQL Server, Oracle, Postgres, etc.. Regardless of the RDBMS the above should work.
SELECT user.name, area.name
FROM
user
INNER JOIN Rel_User_Area RUA ON user.id = RUA.iduser
INNER JOIN Area ON RUA.idarea = area.id
QUALIFY ROW_NUMBER() OVER (PARTITION BY user.id ORDER BY area.id ASC) = 1;
using the ID from Rel_user_Area you mentioned in comments...
This should be pretty platform independent.
SELECT U.name as Username, A.Name as Area
FROM (SELECT min(ID) minID, IDUser, IDarea
FROM Rel_user_Area
GROUP BY IDUser, IDarea) UA
INNER JOIN User U
on U.ID = UA.IDuser
INNER JOIN Area A
on A.ID = UA.IDArea
If Cross apply and top work (could substitute limit 1 vs top if Postgresql or mySQL)
This will run the cross apply SQL once for each record in user; thus you get the most recent rel_user_Area ID per user.
SELECT U.name as Username, A.Name as Area
FROM User U
on U.ID = UA.IDuser
CROSS APPLY (SELECT TOP 1 IDUser, IDArea
FROM Rel_user_Area z
WHERE Z.IDUSER = U.ID
ORDER BY ID ASC) UA
INNER JOIN Area A
on A.ID = UA.IDArea

Inserting into a join table if value does not already exist

I have 2 tables - user, region and a join/connecting table that is used to join both user and region.
I need to insert into the join table all the region values that the user does not already have and i am unsure how to go about this.
I have attempted this numerous ways but i am not entirely sure how to only place the values that do already exist within the table into the join table. Does anyone have any ideas or suggestions?
SELECT
CONVERT( CONCAT('INSERT INTO user_region VALUES(',
user.id,
',',
reg.id,
');') USING UTF8)
FROM
user user
JOIN
user_region user_reg ON user_reg.id = user.id
JOIN
region reg ON reg.id = user_reg.id
WHERE
(user.email_address LIKE '%gmail%'
OR user.email_address LIKE '%hotmail%');
User Table User Region Region
----------- ----------- ------
1 1 2 1
2 3 2 2
3 3 4 3
4 4 3 4
Kind of
INSERT INTO user_region (userID, regionID)
SELECT u.userID, r.regionID
FROM
(SELECT DISTINCT userId
FROM user
WHERE user.email_address LIKE '%gmail%'
OR user.email_address LIKE '%hotmail%') u
JOIN region r ON NOT EXISTS (
SELECT 1
FROM user_region ur
WHERE ur.userID = u.userID AND ur.regionID = r.regionID )

Join 2 tables based on a single table

I can't seem to get the join / query that I need!
Lets say I have 3 tables (trimmed for this post...)
user_courses
(int) user_id
(int) course_id
users
(int) user_id
(txt) name
(txt) access
courses
(int) course_id
(txt) course_desc
What I am trying to do is select select all users with a certain access type that are taking a specific course (course_id)
something like...
SELECT *
FROM user_courses uc
JOIN users u
ON uc.user_id = u.user_id
JOIN courses c
ON uc.course_id = c.course_id
WHERE u.access = "foobar"
... but working like I want it to :)
I can get close, but will have extra users that don't have the correct access type.
Use inner join.
SELECT *
FROM user_courses uc
INNER JOIN users u
ON uc.user_id = u.user_id
LEFT JOIN courses c
ON uc.course_id = c.course_id
WHERE u.access = "foobar"
perhaps:
select * from users u
where u.access='foobar'
and exists (select 1 from user_courses uc where uc.user_id=u.user_id)
Cheers
Try
...
WHERE ISNULL(u.access, '') = 'foobar'
Not sure if your 'access' field can be null?