Mysql query to select rows and alternatively matching rows on other table - mysql

I've got two tables:
user
ID --- Name
posts
ID --- UserID --- Text --- Postdate
Now I want to select all users and every post they've made in the past 15 minutes. I also want to display every user that didn't didn't even make a post with the matching conditions at all.
I'm currently doing it this way:
$user_q = mysql_query("SELECT * FROM user");
while ($a = mysql_fetch_assoc($user_q))
{
$post_q = mysql_query("SELECT * FROM posts WHERE Userid=".$a['ID']." AND Postdate >= DATE_SUB(NOW(), INTERVAL 15 MINUTE)");
//Do anything with this information
}
Do you have any ideas how I can put all this information in just one query? Doing so many queries makes the server running very slow.

What you want is a left outer join:
select u.*, p.*
from users u left outer join
posts p
on u.id = p.userid and
p.Postdate >= DATE_SUB(NOW(), INTERVAL 15 MINUTE);
The left outer join keeps all the rows in the first table. If nothing matches in the second table, using the condition, then the values are NULL for those fields.
EDIT:
If you want to limit this to 50 random users, you can do this at the users level:
select u.*, p.*
from (select u.*
from users u
order by rand()
limit 50
) u left outer join
posts p
on u.id = p.userid and
p.Postdate >= DATE_SUB(NOW(), INTERVAL 15 MINUTE);
The order by rand() makes them random. You could order by anything -- name, date created, whatever. You can even leave the order by out and just take 50 arbitrary rows returned by the database.

Related

MySQL GROUP BY w/ ORDER BY not having desired result

I have a query that attempts to retrieve IDs of people, but only if those people have more than one address. I'm also checking that the last time I called them was at least 30 days ago. Finally, I'm trying to order the results, because I want to pull up results with the oldest last_called datetime:
SELECT
p.id,
COUNT(*) AS cnt
FROM
people p
LEFT JOIN addresses a
ON p.id = a.id
WHERE p.last_called <= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY p.id
HAVING COUNT(*) > 1
ORDER BY p.last_called ASC
LIMIT 25
Right now, the results are not excluding people with only one address. I haven't even got to the point where I know if the sort order is correct, but right now I'd just like to know why it is that my query isn't pulling up results where there is at least 2 addresses for the person.
If you don't want to include people with no address then I would recommend using INNER JOIN instead of LEFT JOIN and DISTINCT to get distinct address ids
(just in case if you have duplicate mappings), e.g.:
SELECT
p.id,
COUNT(DISTINCT(a.id)) AS cnt
FROM
people p
JOIN addresses a
ON p.id= a.peopleid
WHERE p.last_called <= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY p.id
HAVING COUNT(DISTINCT(a.id)) > 1
As far as Ordering is concerned, MySQL evaluates GROUP BY before ordering the results and hence, you need to wrap the query inside another query to get the ordered results.
Update
Instead of joining on aid, you need to join on peopleId of an address record to get the people record.

joining two tables based on recent logged in datetime

I am working on MySql and have two tables login_users and login_timestamps
Table login_users keeps a record of user_id, name and address whereas table login_timestamps keeps a record of user_id and timestamp, this table adds a new entry each time user logs in, so for example if the user_id '1' logs in 10 times a day, this table will have 10 entries for user_id '1' for today.
Now I need to fetch user profiles based on their last logged in time.
for example if there are 3 users, the MySql query should give me 3 records with their latest logged in time.
The query I am using is
SELECT * FROM login_users LEFT JOIN login_timestamps ON login_users.user_id = login_timestamps.user_id ORDER BY login_timestamps.timestamp DESC
but this gives me all the previous logged in entries rather than the recent one only.
Of course you will get all logged in entries while you didnt specify when or what day or something , hete you need a where clause.
try that:
SELECT * FROM login_users
LEFT JOIN login_timestamps ON login_users.user_id = login_timestamps.user_id
WHERE DATE(`timestamp`) = CURDATE()
ORDER BY login_timestamps.timestamp DESC
this will give you entries for curent day. of course you can specify any condition you want.
EDIT: from your comment.
try that
SELECT l.user_id, max(timestamp) as lasttime
FROM login_users l
LEFT JOIN login_timestamps lt ON l.user_id = lt.user_id
GROUP BY l.user_id
ORDER BY lasttime DESC

Select data from same field but in two different rows, then output to one row

