SQL How To Select All User And User's Friends Posts - sql

I need to find all user and user's friends posts. For example: I have a user with ID=0. This user has 3 friends (status = accepted) with ID's=1, 2, 3. I need to select all POSTS where USER_POSTED=0, 1, 2, 3 using one sql query.
The problem is - user can have from zero to infinity friends. I can find all user friends ID:
SELECT U.USER_ID
FROM USERS U
JOIN RELATIONSHIP R
ON (U.USER_ID = R.USER_ID_FROM OR U.USER_ID = R.USER_ID_TO)
AND U.USER_ID != :userId
WHERE R.STATUS = :status
AND (R.USER_ID_TO = :userId OR R.USER_ID_FROM = :userId);
But I have no idea how to find all their posts in one query.
SQL Data Model:
CREATE TABLE USERS
(
USER_ID NUMBER PRIMARY KEY,
USER_NAME NVARCHAR2(64),
REAL_NAME NVARCHAR2(64),
EMAIL NVARCHAR2(64),
PHONE_NUMBER NVARCHAR2(64),
BIRTH_DATE TIMESTAMP,
POST_ID NUMBER,
PASSWORD NVARCHAR2(16)
);
CREATE TABLE POST
(
POST_ID NUMBER PRIMARY KEY,
MESSAGE NVARCHAR2(128),
DATE_POSTED TIMESTAMP,
LOCATION NVARCHAR2(128),
USERS_TAGGED NUMBER,
USER_POSTED NUMBER,
USER_PAGE_POSTED NUMBER,
CONSTRAINT USER_TARGET_FK FOREIGN KEY (USERS_TAGGED) REFERENCES USERS (USER_ID),
CONSTRAINT USER_POSTED_FK FOREIGN KEY (USER_POSTED) REFERENCES USERS (USER_ID),
CONSTRAINT USER_PAGE_POSTED_FK FOREIGN KEY (USER_PAGE_POSTED) REFERENCES USERS (USER_ID)
);
ALTER TABLE USERS
ADD CONSTRAINT POST_FK FOREIGN KEY (POST_ID) REFERENCES POST (POST_ID);
CREATE TABLE RELATIONSHIP
(
USER_ID_FROM NUMBER NOT NULL,
USER_ID_TO NUMBER NOT NULL,
STATUS NVARCHAR2(256),
FRIENDS_REQUEST_DATE TIMESTAMP,
CONSTRAINT FK_USER_ONE FOREIGN KEY (USER_ID_FROM) REFERENCES USERS (USER_ID),
CONSTRAINT FK_USER_TWO FOREIGN KEY (USER_ID_TO) REFERENCES USERS (USER_ID),
CONSTRAINT PK_RELATIONSHIP PRIMARY KEY (USER_ID_FROM, USER_ID_TO)
);

Your logic for displaying a post is that it needs to be authored by some :userId or by a direct friend of that same user.
SELECT p.POST_ID, p.MESSAGE
FROM POST p
WHERE
p.USER_POSTED = :userId OR
p.USER_POSTED IN (SELECT USER_ID_FROM FROM RELATIONSHIP
WHERE USER_ID_TO = :userId
UNION ALL
SELECT USER_ID_TO FROM RELATIONSHIP
WHERE USER_ID_FROM = :userId);
Note that I used a union under the assumption that a friend relationship could be in any direction. If you always store relationships in one direction (e.g. smallest to largest), then the union is redundant.

Related

How do I select data from a table consisting of a foreign key constraint?

