Finding username of the last post in mysql table joining 3 tables - php

I know I am doing this qry wrong - its taking over 10secs for the results to come back w/ only 3000 rows of data...
I have 3 tables:
users
id
username
DATA:
1|tom
2|dick
3|harry
posts
id
id_users
DATA:
1|1
2|1
3|1
4|2
5|2
6|3
cronjobs
id
id_post
id_wall
DATA: id|id_post|id_wall
1|1|1
2|1|2
3|1|3
4|1|4
5|1|5
6|2|5
7|4|3
8|6|3
9|4|4
A user will make a post
That post will be put on one or more walls and stored in the cronjobs table.
The id on cronjobs is auto increment.
I need to get the username and last post on each wall.
In the above example
Dick was the last person to post on wall 4 with post 4
Harry was the last person to post on wall 3 with post 6
Tom was the last person to post on wall 5 with post 2
Tom was the last person to post on wall 2 with post 1
Tom was the last person to post on wall 1 with post 1
Here is the qry im currently using, I know using the IN clause with the select inside it, is killing this....
SELECT
c.id,
c.id_post,
c.id_wall AS id_wall,
p.id_users AS user_id,
u.NAME AS username
FROM
cronjobs c,
posts p,
users u
WHERE c.id IN
(SELECT MAX(id)
FROM
cronjobs
GROUP BY id_wall)
AND c.id_post = p.id
AND p.id_users = u.id
ORDER BY c.id
Any help is appreciated!

Try this
SELECT
c.id,
c.id_post,
c.id_wall AS id_wall,
p.id_users AS user_id,
u.NAME AS username
FROM
cronjobs c,
posts p,
users u ,
(SELECT MAX(id) as id
FROM
cronjobs
GROUP BY id_wall) table_id
where c.id_post = p.id
AND p.id_users = u.id
and table_id.id =c.id
ORDER BY c.id

You can use join instead of a subquery in where clause, subquery in join part will be executed once while subquery in where clause will be executed for all the resultant rows this query will perform much better than yours, also index your columns that appears in on() part
SELECT
c.id,
c.id_post,
c.id_wall AS id_wall,
p.id_users AS user_id,
u.username AS username
FROM
cronjobs c
join (SELECT MAX(id) id ,id_wall
FROM cronjobs
GROUP BY id_wall) c1 on c.id = c1.id and c.id_wall = c1.id_wall
join posts p on c.id_post = p.id
join users u on p.id_users = u.id
ORDER BY c.id
DEMO

Related

Select each first record with id in another table's result

I have two tables, Users(id, username) and Posts(id, user_id, content). I want to list a summary of them which, I want to list all users and the first post of each user. How can I realize this in one query?
I tried something like.
QUERY
SELECT Users.*, Posts.content
FROM Users, Posts
WHERE Posts.user_id=T_Users.id
But it will return all posts for each user (I cannot add LIMIT 1 at the tail of course which only returns one user).
Some sample records:
Users table:
id username
1 test1
2 test2
Posts table:
id user_id content
1 1 This is a test1's content.
2 1 This is another test1's content.
3 2 This is a test2's content.
And I want the result:
Users.id Users.username Posts.content
1 test1 This is a test1's content.
2 test2 This is a test2's content.
Here is one approach to get the latest record per user i assume the latest record will be considered as the minimum post id
SELECT u.*, p.content
FROM Users u
join Posts p on p.user_id=u.id
join (select user_id ,min(id) id from Posts group by user_id ) p1
on (p.id = p1.id and p.user_id = p1.user_id )
Demo
Try this
select min(x.id) as Id,x.user_id,x.content from(
select p.id,p.user_id,p.content from Users u inner join Posts p
on u.id=p.user_id and u.gender=1
)x group by x.user_id
Have you tried group by clause.
SELECT Users.*, Posts.content
FROM Users, Posts
WHERE Posts.user_id=T_Users.id GROUP BY Posts.user_id

mysql multiple COUNT() from multiple tables with LEFT JOIN