I have a follow table that has follower and a followee fields that represents user_id in a user table. The user table has the names of the users, called full_name. I want to create a query that pushes an update from a post table to all followees when a follower posts. Results to to look like this:
user.full_name of followee, user.fullname of follower, update of followee.
Code I have is:
SELECT user.user_id, user.user_id, post.update
FROM follow
JOIN post ON post.user_id = follow.follower_user_id
JOIN user ON user.user_id = follow.followee_user_id
Where update.date > DATE_SUB(CURDATE(),INTERVAL 0 DAY)
GROUP BY follow.followee_user_id
It does the right query, but I can't get the follower name to populate. it just duplicates the followee. Any idea on how to write the select fields?
You need two joins on the user table - one for the follower and one for the followee:
SELECT followee_user.full_name AS followee_name,
follower_user.full_name AS follower_name,
post.update
FROM follow
INNER JOIN user AS followee_user
ON follow.followee_user_id = followee_user.user_id
INNER JOIN user AS follower_user
ON follow.follower_user_id = follower_user.user_id
INNER JOIN post ON follow.followee_user_id = post.user_id
WHERE post.update > DATE_SUB(CURDATE(), INTERVAL 0 DAY)
Note that you must provide aliases for each instance of the joined user table to disambiguate the two. In this case I have used "followee_user" and "follower_user".
I am not sure what update.date is so I have assumed it is a typo and you meant update; change this as necessary. Also the GROUP BY does not make any sense here so I have removed it.
You need to join to the user table two times, once for the follower role, and once for the followee role... something like this:
SELECT e.full_name AS followee_full_name
, r.full_name AS follower_full_name
, p.update
FROM follow f
JOIN post p ON p.user_id = f.follower_user_id
JOIN user r ON r.user_id = f.follower_user_id
JOIN user e ON e.user_id = f.followee_user_id
WHERE p.update_date > CURDATE() + INTERVAL 0 DAY

Mysql: How to select top 10 users of the last 24 hours for a ranking list (and avoid multiple user entries)

I want to list the top 10 users in the last 24 hours with the highest WPM (words per minute) value. If a user has multiple highscores, only the highest score should be shown. I got this far:
SELECT results.*, users.username
FROM results
JOIN users
ON (results.user_id = users.id)
WHERE results.user_id != 0 AND results.created >= DATE_SUB(NOW(),INTERVAL 24 HOUR)
GROUP BY results.user_id
ORDER BY wpm DESC LIMIT 10
My Problem is, that my code doesn't fetch the highest value. For example:
user x has 2 highscores but instead of selecting the row with the highest wpm for this user, the row with a lower value is selected instead. If I use "MAX(results.wpm)" I get the highest wpm. This would be fine, but I also need the keystrokes for this row. My problem is that even though I fetch the correct user I don't receive the right row for this user (the row which made the user reach the top 10).
This is the results table:
id | user_id | wpm | keystrokes | count_correct_words |
count_wrong_words | created
(editing answer as we cannot use LIMIT inside a subquery)
Here's another attempt...
SELECT users.username, R1.*
FROM users
JOIN results R1 ON users.userId = R1.userId
JOIN (SELECT userId, MAX(wpm) AS wpm FROM results GROUP BY userId) R2 ON R2.wpm = R1.wpm AND R2.userId = R1.userId
WHERE R1.user_id != 0 AND R1.created >= DATE_SUB(NOW(),INTERVAL 24 HOUR)
ORDER BY R1.wpm DESC LIMIT 10;
We use max() to first isolate the maximum wpm's for every user_id, then inner join the Results table with this subset to get the full row information.
Thanks Oceanic, I think your subquery approach was what gave me the working idea:
The problem was, that GROUP BY picked the first column for the aggregation(?), I now use a subquery to first order the results by wpm and use this "tmp table" for my operation
SELECT t1.*, users.username
FROM (SELECT results.* FROM results WHERE results.user_id != 0 AND results.created >= DATE_SUB(NOW(),INTERVAL 24 HOUR) ORDER BY wpm DESC) t1
JOIN users ON (t1.user_id = users.id)
GROUP BY t1.user_id
ORDER BY wpm DESC
LIMIT 10
This seems to work fine.

MySQL subtracting multiple times for same row in update

I have a table of comments and a table of posts
Whenever a post is deleted, a query runs to subtract the number of comments (that are deleted later) from each user's comment_count
So if a user has 2 comments in a post, and that post is deleted, their balance should have 2 subtracted from it
My query is as follows:
UPDATE users
INNER JOIN comment ON users.id = comment.author
SET comment_count = comment_count - 1
WHERE comment.post = 1
User A has 2 comments with .post = 1, but for some reason that user only gets comment_count subtracted by 1 once, when it should happen twice
I think my syntax is right because when I:
SELECT *
FROM users
INNER JOIN comment ON users.id = comment.author
WHERE comment.post = 1
I get two results for user A
Shouldn't UPDATE be iterating over those two results, subtracting each time?
Can someone explain what I am missing? thank you
If you're going to store the count, use:
UPDATE USERS
SET comment_count = (SELECT COUNT(*)
FROM COMMENT c
WHERE c.author = USERS.id)
...or:
UPDATE USERS u
JOIN (SELECT c.author,
COUNT(*) AS numComments
FROM COMMENT c
GROUP BY c.author) x ON x.author = u.id
SET comment_count = x.numComments
There's no point in relying on two records to subtract twice, when you could perform the operation once.
I prefer not to store such values, because they can be calculated based on records without the hassle of keeping the counts in sync. A view might be a better idea...