Dynamically select the table to join in Postgres with case statements - sql

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;

Related

Database join where

Database
Users
id
lastname
firstname
1
Sardor
Sattarov
2
Nurmuhammad
To’xtayev
3
Jasur
Sattarov
Group_items
id
student_id
group_id
1
2
55
2
1
55
3
2
11
Return
example 1
condition
users.id == group_items.student_id do not publish a table that satisfies this desire group_items.id == 55
id
lastname
firstname
3
Jasur
Sattarov
example 2
condition
users.id == group_items.student_id do not publish a table that satisfies this desire group_items.id == 11
id
lastname
firstname
1
Sardor
Sattarov
3
Jasur
Sattarov
Looks like you want OR instead of AND.
SELECT
*
FROM
users u
LEFT JOIN group_items gi ON u.id = gi.student_id
WHERE
gi.student_id IS NULL
OR gi.group_id <> 5
Studends without a group plus studends in all groups but 5.
Please try this,
Example 1:
SELECT
U.ID,U.LASTNAME,U.FIRSTNAME
FROM
USERS U
LEFT JOIN
GROUP_ITEMS G
ON U.ID=G.STUDENT_ID
WHERE G.GROUP_ID <>55
Example 2:
SELECT
U.ID,U.LASTNAME,U.FIRSTNAME
FROM
USERS U
LEFT JOIN
GROUP_ITEMS G
ON U.ID=G.STUDENT_ID
WHERE G.GROUP_ID <>11
You can use join statements in SQL
For Example
for return 1:
SELECT * FROM users u left join group_items gi on gi.student_id = u.id
this query will return all students with their group_id
for filter by group you can use where statement.
SELECT * FROM users u left join group_items gi on gi.student_id = u.id where gi.group_id<>11

Distinct select where row in one of multiple tables

I'm building a feed for a project, and an object can appear in the feed depending from a couple of places.
I have a table of posts, which have a group and a visibility. Some require membership to view, others are public.
posts
id
group_id
visibility
1
1
public
2
1
member
3
2
public
4
2
member
Membership is recorded in a members table, with a start and end date for the membership:
members
id
user_id
group_id
period_start
period_end
1
1
1
2020-01-01
2020-02-01
2
2
2
2020-01-01
2020-01-01
At the moment, my query returns just the posts from the groups the user is a member of:
SELECT posts.*
FROM members
LEFT JOIN posts
ON
posts.group_id = members.group_id AND
posts.visibility IN ('member', 'public') AND
posts.deleted_at IS NULL
WHERE
members.user_id = XYZ AND
members.period_start < now() AND
members.period_end > now();
I want to add an the ability for a user to add a group to their favorites without becoming a member, which will show just the public posts in their feed.
favorites
id
user_id
group_id
1
1
2
How can I change the above query so it selects posts from the users membership groups, and also the public posts from their favorited groups?
It needs to be distinct, so if the user is both a member and has favourited the post doesn't appear twice.
I'm expecting the result to look like the below for user_id 1:
id
group_id
visibility
1
1
public
2
1
member
3
2
public
I think you just want union all:
SELECT p.*
FROM members m JOIN
posts p
ON p.group_id = m.group_id AND
p.visibility IN ('member', 'public') AND
p.deleted_at IS NULL
WHERE m.user_id = XYZ AND
m.period_start < now() AND
m.period_end > now()
UNION ALL
SELECT p.*
FROM favorites f JOIN
posts p
ON p.group_id = f.group_id AND
p.visibility IN ('public') AND
p.deleted_at IS NULL
WHERE f.user_id = XYZ ;
Note the joins are inner joins, not outer joins.
I'm not sure if the favorites need the filter on active members or not; this does not include it.
You can also express this with two LEFT JOINs and some filtering:
SELECT p.*
FROM posts p LEFT JOIN
favorites f
ON f.group_id = p.group_id AND
p.visibility IN ('public') LEFT JOIN
members m LEFT JOIN
ON p.group_id = m.group_id AND
p.visibility IN ('member', 'public') AND
m.period_start < now() AND
m.period_end > now()
WHERE p.deleted_at IS NULL AND
XYZ IN (m.user_id, f.user_id)

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

SQL Join to only one record in one to many relationship

