MySQL subqueries - mysql

Can we do this query without subqueries?
SELECT login, post_n,
(SELECT SUM(vote) FROM votes WHERE votes.post_n=posts.post_n)AS votes,
(SELECT COUNT(comments.post_n) FROM comments WHERE comments.post_n=posts.post_n)AS comments_count
FROM users, posts
WHERE posts.id=users.id AND (visibility=2 OR visibility=3)
ORDER BY date DESC LIMIT 0, 15
tables:
Users: id, login
Posts: post_n, id, visibility
Votes: post_n, vote
id — it`s user id, Users the main table.

Yeah, it's possible:
SELECT login, post_n,
SUM(vote) as votes,
FROM users
JOIN posts using(id)
LEFT JOIN votes using(post_n)
WHERE visibility=2 OR visibility=3
GROUP BY login, post_n
Then flatten the result:
select * from
(
SELECT login, post_n,
SUM(vote) as votes,
FROM users
LEFT JOIN posts using(id)
LEFT JOIN votes using(post_n)
WHERE visibility=2 OR visibility=3
GROUP BY login, post_n
) as votes_count
Then join the comments:
select votes_count.login, votes_count.post_n, votes_count.votes,
COUNT(comments.post_n) as comments_count
from
(
SELECT login, post_n,
SUM(vote) as votes,
FROM users
LEFT JOIN posts using(id)
LEFT JOIN votes using(post_n)
WHERE visibility=2 OR visibility=3
GROUP BY login, post_n
) as votes_count
LEFT JOIN comments using(post_n)
GROUP BY votes_count.login, votes_count.post_n
ORDER BY date DESC LIMIT 0, 15

you can store vote sum and post count in a 'users' table and update them via trigger or 'update' query.

i have test both variants and test variant when we using join or where to merge our tables.
No subqueries variant more slow, 0.0208 sec, and mysql use only 271 rows in votes table, but when i have use joins it use whole rows. Here no subqueries variant:
SELECT res.*, COUNT(*) FROM
(
SELECT login, posts.post_n, SUM(vote)AS votes
FROM users, posts, votes
WHERE users.id=posts.id AND posts.post_n=votes.post_n AND visibility=3
GROUP BY posts.post_n
)AS res, comments
WHERE comments.post_n=res.post_n
GROUP BY res.post_n
ORDER BY date DESC
LIMIT 0, 15
And subqueries varian performed only 0.0027 sec, it`s no cache but using indexes in all tests.
p.s. sorry for my english

Related

MySQL - how to combine three tables to get the counts

There are three tables, I would like to get the count of a user's total tweets and the count of likes his total tweets received.
I tried to combine two queries to get what I want but failed. Have looked through several previous questions but still can't figure it out.
Users table
id
name
1
User1
Tweets table
id
UserId (foreign key)
content
1
User1
hello
Likes table
id
UserId (foreign key)
TweetId (foreign key)
1
User1
hello
First query:
SELECT Users.name, Users.id, COUNT(Tweets.UserId) AS UserTweetCount FROM Users
LEFT JOIN Tweets
ON Users.id = Tweets.UserId
GROUP BY Users.id
ORDER BY UserTweetCount DESC;
Second query:
SELECT Users.name, Users.id, COUNT(Likes.UserId) AS UserTweetBeLikedCount FROM Users
LEFT JOIN Likes
ON Users.id = Likes.UserId
GROUP BY Users.id;
I tried like below but would get wrong UserTweetBeLikedCount counts. The counts would be UserTweetCount's, not UserTweetBeLikedCount's. When I ran two queries separately, it worked well. But when I combined them together, it didn't work right.
Don't know how to display the right counts. Can someone give me hints to solve this, please?
SELECT Users.name, Users.id,
COUNT(Tweets.UserId) AS UserTweetCount, COUNT(Likes.UserId) AS UserTweetBeLikedCount
FROM Users
LEFT JOIN Tweets
ON Users.id = Tweets.UserId
LEFT JOIN Likes
ON Users.id = Likes.UserId
GROUP BY Users.id
ORDER BY UserTweetCount DESC;
I recommend using correlated subqueries for this:
SELECT u.*,
(SELECT COUNT(*)
FROM Tweets t
WHERE u.id = t.UserId
) AS UserTweetCount,
(SELECT COUNT(*)
FROM Likes l
WHERE u.id = l.UserId
) AS UserLikeCount
FROM Users u
ORDER BY UserTweetCount DESC;
As a note: For performance, you want indexes on Tweets(UserId) and Likes(UserId).

Group By and Count in a single sql query

