How can I join these tables to get this count? - sql

I have 4 tables :
users
id int primary key
questions
id int primary key
user_id int references users(id)
answers
id int primary key
question_id references questions(id)
user_id references users(id)
likes
id int primary key
answer_id references answers(id)
question_id references questions(id)
check answer_id xor question_id
A like can either reference an answer or a question, but not both so one foreign key will be null.
user_id in the likes tables is the user who placed the like.
How can I count the number of likes that were placed on each user's questions and answers?

If I correctly understand, you need count likes for each user id, which are earned by answers and questions together.
If so, then one way is:
select coalesce(questions.user_id, answers.user_id) as liked_user_id, count(*)
from likes
left join questions
on likes.question_id = questions.id
left join answers
on likes.answer_id = answers.id
group by liked_user_id

One method uses union all:
select 'questions' as which, count(*)
from questions q join
likes l
on l.question_id = q.id
where q.user_id = $user_id
union all
select 'answers' as which, count(*)
from answers a join
likes l
on l.answer_id = a.id
where a.user_id = $user_id;
EDIT:
If you want the result for all users in one row, then a correlated subqueries are a pretty easy method:
select u.*,
(select count(*)
from questions q join
likes l
on l.question_id = q.id
where q.user_id = u.id
) as question_likes,
(select count(*)
from answers a join
likes l
on l.answer_id = a.id
where a.user_id = u.id
) as answer_likes
from users u;

Related

Postgres many to one relationship join multiple tables and select all rows, provided that at least one row matches some criterea

Suppose I have a schema something like
create table if not exists user (
id serial primary key,
name text not null
);
create table if not exists post (
id serial primary key,
user_id integer not null references user (id),
score integer not null
)
I want to run a query that selects a row from the user table by ID, and all the rows that reference it from the post table, provided that at least one row in the post table has a score of greater than some number n (e.g. 50). I'm not exactly sure how to do this though.
You can use window functions. Let me assume that post has a user_id column so the tables can be tied together:
select u.*
from user u join
(select p.*, max(score) over (partition by user_id) as max_score
from post p
) p
on p.user_id = u.id
where p.max_score > 50;
If you just wanted all scores, then aggregation with filtering might be sufficient:
select u.*, array_agg(p.score order by p.score desc)
from user u join
post p
) p
on p.user_id = u.id
group by u.id
having max(p.score) > 50;

SQL Query multi reference to a table

I have two tables Task and User
Task PRIMARY KEY
IdTask
TaskName
IdHandler FOREIGN KEY REFERENCES IdUser
IdCreator FOREIGN KEY REFERENCES IdUser
User
IdUser PRIMARY KEY
Name
How to query with IdHandler and IdCreator reference to IdUser
Expected Result: TaskName, Name(Handler), Name(Creator)
PS: I'm also do not know if I can put Foreign Keys like that, at least SQL Server let me do it.
SELECT TaskName, h.Name [HandlerName], c.Name [CreatorName]
FROM Task t
INNER JOIN User h ON t.IdHandler = h.IdUser
INNER JOIN User c ON t.IdCreator = c.IdUser
Please see below query.
Select
tt.TaskName,
userHandler.Name as 'UserHandler',
userCreator.Name as 'UserCreator'
from
TasKTable tt
left join
User userHandler on userHandler.IdUser = tt.IdHandler
left join
User userCreator on userCreator.IdCreator = tt.IdHandler
select all field from all table
Select
tt.*,
userHandler.*,
userCreator.*
from
TasKTable tt left join
User userHandler on userHandler.IdUser = tt.IdHandler left join
User userCreator on userCreator .IdCreator = tt.IdHandler
You may also check this SQL Tutorial for reference.
Here is an alternative query not using JOINs
select T.NAME_TASK
,(select U1.NAME from USERS U1 where U1.ID_USER = T.ID_HANDLER) as HANDLER
,(select U2.NAME from USERS U2 where U2.ID_USER = T.ID_CREATOR) as CREATOR
from TASKS T
Refer to this db<>fiddle

Need a query to select specific comments with the name of the writer

I have a database contains 3 tables, as following:
CREATE TABLE users
(
id SERIAL PRIMARY KEY,
name VARCHAR(30) NOT NULL,
password VARCHAR(60) NOT NULL,
phone VARCHAR(10) NOT NULL,
email VARCHAR(30) UNIQUE
);
CREATE TABLE posts
(
id SERIAL PRIMARY KEY,
body TEXT NOT NULL,
user_id INTEGER REFERENCES users(id)
);
CREATE TABLE comments
(
id SERIAL PRIMARY KEY,
body TEXT NOT NULL,
post_id INTEGER REFERENCES posts(id),
user_id INTEGER REFERENCES users(id)
-- parent_id INTEGER REFERENCES comments(id)
);
I need a query to select all the comments for one specific post using the id of this post. What made me struggling is how to select the name of the user who wrote the comment!
This is what I tried :
select
c.body as cbody, p.body as pbody, c.user_id as user_id
from
users u
inner join
posts p on u.id = p.user_id
inner join
comments c on c.post_id = p.id
where
p.id=($1)
Any help??
I would use the Comments table as the first table in the FROM clause, since that's your main table for your query data. (Just personal preference, not a requirement.)
After that, I would join the Users table on Comments.user_id to get the comments' authors.
You probably do not want the post's body included with every comment, so I would leave that out.
So my query would look something like this:
SELECT c.body AS cbody, c.user_id AS user_id, u.name AS uname
FROM Comments c LEFT JOIN Users u ON u.id = c.user_id
WHERE c.post_id=($1);
An INNER JOIN might be valid too, but you need to be sure that Comments.user_id is always filled. When Comments.user_id is NULL, the comment will not be included in the queries result when INNER JOIN is used.

