Mysql - Order By Sql Query - mysql

I want to order my query with total participants, total comments and total likes. But this isn't working. Any solutions?
And this query will be very hard for server when users number increased, is there any suggestion to optimize query? Thanks.
SELECT P.*,
distance_in_meters_lat_lng(P.post_latitude, P.post_longitude, :latitude, :longitude) as distance,
U.user_id,
U.user_name,
U.user_lastName,
U.user_photo,
LT.time_hour,
LC.category_name,
(SELECT COUNT(*) FROM posts_likes PL WHERE PL.post_id = P.post_id) as likes,
(SELECT COUNT(*) FROM comments C WHERE C.post_id = P.post_id) as comments,
(SELECT COUNT(*) FROM posts_likes PL2 WHERE PL2.post_id = P.post_id AND PL2.user_id = :user_id) as like_status,
(SELECT COUNT(*) FROM post_participants PC WHERE PC.post_id = P.post_id) as participants,
(SELECT COUNT(*) FROM post_participants PC2 WHERE PC2.post_id = P.post_id AND PC2.user_id = :user_id) as participant_status,
(SELECT user_id FROM post_participants PC3 WHERE PC3.post_id = P.post_id AND is_winner = 1) as winner,
(SELECT user_name FROM post_participants PC3 WHERE PC3.post_id = P.post_id AND is_winner = 1) as name,
(SELECT user_lastName FROM post_participants PC3 WHERE PC3.post_id = P.post_id AND is_winner = 1) as lastName,
(SELECT user_photo FROM post_participants PC3 WHERE PC3.post_id = P.post_id AND is_winner = 1) as photo
FROM posts P
INNER JOIN users U USING(user_id)
LEFT JOIN lot_times LT USING(time_id)
LEFT JOIN lot_categories LC USING(category_id)
WHERE P.user_id NOT IN (:list)
AND P.post_type = 'lot'
ORDER BY participants, likes, comments DESC
LIMIT 0, 20

This query is really big so subqueries would definitely take time, here you should use direct.
Hope this helps
SELECT
P.*,
distance_in_meters_lat_lng(P.post_latitude, P.post_longitude, :latitude, :longitude) as distance,
U.user_id,
U.user_name,
U.user_lastName,
U.user_photo,
LT.time_hour,
LC.category_name,
COUNT(PL.*) as likes,
COUNT(C.*) as comments,
COUNT(PL2.*) as like_status,
COUNT(PC.*) as participants,
COUNT(PC2.*) as participant_status,
PC3.user_id as winner,
PC3.user_name as name,
PC3.user_lastName as lastName,
PC3.user_photo as photo,
FROM
posts P
JOIN
posts_likes PL USING (post_id)
JOIN
comments C USING (post_id)
JOIN
posts_likes PL2 ON (PL2.post_id = P.post_id AND PL2.user_id = :user_id)
JOIN
post_participants PC USING (post_id)
JOIN
post_participants PC2 ON (PC2.post_id = P.post_id AND PC2.user_id = :user_id)
JOIN
post_participants PC3 ON (PC3.post_id = P.post_id AND is_winner = 1)
INNER JOIN
users U USING (user_id)
LEFT JOIN
lot_times LT USING (time_id)
LEFT JOIN
lot_categories LC USING (category_id)
WHERE
P.user_id NOT IN (:list) AND P.post_type = 'lot'
ORDER BY
participants, likes, comments DESC
LIMIT 0, 20

As you are using join in your query anyway, I would recommend joining comments and likes table and get the counts in select, eg.:
select p.id, count(l.id) as like_count, count(c.id) as comment_count
from post p
left join likes l on p.id = l.post_id
left join comments c on p.id = c.post_id
order by like_count desc, comment_count desc;
Here is the SQL Fiddle.

At last I found answer myself.
SELECT P.*,
distance_in_meters_lat_lng(P.post_latitude, P.post_longitude, :latitude, :longitude) as distance,
U.user_id,
U.user_name,
U.user_lastName,
U.user_photo,
LT.time_hour,
LC.category_name,
(SELECT COUNT(*) FROM posts_likes PL WHERE PL.post_id = P.post_id) as likes,
(SELECT COUNT(*) FROM comments C WHERE C.post_id = P.post_id) as comments,
(SELECT COUNT(*) FROM posts_likes PL2 WHERE PL2.post_id = P.post_id AND PL2.user_id = :user_id) as like_status,
(SELECT COUNT(*) FROM post_participants PC WHERE PC.post_id = P.post_id) as participants,
(SELECT COUNT(*) FROM post_participants PC2 WHERE PC2.post_id = P.post_id AND PC2.user_id = :user_id) as participant_status,
(SELECT user_id FROM post_participants PC3 WHERE PC3.post_id = P.post_id AND is_winner = 1) as winner,
(SELECT user_name FROM post_participants PC3 WHERE PC3.post_id = P.post_id AND is_winner = 1) as name,
(SELECT user_lastName FROM post_participants PC3 WHERE PC3.post_id = P.post_id AND is_winner = 1) as lastName,
(SELECT user_photo FROM post_participants PC3 WHERE PC3.post_id = P.post_id AND is_winner = 1) as photo
FROM posts P
INNER JOIN users U USING(user_id)
LEFT JOIN lot_times LT USING(time_id)
LEFT JOIN lot_categories LC USING(category_id)
WHERE P.post_type = 'lot'
ORDER BY participants DESC, likes DESC, comments DESC
LIMIT 0,20