I have two tables, User & Playlist
Table User:
CREATE TABLE IF NOT EXISTS User (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(26) NOT Null UNIQUE,
email TEXT NOT Null UNIQUE,
password VARCHAR(100)
)
Table Playlist:
CREATE TABLE IF NOT EXISTS Playlist (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
image BLOB,
private NUMERIC,
playlistOwner VARCHAR(26),
FOREIGN KEY(playlistOwner) REFERENCES User(id)
)
Playlist table:
What I have tried with:
SELECT * FROM Playlist
JOIN Playlist
ON 10 = Playlist.playlistOwner
WHERE playlistOwner = ald;
Which gives the error:
Presumably, you intend something like this:
SELECT *
FROM Playlist pl JOIN
User u
ON pl.playListOwner = u.id
WHERE u.id = 10;
This is just a guess, but your question mentions two tables and the query references only one of them. Plus, a self-join doesn't seem particularly useful.
First, if you want the column playlistOwner of the table Playlist as foreign key that references the column id of the table User you should define with the same data type as the referenced column.
So the correct definition should be playlistOwner INTEGER:
CREATE TABLE IF NOT EXISTS Playlist (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
image BLOB,
private NUMERIC,
playlistOwner INTEGER,
FOREIGN KEY(playlistOwner) REFERENCES User(id)
)
and the values that you store in the column playlistOwner must be the ids of the users and not their usernames.
Now if you know the user's id there is no need for a join.
You simply do:
SELECT *
FROM Playlist
WHERE playlistOwner = 10;
If you know only the username then you join the tables:
SELECT p.*
FROM Playlist p INNER JOIN User u
ON u.id = p.playlistOwner
WHERE u.username = 'somename'
or with a subquery in the WHERE clause:
SELECT *
FROM Playlist
WHERE playlistOwner = (SELECT id FROM User WHERE username = 'somename')

Is there a way to implement count in left join

I'm setting up a chat-Group application using a simple database. I want to know if there is a way for me to see the people that liked/disliked a certain post, which can have a picture or a video attached to it. The important information I want to know is the post ID, message of the post, date of the post, url of the picture I want to post, url of the video, and the id of the user posting said post. I'm using left joins to see all this information, but I'm missing if the posts have likes or dislikes.
I already tried using the aggregate count to see each like and dislike of the posts, but I don't know how to mix this query with the previous information.
This is my query to see the information listed above:
select P.post_ID, P.post_msg, P.post_date, F.photo_url,V.video_url, P.user_ID
from ((Post as P left join Photo as F on P.post_ID = F.post_ID ) left join Video as V
on P.post_ID = V.post_ID)
where P.chat_ID = 1
Here is the output of the Query:
Here is an example of the output I want:
The query above displays everything I'm looking for except each like and dislike of each post. Is there anyway to see this information mixed with what I want?
Here is the structure of the DataBase:
create table Login(login_ID serial primary key, login_Date DATE NOT NULL DEFAULT CURRENT_DATE, user_name varchar(20) NOT NULL);
create table Users(user_ID serial primary key, user_name varchar(20) NOT NULL, user_password varchar(20) NOT NULL);
create table Contact_List(contactlist_ID serial primary key, user_name varchar(20) NOT NULL,user_ID integer references Users(user_ID) on delete cascade);
create table Person(person_ID serial primary key, person_name varchar(20) NOT NULL, person_lastname varchar(30) NOT NULL, person_phone varchar(12), person_email varchar(50) NOT NULL, user_ID INTEGER references Users(user_ID) on delete cascade);
create table Admin(admin_ID serial primary key, user_ID INTEGER references Users(user_ID) on delete cascade);
create table Chat_Group(chat_ID serial primary key, chat_name varchar(50) NOT NULL, admin_Id INTEGER references Admin(admin_ID) on delete cascade);
create table Group_List(user_ID INTEGER references Users(user_ID) on delete cascade, chat_ID integer references Chatgroup(chat_id) on delete cascade, primary key(user_id, chat_id));
create table Post(post_ID serial primary key, post_msg varchar(280), post_date DATE NOT NULL DEFAULT CURRENT_DATE, user_ID INTEGER references Users(user_ID) on delete cascade, chat_ID INTEGER references Chatgroup(chat_ID) on delete cascade);
create table Video(video_ID serial primary key, video_url varchar(280) NOT NULL, post_ID INTEGER references Post(post_ID) on delete cascade);
create table Photo(photo_ID serial primary key, photo_url varchar(280) NOT NULL, post_ID INTEGER references Post(post_ID) on delete cascade);
create table Reply(reply_ID serial primary key, reply_msg varchar(280) NOT NULL, reply_Date DATE NOT NULL DEFAULT CURRENT_DATE, post_ID INTEGER references Post(post_ID) on delete cascade, user_ID INTEGER references Users(user_ID) on delete cascade);
create table HashTag(hashtag_ID serial primary key, hashtag_msg varchar(200) NOT NULL,post_ID INTEGER references Post(post_ID) on delete cascade NOT NULL, user_ID INTEGER references Users(user_ID) on delete cascade);
create table Reaction(reaction_ID serial primary key,reaction_date DATE NOT NULL DEFAULT CURRENT_DATE, reaction_like BOOLEAN, reaction_dislike BOOLEAN, post_ID INTEGER references Post(post_ID) on delete cascade, user_ID INTEGER references users(user_ID) on delete cascade);
create table React_to(reaction_ID INTEGER references Reaction(reaction_ID) on delete cascade, reply_ID INTEGER references Reply(reply_ID) on delete cascade, primary key(reaction_ID,reply_ID));
Excuse me for my bad english, english is not my main language.
One way of doing this would use correlated subquery.
SELECT p.post_id,
p.post_msg,
p.post_date,
f.photo_url,
v.video_url,
p.user_id,
(SELECT count(*)
FROM reaction r
WHERE r.post_id = p.post_id
AND r.reaction_like) likes,
(SELECT count(*)
FROM reaction r
WHERE r.post_id = p.post_id
AND r.reaction_dislike) dislikes
FROM post p
LEFT JOIN photo f
ON p.post_id = f.post_id
LEFT JOIN video v
ON p.post_id = v.post_id
WHERE p.chat_id = 1;
However I wonder if a reaction can be both a like and a dislike at once. At least your data model allowes such a thing...
I don't like the idea of going to the reactionss table twice. One way to avoid this is to use a lateral join:
select P.post_ID, P.post_msg, P.post_date, F.photo_url, V.video_url, P.user_ID,
r.num_likes, r.num_dislikes
from Post P left join
Photo F
on P.post_ID = F.post_ID left join
Video V
on P.post_ID = V.post_ID left join lateral
(select sum(r.reaction_like) as num_likes,
sum(r.reaction_dislike) as num_dislikes
from reactions r
where r.post_id = p.post_id
) r
on 1=1
where P.chat_ID = 1