I want to show the conclusion of all users.
I have 3 tables.
table post
post_id(index) user_id
1 1
2 3
3 3
4 4
table photo
photo_id(index) user_id
1 2
2 4
3 1
4 1
table video
photo_id(index) user_id
1 4
2 4
3 3
4 3
and in table user
user_id(index) user_name
1 mark
2 tommy
3 john
4 james
in fact, it has more than 4 rows for every tables.
I want the result like this.
id name post photo videos
1 mark 1 2 0
2 tommy 0 1 0
3 john 2 0 2
4 james 1 1 2
5 .. .. .. ..
Code below is SQL that can work correctly but very slow, I will be true appreciated if you help me how it using LEFT JOIN for it. Thanks.
SQL
"select user.*,
(select count(*) from post where post.userid = user.userid) postCount,
(select count(*) from photo where photo.userid = user.userid) photoCount,
(select count(*) from video where video .userid = user.userid) videoCount
from user order by user.id"
(or ORDER BY postCount, photoCount or videoCount ASC or DESC as i want )
I done researched before but no any helped me.
SELECT u.user_id,
u.user_name,
COUNT(DISTINCT p.post_id) AS `postCount`,
COUNT(DISTINCT ph.photo_id) AS `photoCount`,
COUNT(DISTINCT v.video_id) AS `videoCount`
FROM user u
LEFT JOIN post p
ON p.user_id = u.user_id
LEFT JOIN photo ph
ON ph.user_id = u.user_id
LEFT JOIN video v
ON v.user_id = u.user_id
GROUP BY u.user_id
ORDER BY postCount;
Live DEMO
Your method of doing this is quite reasonable. Here is your query:
select user.*,
(select count(*) from post where post.userid = user.userid) as postCount,
(select count(*) from photo where photo.userid = user.userid) as photoCount,
(select count(*) from video where video.userid = user.userid) as videoCount
from user
order by user.id;
For this query, you want the following indexes:
post(userid)
photo(userid)
video(userid)
user(id)
You probably already have the last one, because user.id is probably the primary key of the table.
Note that a left join approach is a bad idea in this case. The three tables -- posts, photos, and videos -- are independent of each other. If a user has five of each, then joining them together would produce 125 intermediate rows. If a user has fifty of each, it would be 125,000 -- a lot of extra processing.
Your answer is probably slow as it is using a correlated sub-query i.e. the sub query is running once for each user_id (unless the optimizer is doing something smart - which shouldn't be counted on).
You could use a left outer join and count or use something temporary like:
SELECT u.user_id,
u.user_name,
ph.user_count AS 'photoCount',
p.user_count AS 'postCount',
v.user_count AS 'videoCount'
FROM user u
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM photo
GROUP BY user_id
) ph
ON ph.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM post
GROUP BY user_id
) p
ON p.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM video
GROUP BY user_id
) v
ON v.user_id=u.user_id
There are pros and cons for both (depending on indexes). Always have a look at the query plan (using EXPLAIN for MySQL).

MySql inner join takes more than 10 seconds