I have two tables - users and login_reports.
users table have 4 columns - id, email, name, password, created_at, updated_at
login_reports table have 3 columns - id, user_id, created_at.
Every time user logins, an entry is created in login_reports.
Now I have to write a query to show login reports on admin dashoard.
The query should return rows of users having login count and last login.
Can someone help me with this.
SELECT users.id AS id, count(users.id) FROM users
INNER JOIN login_reports
ON users.id = login_reports.user_id
GROUP BY users.id
How do I get last login timestamp i.e. the last entry of that user in login_reports (created_at).
I think you want something like this
select u.id, count(r.id), max(r.created_at)
from user u
left join login_reports r on r.user_id = u.id
group by u.id
edit: thanks #tcadidot0
I used following query to solve this
SELECT users.id as id, count(login_reports.id) as login_count, login_reports.created_at as last_login FROM users LEFT JOIN (SELECT * FROM login_reports ORDER BY created_at DESC) login_reports ON users.id = login_reports.user_id

How to join 3 tables and get sum from result of individual joins?

I have the following scenario:
Table: users : user_id, username ,...
Table: login: user_id, login_date, ...
Table: point: user_id, points, point_time
Joins will be on the basis of users.user_id with other tables.
Now, I want to get count of all the logins as well as sum of all the points earned by the user.
Now, when I do:
select users.user_id,count(*) from users
inner join login on users.user_id=login.user_id
group by users.user_id
It returns count as 36(for example).
Whenever I run:
select users.user_id,count(*),sum(points) from users
inner join point on users.user_id=point.user_id
group by users.user_id
It returns sum as 400(for example) and count as 2.
But if I combine both the queries:
select users.user_id,count(*),sum(points) from users
inner join login on users.user_id=login.user_id
inner join point on users.user_id=point.user_id
group by users.user_id
It returns count as 72 (36 * 2) and sum as 800 (400 *2).
Twice because of multiple userIds present.
I tried several things like combining with distincts but nothing seems to work. Please help.Better if it's possible with joins alone. Thanks in advance. I am using mysql with Php.
You can sum the points in a subquery and select distinct logins in the count
select users.user_id,l.login,p.points from users
inner join (select user_id, count(1) login from login
group by login) as l on users.user_id=login.user_id
inner join (select user_id, sum(point) as point
from point group by user_id ) as p on users.user_id=point.user_id
You should be able to do your count by joining in your login table and then including a subquery to get your count of points:
select users.user_id, count(*) as login_count,
(select sum(points) from point
where point.user_id = users.user_id) as points_sum
from users
inner join login on users.user_id=login.user_id
group by users.user_id

Combining count from one table and distinct from another

I've setup a contest where video submissions are sent in and then people vote on them. Submissions are sent into a table submissions with this structure:
submission_id, title, videoname
The voting table votes structure is:
video_id, voter_id
The video_id correlates to the submission_id in the submissions table.
I want to get the number of votes for each video like so:
select video_id, count(1) from votes group by submission_id
But I also want to display the title for each video so the result would be:
video_id, count, title
I am a sql noob so please forgive me if this is a simple statement. I have done some research and was not able to come up with something on my own and would appreciate any help.
I would recommend doing a LEFT JOIN instead of an INNER JOIN... and COUNT(v.video_id) instead of COUNT(*). This way you will still return submissions that currently have 0 votes:
select
s.submission_id as video_id,
count(v.video_id) as vote_count,
s.title
from
submissions s
left join votes v on v.video_id = s.submission_id
group by
s.submission_id,
s.title
select s.submission_id, s.title, s.videoname, c.cnt
from
submissions s,
(select video_id, count(1) as cnt from votes group by video_id) c
where
s.submission_id = c.video_id
This will return every video from you submissions table and show the number of votes in the votes table. If there have not been any votes it will show up in the results with a NULL as the Votes column
SELECT video_id, title, COUNT(voter_id) Votes
FROM submissions s
LEFT OUTER JOIN votes v ON s.submission_id = v.video_id
GROUP BY video_id, title,
You have to make a join for retrieve the votes and the video title, between the two tables like this :
SELECT submissions.submission_id
, COUNT(1)
, submissions.videoname
FROM submissions LEFT OUTER JOIN votes
ON votes.video_id = submissions.submission_id
GROUP BY submissions.submission_id, submissions.videoname

MySQL query already GROUPed and ORDERed : how to ORDER inside the GROUPs?

I have this query:
SELECT id_user, COUNT(*) as count
FROM posts
GROUP BY id_user
ORDER BY COUNT(*) DESC
which gives me the id_user ordered by occurrences, and the number of each occurrence.
Can I get, in the same request, the LAST post from each 'id_user'? i.e. I want to select the last 'post' too, but when I do
SELECT id_user, post, COUNT(*) as count
Tthe value in 'post' isn't the last one (nor the first one; actually I don't know how groups are ordered). Should I run another query?
I believe u can accomplish this by adding max(post_id) last_post to your select.
This ought to do it in one query:
SELECT
p.id_user,
ap.post AS last_post,
COUNT(*) as count
FROM
posts p
JOIN posts ap on (
p.id_user = ap.id_user
AND ap.post_id = (
SELECT MAX(post_id) FROM posts ip WHERE p.id_user = ip.id_user
)
GROUP BY
p.id_user,
ap.post
ORDER BY
COUNT(*) DESC