Result not returned if 3rd table empty, why?

I want to know the way join works,I have 3 tables users, addresses and location, users and addresses tables has data but not in location. When i made a join with addresses from users it return data but when i joined with location it don't return data, my question is why it is nil, because i joined user to address and user to location, so if user has data in address table then it will return that data but not in location so do not return location data in location columns. My schema is
create temporary table users (
id serial,
username VARCHAR(25) NOT NULL,
PRIMARY KEY (id)
);
CREATE temporary TABLE addresses (
id serial,
user_id int NOT NULL,
city VARCHAR(30) NOT NULL,
PRIMARY KEY (id),
CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users (id)
);
CREATE temporary TABLE location (
id serial,
user_id int NOT NULL,
state VARCHAR(30) NOT NULL,
PRIMARY KEY (id),
CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users (id)
);
insert into users(username) values ('u1'), ('u2'), ('u3'), ('u4')
insert into addresses(user_id, city) values (1, 'c1'), (2,'c2'), (3,'c3')
select *
from users u
inner join addresses a on u.id=a.user_id
inner join location l on u.id=l.user_id;
insert into location(user_id, state) values (3, 's2')
An inner join requires matches in both tables. Outer joins allow you to handle situations where that is not true. Your query only shows inner joins.

Complicated Join Query, Join 3 tables with multiple group bys

I have 3 tables:
Tweets:
CREATE TABLE tweets (
text_content VARCHAR(280) not null,
username VARCHAR(50) not null,
timestamp TIMESTAMP not null DEFAULT current_timestamp,
id UUID not null DEFAULT uuid_generate_v4(),
CONSTRAINT tweets_pk PRIMARY KEY (id)
);
Likes:
CREATE TABLE likes (
username VARCHAR(50) not null,
timestamp TIMESTAMP not null default current_timestamp,
post_id UUID not null,
CONSTRAINT likes_pk PRIMARY KEY (username, post_id),
CONSTRAINT likes_post_id_fk FOREIGN KEY (post_id) REFERENCES tweets(id)
);
And Retweets
CREATE TABLE retweets (
username VARCHAR(50) not null,
timestamp TIMESTAMP not null default current_timestamp,
post_id UUID not null,
CONSTRAINT retweets_pk PRIMARY KEY (username, post_id),
CONSTRAINT retweets_post_id_fk FOREIGN KEY (post_id) REFERENCES tweets(id)
);
I need a query, that would select all tweets, along with the amount of likes and retweets they have.
I did manage to write a working query, but I think I over-complicated it, and would love to hear simpler solutions!
You want to aggregate before joining. Assuming the join key is post_id:
select t.*, l.likes, r.retweets
from tweets t left join
(select post_id, count(*) as likes
from likes
group by post_id
) l
on l.post_id = t.id left join
(select post_id, count(*) as retweets
from retweets
group by post_id
) r
on r.post_id = t.id;