I have two tables posts and followings
posts (id,userid,post,timestamp) 30 000 rows
and
followings(id_me,userid) 90 000 rows
I want to get lattest 10 posts form posts table based on the people i follow and my posts
SELECT p.*
FROM posts as p INNER JOIN
followings as f
ON (f.id_me=(my user id) AND p.userid=f.userid )
OR
p.userid=(my user id)
ORDER BY id DESC LIMIT 10
But it takes about 10-15 seconds to return. Thanks in advance!
First, remove the filter from the join clause, let the join just correlate the joining tables.
(
SELECT p.*
FROM posts as p
INNER JOIN followings as f ON p.userid=f.userid
where f.id_me=(my user id)
UNION
SELECT p.*
FROM posts as p
where p.userid=(my user id)
)
ORDER BY id DESC LIMIT 10
second, verify your indexes if that ids got no indexes it ill perform a full table scan for each cartesian product of both tables (30k x 90k =~ 3700k pairs being compared)
third, if you don't follow yourself you need a union from post you are following and your posts
Using an OR in SQL is a performance killer, try this:
SELEC p.*
FROM posts as p INNER JOIN
followings as f
ON (f.id_me=(my user id) AND p.userid IN (f.userid,(my user id)))
ORDER BY id DESC LIMIT 10
Do this query using union:
(SELECT p.*
FROM posts p INNER JOIN
followings f
ON (f.id_me=(my user id) AND p.userid=f.userid
)
union
(select p.*
from posts p
where p.userid=(my user id)
)
ORDER BY id DESC
LIMIT 10
If the two conditions never overlap, then use union all instead.
An OR condition like that prevents the query optimizer from making use of indexes. Use a UNION instead:
SELECT *
FROM (SELECT p.*
FROM posts as p
INNER JOIN followings as f
ON f.id_me=(my user id) AND p.userid=f.userid
UNION
SELECT *
FROM posts
WHERE userid = (my user id)) u
ORDER BY id DESC
LIMIT 10
It might be just me but I think your WHERE clause is in an inefficient location:
SELECT
p.*
FROM
posts p
INNER JOIN
followings f
ON p.userid=f.userid
WHERE
MyUserID IN (p.userid, f.id_me)
ORDER BY
id DESC
LIMIT
10
I read in comments that you have the required indexes. The problem is the query. Combining OR with a JOIN confuses the poor and (often) dumb optimizer. The LIMIT 10 should be helpful but the optimizer is not (yet) smart enough to make the best plan.
Try this query:
( SELECT p.*
FROM posts AS p
JOIN followings AS f
ON f.id_me = (my_user_id)
AND p.userid = f.userid
ORDER BY p.id DESC
LIMIT 10
)
UNION ALL
( SELECT p.*
FROM posts AS p
WHERE p.userid = (my_user_id)
ORDER BY p.id DESC
LIMIT 10
) AS x
ORDER BY id DESC
LIMIT 10 ;

How to get all posts from each user?

I have the table users with information about every user. Then the table posts with information about articles and finally the table user_posts, which contains following columns:
user_id
post_id
...
I am trying to get the chart of users with the highest count of posts. I made this query:
SELECT u.id as uid, u.name as uname,
count(up.id) as up_count
FROM users as u JOIN user_posts as up ON up.user_id = u.id ORDER BY vcount DESC LIMIT 25
This query returns me only one user and the total count of all rows in the table user_posts.
What am I doing wrong? I need to get the list of 25 users sorted by the count of articles that published each user.
Thank you in advance
Your query needs to have GROUP BY clause because you have used COUNT() function.
SELECT u.id as uid,
u.name as uname,
count(up.id) as up_count
FROM users as u
LEFT JOIN user_posts as up
ON up.user_id = u.id
GROUP BY u.id, u.name
ORDER BY up_count DESC LIMIT 25
You must have grouped them by ID otherwise you'll single total count result for all records. One more thing, use LEFT JOIN so even users with no post still will be visible in you result with the score of 0.
SELECT
u.id as uid, u.name as uname, count(up.id) as up_count
FROM users as u
JOIN user_posts as up ON up.user_id = u.id
GROUP BY
u.id, u.name
ORDER BY
vcount
DESC LIMIT 25

Top users(comments)

I have a users table and a comments table and I want to select from users the top users that have the biggest amount of comments from the comments table and order them by numbers of comments
Just the forum comments should be counted(type can be forum,picture,news etc)
table structure
users
id | username | password
comments
id | text | type | author_id
SELECT
u.id,u.username,u.password ,count(c.id) as total
FROM
users u
JOIN comments c on c.author_id = u.id
GROUP BY u.id
ORDER BY total DESC;
This should do the trick, try and let us know how it worked out for you
UPDATE
For details on how to do this you will fall inlove with this article
Update : New Link, old one is broken Databases
Tested on PostgreSQL 8.1:
select users.id, count(comments.author_id)
from users, comments
where users.id = comments.author_id
group by users.id
order by 2 desc
SELECT users.id, users.name, COUNT(comments.id) AS cnt
FROM users
LEFT JOIN comments ON users.id = comments.author_id
WHERE type IN ('forum', 'picture', 'news', 'etc')
GROUP BY users.id
ORDER BY cnt DESC
LIMIT 10
Get the 10 most prolific commenter where the comment type is 'form', 'picture', 'news', or 'etc'.

Categories