I have the following query:
SELECT c.[ClientID]
,u.[AccessId]
FROM [tblClient] c
INNER JOIN [tblUser] u ON u.[Id] = c.[UserId]
This tblUser has multiple ID's for each UserID row.
So it would look like this:
UserID AccessID
1 AD2F0A-965B78414-2B34906F2-0127AA5A
1 ID2F0A9-65B784-142B34906-F20127AA5A
1 UD2F0A9-65B78-4142B34906F-20127AA5A
2 TD2F0A9-65B784142-B34906F-20127AA5A
2 RD2F0A9-65B784142B3-4906-F20127AA5A
3 WD2F0A96-5B784142-B34906F201-27AA5A
3 ZD2F0A96-5B784-142B34-906F2-0127AA5A
3 CD2F0A965-B784142B3-4906F20-127AA5A
Is there a way to only get the top(or 1) AccessId for each UserID? It doesnt matter which AccessID i get, I just want 1 of them.
Thanks
SELECT c.[ClientID], MAX(u.[AccessId])
FROM [tblClient] c
INNER JOIN [tblUser] u
ON u.[Id] = c.[UserId]
GROUP BY c.[ClientID]

Selecting a user's friend's events

I have these information :
Table "Users" =>
**Id** **Name**
1 a
2 b
3 c
4 d
5 e
Table "Friends" =>
**SenderId** **ReceiverId** **State**
1 2 x
2 3 ok
3 1 ok
3 4 ok
5 3 ok
5 4 ok
Table "Events" =>
**SenderId** **receiverId** **text**
1 1 3 ssss
2 3 1 dsadsa
3 2 3 safsdf
4 3 5 fgfdgfd
5 4 3 fgfhgfh
6 5 4 sad sad
My question is that how could I get the events of user's friends in one sql statement using JOINS only .
for example :=>
userId : 1
his friends : 3 (state = ok)
friends events : 1, 2, 3, 4, 5 (events from 1 to 5 have the userId 3 which considered as a friend to user 1 )
ANY HELP .. THANKS ;) !!
You didn't specify a name for the first column in your Events table so I've called it row_ID:
SELECT U1.ID AS user_ID, U1.Name AS user_name,
U2.ID AS friend_user_ID, U2.Name AS friend_user_name,
E1.row_ID, E1.text AS event_text
FROM Users AS U1
INNER JOIN Friends AS F1
ON (
U1.ID = F1.ReceiverId
OR U1.ID = F1.SenderId
)
AND F1.State = 'ok'
INNER JOIN Users AS U2
ON U2.ID = F1.SenderId
INNER JOIN Events AS E1
ON (
U2.ID = E1.ReceiverId
OR U2.ID = E1.senderId
)
WHERE U1.ID = 1;
Your table structure is quite confusing with all those Sender and Receiver ID's, but I think the following query will work.
SELECT * FROM
(SELECT Users.Name, Events.text
FROM Users
LEFT JOIN Friends ON Users.Id = Friends.ReceiverId
LEFT JOIN Events ON Friends.SenderId = Events.SenderId
WHERE Users.Id = 1)
UNION ALL
(SELECT Users.Name, Events.text
FROM Users
LEFT JOIN Friends ON Users.Id = Friends.ReceiverId
LEFT JOIN Events ON Friends.SenderId = Events.ReceiverId
WHERE Users.Id = 1)
You can probably simplify this, but I'm not sure how, considering you want to select events 1-5 because the friend ID can be either the Events.SenderId or the Events.ReceiverId.
I don't know if SQL supports this, but maybe put an OR into the LEFT JOIN clause (?):
SELECT Users.Name, Events.text
FROM Users
LEFT JOIN Friends ON Users.Id = Friends.ReceiverId
LEFT JOIN Events ON (Friends.SenderId = Events.SenderId OR Friends.SenderId = Events.ReceiverId)
WHERE Users.Id = 1
It's pretty easy with a sub-query.
Here's the subquery (find user 1's friends)
SELECT
senderID
FROM
friends
WHERE
receiverID = 1
Here's the main query (find user1's friend's events)
SELECT
primary_key,
senderID,
receiverID,
text
FROM
events
WHERE
senderID IN (subquery) --events where user1's friends were the senders
OR receiverID IN (subquery) --events where user1's friend's were the receivers
Put it all together:
SELECT
*
FROM
events
WHERE
senderID IN (SELECT
senderID
FROM
friends
WHERE
senderID = 1) --events where user1's friends were the senders
OR receiverID IN (SELECT
senderID
FROM
friends
WHERE
receiverID = 1) --events where user1's friend's were the receivers
You might consider replacing the explicit '1' with a variable, so you can run this query for every user.
Good luck