SQL/Oracle - adding a userID to a friend table with the name FriendID

I am currently working on a piece of corusework for an Advanced databases university module using Oracle APEX with Objects.
We are required to create a facebook like application with a user table and a friend table.
The users table was created as follows:
CREATE TABLE user_table (
userId NUMBER NOT NULL,
userName VARCHAR2(50) NOT NULL,
emailAddress VARCHAR2(150) NOT NULL,
password VARCHAR(20) NOT NULL, PRIMARY KEY(userId)
);
The freinds following table:
CREATE TABLE friends_following_table(
friendId NUMBER NOT NULL,
userId NUMBER references user_table,
PRIMARY KEY(friend_id )
);
My question:
I am required to have friends that are connected to users.
How can the userId become a friendId? Or have done this completely wrong?
Sample user:
userId: 1
name: shannon
userId: 2
name: Alison
User 1 and user 2 need to be friends and the database needs to reflect this
Thanks
Based on given user_table in your question:
CREATE TABLE user_table (
userId NUMBER NOT NULL,
userName VARCHAR2(50) NOT NULL,
emailAddress VARCHAR2(150) NOT NULL,
password VARCHAR(20) NOT NULL, PRIMARY KEY(userId)
);
And also based on your sample data and desired output:
Sample user: userId: 1 name: shannon userId: 2 name: Alison
User 1 and user 2 need to be friends and the database needs to reflect
this
In fact you have a Many-To-Many relationship between users so you need such this table for the relations:
CREATE TABLE friends_following_table(
user_id NUMBER REFERENCES user_table,
follower_id NUMBER REFERENCES user_table,
PRIMARY KEY(user_id,follower_id)
);
In case of your sample data:
INSERT INTO user_table
VALUES(1,'shannon','s#so.com','123');
INSERT INTO user_table
VALUES(2,'Alison','a#so.com','123');
now you have to specify which one follows the other one:
if user 2 follows user 1:
INSERT INTO friends_following_table VALUES(1,2);
and if user 1 follows user 2:
INSERT INTO friends_following_table VALUES(2,1);
You can extract every two followers by a simple join:
SELECT u1.userId,u1.userName,
u2.userName "FOLLOWS"
FROM user_table u1
JOIN friends_following_table f on u1.userId=f.follower_id
JOIN user_table u2 on u2.userId=f.user_Id
you can count the number of followers of every user by using count function with a left join:
SELECT u.userId,u.userName,
count(f.follower_id) "FOLLOWERS"
FROM user_table u
LEFT JOIN friends_following_table f on u.userId=f.user_Id
GROUP BY u.userId,u.userName
you can also count the number of followings of every user by using count function with a left join:
SELECT u.userId,u.userName,
count(f.follower_id) "FOLLOWINGS"
FROM user_table u
LEFT JOIN friends_following_table f on u.userId=f.follower_id
GROUP BY u.userId,u.userName
Try this schema instead:
CREATE TABLE UserTable (
UserID NUMBER NOT NULL
, UserName VARCHAR2(50) NOT NULL
, EmailAddress VARCHAR2(150) NOT NULL
, Password VARCHAR(20) NOT NULL
, CONSTRAINT PK_UserTable PRIMARY KEY(UserID)
);
CREATE TABLE FriendTable (
UserID NUMBER NOT NULL
, FriendID NUMBER NOT NULL
, CONSTRAINT PK_FriendTable PRIMARY KEY(UserID, FriendID)
, CONSTRAINT FK_FriendTable_UserID FOREIGN KEY (UserID) REFERENCES UserTable(UserID)
, CONSTRAINT FK_FriendTable_FriendID FOREIGN KEY (FriendID) REFERENCES UserTable(UserID)
);