Related

MySQL - Right join OR Right join

I'm building a social application.
I'd like to select all the posts, that the user has interacted with in some way (liked, commented, or liked a comment)
How could I achieve that? I'm imagining something like this:
SELECT p.* FROM posts p
RIGHT JOIN postLikes pl ON pl.postId = p.id AND pl.userId = :userId
OR
RIGHT JOIN postComments pc ON pc.postId = p.id AND pc.userId = :userId
OR
RIGHT JOIN postCommentLikes pcl ON pcl.postId = p.id AND pcl.userId = :userId
GROUP BY p.id
ORDER BY p.id DESC LIMIT :startIndex, 20
I'd like to achieve this in one query, because I have a paging system (20 posts / page)
Using UNION ALL would mess up this system.
See:
SELECT res.* FROM ((SELECT p.*, p.id AS postId FROM posts p
RIGHT JOIN postLikes pl ON pl.postId = p.id AND pl.userId = :userId)
UNION ALL
(SELECT * FROM posts p.*, p.id AS postId
RIGHT JOIN postComments pc ON pc.postId = p.id AND pc.userId = :userId)
UNION ALL
(SELECT * FROM posts p.*, p.id AS postId
RIGHT JOIN postCommentLikes pcl ON pcl.postId = p.id AND pcl.userId = :userId)) AS res
GROUP BY postId
ORDER BY postId DESC LIMIT :startIndex, 20
This way the order would be messed up, and therefore I wouldn't know what's the right startIndex
You can use EXISTS like this:
SELECT p.* FROM posts p
WHERE
EXISTS (SELECT 1 FROM postLikes pl WHERE pl.postId = p.id AND pl.userId = :userId)
OR
EXISTS (SELECT 1 FROM postComments pc WHERE pc.postId = p.id AND pc.userId = :userId)
OR
EXISTS (SELECT 1 FROM postCommentLikes pcl WHERE pcl.postId = p.id AND pcl.userId = :userId)
ORDER BY p.id DESC LIMIT :startIndex, 20
or with UNION and the operator IN:
SELECT * FROM posts
WHERE id IN (
SELECT postId FROM postLikes WHERE userId = :userId
UNION
SELECT postId FROM postComments WHERE userId = :userId
UNION
SELECT postId FROM postCommentLikes WHERE userId = :userId
)
ORDER BY id DESC LIMIT :startIndex, 20

How can I calculate the number of all rows when there are both LIMIT and JOIN clauses?

Here is my query:
select u.id, u.name,
(select count(*) from users where name = u.name) as total
from users u
where u.name = 'anything'
order by id
limit 1
As you know, my query returns 1 user which has anything name. And total contains the number of all users which have anything name. Ok all fine.
Now I want to do the same thing when there are three JOINs in the query. Please assume this:
select u.id, u.name, sum(r.reputation) rep
from users u
join reputation r on u.id = r.user_id
join posts_tags pt on r.post_id = pt.post_id
join tags t on pt.tag_id = t.id
where u.name = 'anything' and t.name = 'mytag'
group by u.id, u.name
order by rep desc, u.id
limit 1
Now I want to know, how can I implement total part in this ^ query?
Try this query ,hope this give you the right result.
select * , count(id) as total from (select u.id, u.name, sum(r.reputation) rep
from users u
join reputation r on u.id = r.user_id
join posts_tags pt on r.post_id = pt.post_id
join tags t on pt.tag_id = t.id
where u.name = 'anything' and t.name = 'mytag'
group by u.id, u.name
order by rep desc, u.id
limit 1 ) as result

Select threads from forum based on comments

