Is there a way to implement count in left join - sql

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

Related

is it possible in SQL to call two difrent table with difrent data at the same time

so basicly what i need is to get two tables at the same time and then use a condition that reside in the first table and i want to apply it to the second one
if it not possible do i need to call SELECT Twice ?
is this code right ?
to determine the average rating of all movies released in 2012
SELECT AVG(rating),year FROM ratings , movies
WHERE year = 2012;
//these are the tables that i have
CREATE TABLE movies (
id INTEGER,
title TEXT NOT NULL,
year NUMERIC,
PRIMARY KEY(id)
);
CREATE TABLE stars (
movie_id INTEGER NOT NULL,
person_id INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id),
FOREIGN KEY(person_id) REFERENCES people(id)
);
CREATE TABLE directors (
movie_id INTEGER NOT NULL,
person_id INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id),
FOREIGN KEY(person_id) REFERENCES people(id)
);
CREATE TABLE ratings (
movie_id INTEGER NOT NULL,
rating REAL NOT NULL,
votes INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id)
);
CREATE TABLE people (
id INTEGER,
name TEXT NOT NULL,
birth NUMERIC,
PRIMARY KEY(id)
);
I think you want a JOIN and an aggregate function:
SELECT AVG(r.rating) avg_rating_2012
FROM ratings r
INNER JOIN movies m on m.id = r.movie_id
WHERE m.year = 2012;
If you want this for all years at once, then use GROUP BY:
SELECT m.year, AVG(r.rating) avg_rating
FROM ratings r
INNER JOIN movies m on m.id = r.movie_id
GROUP BY m.year;

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

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.

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 get all entries by ids

I have 3 tables roughly like below:
create table user(
id integer primary key
)
create table post(
id integer primary key,
author integer,
foreign key (author) references user(id)
)
create table user_following(
id integer primary key,
follower integer,
followee integer,
foreign key (follower) references user(id),
foreign key (followee) references user(id)
)
these tables were created by ORM framework, I want to use raw SQL to get all posts by a user's followee, which can be multiple users. Can I do it in SQL?
If you know the follower's ID, the below statement will conduct a join and get the follower's followees' posts. You can replace the number 3 with any user id.
SELECT * FROM user_following AS a
JOIN post AS b ON a.followee = b.author
WHERE a.follower=3;
Something like this?
select p.*
from post p
join user_following uf on p.author = uf.followee
where uf.follower = 123;
select posts.*
from post posts
inner join [user] u_followee on u_followee.id = posts.author
inner join user_following ufo on ufo.followee = u_followee.id
inner join [user] u_follower on ufo.follower = u_follower.id
where u_follower.id = #USER_ID_WHOSE_FOLLOWEE_POSTS_REQUIRED

Using PostgreSQL with clause show count from two tables

I have four tables advertisement,sales_enquiry,sales_estimate and sales_leadsource. Database used is PostgreSQL. I want to get the count of sales_enquiry and count of sales_estimate for an advertisement, advertisement and sales_enquiry has the id of sales_leadsource as foreign key, sales_estimate table has the id of sales_enquiry as foreign key. I have tried using the following query but result is not getting correctly.
Table schema is
TABLE sales_leadsource
(
leadsource_id serial NOT NULL,
leadsource character varying(250),
CONSTRAINT sales_leadsource_pkey PRIMARY KEY (leadsource_id)
)
TABLE advertisement
(
id serial NOT NULL,
ad_source_name character varying(200),
amount numeric(18,2),
duration character varying(50),
leadsource_id integer,
CONSTRAINT advertisement_pkey PRIMARY KEY (id),
CONSTRAINT advertisement_leadsource_id_fkey FOREIGN KEY (leadsource_id)
REFERENCES sales_leadsource (leadsource_id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE RESTRICT
)
TABLE sales_enquiry
(
enquiry_id serial NOT NULL,
leadsource_id integer,
customer_name character varying(200),
advertisement_id integer,
CONSTRAINT sales_enquiry_pkey PRIMARY KEY (enquiry_id),
CONSTRAINT sales_enquiry_advertisement_id_fkey FOREIGN KEY (advertisement_id)
REFERENCES advertisement (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE RESTRICT
)
TABLE sales_estimate
(
ref_no integer NOT NULL,
enquiry_id integer,
property_id integer,
sqft_amount numeric,
CONSTRAINT sales_estimate_pkey PRIMARY KEY (ref_no),
CONSTRAINT sales_estimate_enquiry_id_fkey FOREIGN KEY (enquiry_id)
REFERENCES sales_enquiry (enquiry_id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE RESTRICT
)
Query is as follows
with campaign as(
select se.leadsource_id,se.advertisement_id,count(*) as leads_count from sales_enquiry se group by se.advertisement_id,se.leadsource_id
),
converted as(
select se.leadsource_id,se.advertisement_id,count(*) as converted_count from sales_enquiry se
left join sales_estimate sm on se.enquiry_id=sm.enquiry_id
where sm.status_id=1 and sm.application_status=1 or sm.application_status=2
group by se.advertisement_id,se.leadsource_id
)
select a.id,
coalesce(a.ad_source_name,'None'),
a.amount,
a.duration,
c.leads_count,
c.leadsource_id,
sl.leadsource,
cv.converted_count
From advertisement a
right join campaign c on a.id=c.advertisement_id
left join converted cv on c.advertisement_id=cv.advertisement_id
left join advertisement_status ads on a.status_id=ads.status_id
left join sales_leadsource sl on c.leadsource_id=sl.leadsource_id;
Please help. Thanks in advance.