Multiple selects on joined tables with group by?

I have three tables with the structures outlined below:
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) UNIQUE
);
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id) NOT NULL,
category BIGINT REFERENCES categories(id) NOT NULL,
text TEXT NOT NULL
);
CREATE TABLE posts_votes (
user_id BIGINT REFERENCES users(id) NOT NULL,
post_id BIGINT REFERENCES posts(id) NOT NULL
value SMALLINT NOT NULL,
PRIMARY KEY(user_id, post_id)
);
I was able to compose a query that gets each post with its user and its total value using the below query:
SELECT p.id, p.text, u.username, COALESCE(SUM(v.value), 0) AS vote_value
FROM posts p
LEFT JOIN posts_votes v ON p.id=t.post_id
JOIN users u ON p.user_id=u.id
WHERE posts.category=1337
GROUP BY p.id, p.text, u.username
But now I want to also return a column that returns the result of SELECT COALESCE((SELECT value FROM posts_votes WHERE user_id=1234 AND post_id=n), 0) for each post_id n in the above query. What would be the best way to do this?
I think an additional LEFT JOIN is a reasonable approach:
SELECT p.id, p.text, u.username, COALESCE(SUM(v.value), 0) AS vote_value,
COALESCE(pv.value, 0)
FROM posts p JOIN
users u
ON p.user_id=u.id LEFT JOIN
topics_votes v
ON p.id = t.post_id LEFT JOIN
post_votes pv
ON pv.user_id = 1234 AND pv.post_id = p.id
WHERE p.category = 1337
GROUP BY p.id, p.text, u.username, pv.value;

MySQL Left Join with conditional

It seems pretty simple i have a table 'question' which stores a list of all questions and a many to many table which sits between 'question' and 'user' called 'question_answer'.
Is it possible to do one query to get back all questions within questions table and the ones a user has answered with the un answered questions being NULL values
question:
| id | question |
question_answer:
| id | question_id | answer | user_id |
I am doing this query, but the condition is enforcing that only the questions answered are returned. Will i need to resort to nested select?
SELECT * FROM `question` LEFT JOIN `question_answer`
ON question_answer.question_id = question.id
WHERE user_id = 14583461 GROUP BY question_id
if user_id is in the outer joined to table then your predicate user_id = 14583461 will result in not returning any rows where user_id is null i.e. the rows with unanswered questions. You need to say "user_id = 14583461 or user_id is null"
Shouldn't you use RIGHT JOIN?
SELECT * FROM question_answer RIGHT JOIN question ON question_answer.question_id = question.id
WHERE user_id = 14583461 GROUP BY question_id
something like this might help (http://pastie.org/1114844)
drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
username varchar(32) not null
)engine=innodb;
drop table if exists question;
create table question
(
question_id int unsigned not null auto_increment primary key,
ques varchar(255) not null
)engine=innodb;
drop table if exists question_ans;
create table question_ans
(
user_id int unsigned not null,
question_id int unsigned not null,
ans varchar(255) not null,
primary key (user_id, question_id)
)engine=innodb;
insert into users (username) values
('user1'),('user2'),('user3'),('user4');
insert into question (ques) values
('question1 ?'),('question2 ?'),('question3 ?');
insert into question_ans (user_id,question_id,ans) values
(1,1,'foo'), (1,2,'mysql'), (1,3,'php'),
(2,1,'bar'), (2,2,'oracle'),
(3,1,'foobar');
select
u.*,
q.*,
a.ans
from users u
cross join question q
left outer join question_ans a on a.user_id = u.user_id and a.question_id = q.question_id
order by
u.user_id,
q.question_id;
select
u.*,
q.*,
a.ans
from users u
cross join question q
left outer join question_ans a on a.user_id = u.user_id and a.question_id = q.question_id
where
u.user_id = 2
order by
q.question_id;
edit: added some stats/explain plan & runtime:
runtime: 0.031 (10,000 users, 1000 questions, 3.5 million answers)
select count(*) from users
count(*)
========
10000
select count(*) from question
count(*)
========
1000
select count(*) from question_ans
count(*)
========
3682482
explain
select
u.*,
q.*,
a.ans
from users u
cross join question q
left outer join question_ans a on a.user_id = u.user_id and a.question_id = q.question_id
where
u.user_id = 256
order by
u.user_id,
q.question_id;
id select_type table type possible_keys key key_len ref rows Extra
== =========== ===== ==== ============= === ======= === ==== =====
1 SIMPLE u const PRIMARY PRIMARY 4 const 1 Using filesort
1 SIMPLE q ALL 687
1 SIMPLE a eq_ref PRIMARY PRIMARY 8 const,foo_db.q.question_id 1
Move the user_id predicate into the join condition. This will then ensure that all rows from question are returned, but only rows from question_answer with the specified user ID and question ID.
SELECT * FROM question
LEFT JOIN question_answer ON question_answer.question_id = question.id
AND user_id = 14583461
ORDER BY user_id, question_id