I've got the following sql that will return a list of forums. Under each forum it will select the thread with the latest comment. This works fine but when a new thread hasn't got any comments, nothing is returned.
How to tackle this problem?
SELECT f.Id AS forum_id,
f.name AS forum_name,
f.slug AS forum_slug,
f.image AS forum_image,
t.Id AS thread_id,
t.title AS thread_topic,
t.unixtime AS thread_timestamp,
p.Id AS post_id,
p.content AS post_content,
p.unixtime AS post_timestamp,
(SELECT COUNT(*) FROM a_comments o WHERE o.forumID=f.Id AND o.teamId = {$teamId}) comments_count,
(SELECT COUNT(*) FROM a_threads w WHERE w.forumID=f.Id AND w.teamId = {$teamId}) threads_count
FROM a_forums f
LEFT JOIN (SELECT t2.forumID, max(COALESCE(p2.unixtime, t2.unixtime)) as ts, COUNT(p2.unixtime) as post_count
FROM a_threads t2
LEFT JOIN a_comments p2 ON p2.threadId = t2.id
GROUP BY t2.forumId) max_p ON f.id = max_p.forumId
LEFT JOIN a_comments p ON max_p.ts = p.unixtime AND p.teamId = {$teamId} AND p.deleted = 0
LEFT JOIN a_threads t ON f.Id = t.forumID AND (max_p.post_count = 0 OR p.threadId = t.ID) AND t.teamId = {$teamId} AND t.deleted = 0
ORDER BY f.id
I think you just have to change the LEFT JOIN in the first subquery to a JOIN. With the LEFT JOIN, you'll get NULL or a non-valid time for the comment. This then throws off the rest of the logic -- I think.
SELECT f.Id AS forum_id, f.name AS forum_name, f.slug AS forum_slug, f.image AS forum_image,
t.Id AS thread_id, t.title AS thread_topic, t.unixtime AS thread_timestamp,
p.Id AS post_id, p.content AS post_content, p.unixtime AS post_timestamp,
(SELECT COUNT(*) FROM a_comments o WHERE o.forumID=f.Id AND o.teamId = {$teamId}) as comments_count,
(SELECT COUNT(*) FROM a_threads w WHERE w.forumID=f.Id AND w.teamId = {$teamId}) as threads_count
FROM a_forums f LEFT JOIN
(SELECT t2.forumID, max(p2.unixtime) as ts,
COUNT(p2.unixtime) as post_count
FROM a_threads t2 JOIN
a_comments p2
ON p2.threadId = t2.id
GROUP BY t2.forumId
) max_p
ON f.id = max_p.forumId LEFT JOIN
a_comments p
ON max_p.ts = p.unixtime AND p.teamId = {$teamId} AND
p.deleted = 0 LEFT JOIN
a_threads t
ON f.Id = t.forumID AND (max_p.post_count = 0 OR p.threadId = t.ID) AND t.teamId = {$teamId} AND t.deleted = 0
ORDER BY f.id

Duplicate results with mysql query

I've 3 tables to query. I make a select on the first one, depending on the two others. I must have only distinct id from the 1st table, but my query is returning some duplicates... http://sqlfiddle.com/#!2/3e3d6/1
My query:
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND p.id NOT IN (
SELECT post_id
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id);
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND NOT EXISTS(
SELECT null
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id);
Any idea please?
not entirely sure I understand what you are looking for, but I think this is what you want...
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND p.id NOT IN (
SELECT post_id
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id)
GROUP BY p.id;
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND NOT EXISTS(
SELECT null
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id)
GROUP BY p.id;

Query for finding Persons with most points

I am using Mysql and have these tables: (only important columns shown)
Person
id, primary key
Post
id, primary key
points, INT
Visit
id, primary key
person_id, refers to Person
post_id, refers to Post
What I want to find is the Persons (top 5) with most points overall? And the persons with most points on each Post.
Can anyone please guide me? Any help is deeply apreciated!
Top 5 persons with most points overall:
SELECT
p.id,
SUM(Post.points) AS total_points
FROM
Person p
INNER JOIN Visit v
ON p.id = v.person_id
INNER JOIN Post
ON v.post_id = Post.id
GROUP BY
p.id
ORDER BY
SUM(Post.points) DESC
LIMIT 5
Top 5 persons with most points in one post:
SELECT
p.id,
MAX(Post.points) AS best_post_points
FROM
Person p
INNER JOIN Visit v
ON p.id = v.person_id
INNER JOIN Post
ON v.post_id = Post.id
GROUP BY
p.id
ORDER BY
MAX(Post.points) DESC
LIMIT 5
Top 5 posts:
SELECT
p.id,
Post.points
FROM
Person p
INNER JOIN Visit v
ON p.id = v.person_id
INNER JOIN Post
ON v.post_id = Post.id
ORDER BY
Post.points DESC
LIMIT 5
For each Post
SELECT id FROM Person where id in (SELECT person_id FROM Visit where post_id in
(SELECT id FROM Post order by points DESC limit 5))
Overall (not sure if will work, not tested)
SELECT id FROM Person where id in (SELECT distinct(person_id) FROM Visit where post_id in
(SELECT id FROM Post order by points DESC limit 5) GROUP BY person_id )
SELECT *
FROM
(
SELECT P.id , SUM(PP.points)
FROM PERSON P JOIN VISIT V ON ( V.person_id = P.id )
JOIN POST PP JOIN ON ( PP.id = V.post_id )
GROUP BY P.id
ORDER BY PP.points DESC
)
LIMIT 5;
SELECT *
FROM
(
SELECT P.id , COUNT(*) NUM_OF_POST
FROM PERSON P JOIN VISIT V ON ( V.person_id = P.id )
JOIN POST PP JOIN ON ( PP.id = V.post_id )
GROUP BY P.id
ORDER BY NUM_OF_POST DESC
)
LIMIT 5;