I have a query in mysql with 2 select from's. When I run these queries individually they run quick within 1 second. But when I combine them with union all, the website freeze's and it takes atleast 20 seconds for the same query to execute in union all.
Any idea why this happens? Can't figure it out.
See the query below:
SELECT p.user_id, p.description, p.post_id, p.created_at, ps.username AS size_name, ps.username AS user_name, ps.avatar AS avatar, pz.title AS title, pz.slug AS slug
FROM comments p
JOIN users ps ON ps.id = p.user_id
JOIN posts pz ON pz.id = p.post_id
UNION ALL
SELECT p2.user_id, p2.description, p2.post_id, p2.created_at, ps2.username AS size_name, ps2.username AS user_name, ps2.avatar AS avatar, pz2.title AS title, pz2.slug AS slug
FROM reply p2
JOIN users ps2 ON ps2.id = p2.user_id
JOIN posts pz2 ON pz2.id = p2.post_id
order by created_at DESC
LIMIT 10
Before merging with UNION, reduce the size of the tables returned by each subquery. Since you only want the top 10, you only need the top 10 of each subquery.
SELECT *
FROM (
(SELECT p.user_id, p.description, p.post_id, p.created_at, ps.username AS size_name, ps.username AS user_name, ps.avatar AS avatar, pz.title AS title, pz.slug AS slug
FROM comments p
JOIN users ps ON ps.id = p.user_id
JOIN posts pz ON pz.id = p.post_id
ORDER BY created_at DESC
LIMIT 10)
UNION ALL
(
SELECT p2.user_id, p2.description, p2.post_id, p2.created_at, ps2.username AS size_name, ps2.username AS user_name, ps2.avatar AS avatar, pz2.title AS title, pz2.slug AS slug
FROM reply p2
JOIN users ps2 ON ps2.id = p2.user_id
JOIN posts pz2 ON pz2.id = p2.post_id
ORDER BY created_at DESC
LIMIT 10)
) AS x
order by created_at DESC
LIMIT 10
UNION defaults to UNION DISTINCT. If you want UNION ALL, explictly say that (and you should, wherever possible, since UNION DISTINCT has to do a lot of work to make them distinct).
The other thing is that the order by and limit apply to the query as a whole. You may want to make each unioned query have them too. See https://dev.mysql.com/doc/refman/8.0/en/union.html:
ORDER BY and LIMIT in Unions
To apply an ORDER BY or LIMIT clause to an individual SELECT, parenthesize the SELECT and place the clause inside the parentheses:
(SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);
If your fast individual versions did have those, that should help. So something like this:
(SELECT p.user_id, p.description, p.post_id, p.created_at, ps.username AS size_name, ps.username AS user_name, ps.avatar AS avatar, pz.title AS title, pz.slug AS slug
FROM comments p
JOIN users ps ON ps.id = p.user_id
JOIN posts pz ON pz.id = p.post_id
order by created_at DESC
LIMIT 10)
UNION ALL
(SELECT p2.user_id, p2.description, p2.post_id, p2.created_at, ps2.username AS size_name, ps2.username AS user_name, ps2.avatar AS avatar, pz2.title AS title, pz2.slug AS slug
FROM reply p2
JOIN users ps2 ON ps2.id = p2.user_id
JOIN posts pz2 ON pz2.id = p2.post_id
order by created_at DESC
LIMIT 10)
order by created_at DESC
LIMIT 10
Related
I have 3 counts count(like), COUNT(comment), COUNT(views), what I want is to addition of this 3 in that query and get the total addition of this 3 counts.
Note:- I am using 3 joins for get this 3 count and group by for group by posts.
e.g. -
COUNT(like) = 5
COUNT(comment) = 3
COUNT(views) = 12
so i need a key total_count = 20
Is this possible?
SELECT up.id, COUNT(upl.id) as likes_count, COUNT(upc.id) as collected_count, COUNT(upvb.id) as viewed_by_count
FROM posts as up
LEFT JOIN post_likes as upl ON upl.post_id = up.id
LEFT JOIN post_viewd_by as upvb ON upvb.post_id = up.id
LEFT JOIN post_collected as upc ON upc.post_id = up.id WHERE up.status = 'Active' GROUP BY up.id
ORDER BY likes_count DESC, up.insertdate DESC
try this
SELECT up.id, COUNT(upl.id) as likes_count, COUNT(upc.id) as collected_count, COUNT(upvb.id) as viewed_by_count , COUNT(upl.id)+COUNT(upc.id)+COUNT(upvb.id) AS Total
FROM posts as up
LEFT JOIN post_likes as upl ON upl.post_id = up.id
LEFT JOIN post_viewd_by as upvb ON upvb.post_id = up.id
LEFT JOIN post_collected as upc ON upc.post_id = up.id
WHERE up.status = 'Active'
GROUP BY up.id
ORDER BY likes_count DESC, up.insertdate DESC
If you want correct counts, then one method is correlated subqueries:
SELECT up.id,
(likes_count + viewed_by_count + collected_count) as total
FROM (SELECT up.*,
(SELECT COUNT(*)
FROM post_likes upl
WHERE upl.post_id = up.id
) as likes_count,
(SELECT COUNT(*)
FROM post_viewd_by upvb
WHERE upvb.post_id = up.id
) as viewed_by_count,
(SELECT COUNT(*)
FROM post_collected pc
WHERE pc.post_id = up.id
) as collected_count
FROM posts up
WHERE up.status = 'Active'
) up
ORDER BY likes_count DESC, up.insertdate DESC;
Assuming you have an index on the post_id in all the subsidiary tables, this should also be the fastest method.
i want to display three replies from each user i have in my users table, so for instance if i have 3 users and each of them had replied to lets say 10 messages, i want my query to only retrieve 9 replies and not all of the replies in my messages_reply table.
heres what i tried:
$replyquery="select *
from messages_reply
LEFT JOIN users
ON messages_reply.from_id=users.id
GROUP BY messages_reply.id LIMIT 3";
i know that what i wrote means that bring me 3 replies only, so how do i bring 3 replies from each user in my users table?
In many databases, you can use row_number() for this:
select *
from (
select mr.*, u.*, row_number() over(partition by u.id order by mr.id desc) rn
from messages_reply mr
inner join users u on mr.from_id = u.id
) t
where rn <= 3
If you are running MySQL < 8.0, as I suspect from the lax use of group by in your query:
select mr.*, u.*
from messages_reply mr
inner join users u on mr.from_id = u.id
where mr.id >= (
select mr1.id
from messages_reply mr1
where mr1.from_id = u.id
order by mr1.id desc
limit 2, 1
)
This gives you the 3 message replies with the greatest id for each user.
Query not tested, just a concept
SELECT *
FROM users
LEFT JOIN (
SELECT *
FROM messages_reply
WHERE from_id = users.id
ORDER BY <your wanted order field> DESC
LIMIT 3) replies
ON users.id = replies.from_id
I am making a query where I sort posts by upvotes. I have table posts and votes(post_id, user_id, vote), where vote can be 1 or -1. So now my problem is if post does not have any upvotes it won't show in result at all.
My query:
SELECT P.* FROM `vicoteka-api`.posts P
INNER JOIN (
SELECT post_id, COUNT(*) vote_count FROM `vicoteka-api`.votes
WHERE vote = 1 GROUP BY post_id
) V ON P.id = V.post_id
ORDER BY V.vote_count DESC
How can I include posts that don't exist in votes pivot table?
Use a left join with coalesce to get posts with no votes.
SELECT P.*,COALESCE(v.vote_count,0) as vote_count
FROM `vicoteka-api`.posts P
LEFT JOIN (SELECT post_id, COUNT(*) vote_count
FROM `vicoteka-api`.votes
WHERE vote = 1
GROUP BY post_id
) V ON P.id = V.post_id
ORDER BY vote_count DESC
Vamsi's answer is correct. If performance is a consideration, you might want to compare it to:
SELECT P.*, COUNT(v.post_id) as vote_count
FROM vicoteka-api.posts P LEFT JOIN
vicoteka-api.votes v
ON P.id = V.post_id AND v.vote = 1
GROUP BY p.id
ORDER BY vote_count DESC;
Or, what might even be better:
SELECT p.*,
(SELECT COUNT(*)
FROM vicoteka-api.votes v
WHERE P.id = V.post_id AND v.vote = 1
) as vote_count
FROM vicoteka-api.posts p
ORDER BY vote_count DESC;
The latter two allow an index to be used for the JOIN. The last one even saves effort on the GROUP BY.
SELECT P.*, V.POST_ID, V_COUNT, VOTE_COUNT FROM `vicoteka-api`.posts P
RIGHT OUTER JOIN (
SELECT post_id, COUNT( NVL(vote_count),0) AS V_COUNT, VOTE_COUNT
FROM `vicoteka-api`.votes
GROUP BY post_id, vote_count
) V ON P.id = V.post_id and V.vote_count <> -1
ORDER BY V.vote_count DESC
Correct me if I am wrong.
What I want:
SELECT u.username, u.last_activity
FROM users_userprofile
WHERE u.id IN (
SELECT DISTINCT(p.user_id) FROM forums_post p
WHERE p.thread_id = 423993
ORDER BY p.created_at DESC
LIMIT 4
);
This doesn't work because of LIMIT in subquery. I want to keep order of subquery but I want to get username and last_activity instead of user_id.
Any suggestion how I could achieve this?
Replace the subquery with a view:
CREATE VIEW subv AS SELECT p.user_id FROM forums_post p
WHERE p.thread_id = 423993
ORDER BY p.created_at DESC
LIMIT 4;
SELECT u.username, u.last_activity
FROM users_userprofile
WHERE u.id IN (SELECT * FROM subv);
you could use join for the table and the subquery instead of using where in:
SELECT u.username, u.last_activity
FROM users_userprofile u
JOIN (
SELECT p.user_id FROM forums_post p
WHERE p.thread_id = 423993
ORDER BY p.created_at DESC
LIMIT 4
) q
on u.user_id=q.user_id
Why woldn't you do it with a JOIN? There seems to be no performance impact because WHERE and LIMIT clauses are the same. It won't JOIN the whole tables:
SELECT p.user_id, u.username, u.last_activity
FROM users_userprofile u
JOIN forums_post p ON p.user_id = u.id
WHERE p.thread_id = 423993
GROUP BY p.user_id ORDER BY MAX(p.created_at) DESC
LIMIT 4
I am attempting to work with the following tables:
users (id, name)
topics (id, name, slug)
posts (id, title, content, topicid, authorid, datetime, slug)
replies (id, authorid, threadid, content, datetime)
I want to create a list of posts ordered by latest reply (or the post date if there are no replies). Each listing should contain: the post title, the number of replies, the date of the post, the date of the latest reply, the name of the author, the name of the person who last replied, etc.
I can get everything except the date and author of the latest reply. The date can be done via MAX(replies.datetime), but I'm not sure how to get the latest author.
My last attempt was trying to use an exclusion join:
select posts.id, posts.title, posts.authorid, posts.topicid, posts.content, posts.datetime, count(replies.id) as replies, r2.datetime, replies.datetime, GREATEST(posts.datetime, coalesce(max(replies.datetime), posts.datetime)) as latesttime, users.name as author, commenters.name as commenter, r2.id
from posts
left join replies on replies.threadid = posts.id
left join users on users.id = posts.authorid
left join users commenters on commenters.id = replies.authorid
left join replies as r2 on replies.id = r2.id and replies.datetime < r2.datetime
where r2.id is null
group by posts.id
order by latesttime DESC, replies.datetime DESC
limit 20;
Unfortunately, this still won't retrieve the latest comment author. Any ideas?
You have to first start with an inner most query on the max ID for a given thread and what it's REPLY ID was joined to the original replies to get ONE instance... From that, you can get the reply author's information. THEN, get original posting information as needed.
SELECT
p.id,
p.title,
p.content,
p.topicid,
p.authorid,
p.datetime as postdate,
p.slug,
postUser.Name as PostAuthor,
coalesce( MaxReplyUser.NumReplies, 0 ) NumReplies,
coalesce( MaxReplyUser.name, '' ) ReplyUser,
coalesce( MaxReplyUser.AuthorID, 0 ) ReplyAuthor,
coalesce( MaxReplyUser.ThreadID, 0 ) ReplyThread,
coalesce( MaxReplyUser.DateTime, '' ) ReplyDateTime,
coalesce( MaxReplyUser.Content, '' ) ReplyContent
from
Posts p
left join
( SELECT
u1.name,
r1.authorid,
r1.threadid,
r1.datetime,
r1.content,
MaxReplies.NumReplies
from
( SELECT
threadid,
COUNT(*) as NumReplies,
MAX( id ) as MaxReplyID
from
replies
group by
threadID ) MaxReplies
INNER JOIN replies r1
ON MaxReplies.MaxReplyID = r1.id
INNER JOIN Users u1
ON r1.AuthorID = u1.ID ) MaxReplyUser
ON p.id = MaxReplyUser.threadID
inner join users postUser
ON p.authorid = postuser.id
order by
if( MaxReplyUser.threadID IS NULL, p.DateTime, MaxReplyUser.DateTime ) DESC
uld you test it?
SELECT posts.id, posts.title, posts.authorid, posts.topicid, posts.content, posts.datetime,
(SELECT name FROM users WHERE id = posts.autorid) "Author",
(SELECT COUNT(*) FROM replies WHERE threadid = posts.id) "Replies",
(SELECT MAX(datetime) FROM replies r WHERE threadid = posts.id) "Lastreplytime",
(SELECT (SELECT name FROM users WHERE id = r.authorid LIMIT 1) FROM replies r WHERE threadid = posts.id ORDER BY datetime DESC LIMIT 1)
FROM posts
ORDER BY Lastreplytime DESC
LIMIT 20;