Reputation: 323
I have 2 tables:
users
id | user
likes
id | owner | date
And this query:
$owner_result = $connector->query("SELECT id FROM users");
while($owner = $connector->fetchArray($owner_result))
{
$rs = $connector->query("SELECT count(id) AS num FROM likes
WHERE date > DATE_SUB(NOW(), INTERVAL 1 WEEK)
AND owner='$owner[id]'
ORDER BY num DESC LIMIT 5");
while($rw = $connector->fetchArray($rs))
{echo "This owner ($owner[id]) has $rw[num] likes this week";}
}
What i want i to do is to return me 5 users.id rows with the most big ammount of rows in likes table. My query returns me just $owner[id] but not ammount of likes for each one. And i think the performance of my query is very low as well because my query checks amount of likes for every users.id in likes table, but it may be that likes table doesn't contain some users.id .
Any advice for solve my problem or improve my query will be very welcome. Thank you.
Upvotes: 0
Views: 61
Reputation: 108430
I think a single query would get you the specified result, for example:
SELECT u.id
, COUNT(t.owner) AS cnt_likes
FROM users u
JOIN likes t
ON t.owner = u.id
WHERE t.date > DATE_SUB(NOW(), INTERVAL 1 WEEK)
GROUP BY u.id
ORDER BY 2 DESC
LIMIT 5
An appropriate index on the likes
table should improve performance:
... ON likes (owner, date)
Alternatively, this will give an equivalent result, possibly with (slightly) better performance:
SELECT u.id
, t.cnt_likes
FROM users u
JOIN ( SELECT s.owner
, COUNT(1) AS cnt_likes
FROM likes s
WHERE s.date > DATE_SUB(NOW(), INTERVAL 1 WEEK)
GROUP BY s.owner
) t
ON t.owner = u.id
ORDER BY t.cnt_likes DESC
LIMIT 5
You would get even better performance if you were guaranteed that all the values of the owner
column in the likes
table were actually id
values in the user
table... because you could avoid the join to the users table, and get the entire result from the likes
table:
SELECT s.owner
, COUNT(1) AS cnt_likes
FROM likes s
WHERE s.date > DATE_SUB(NOW(), INTERVAL 1 WEEK)
GROUP BY s.owner
ORDER BY cnt_likes DESC
LIMIT 5
But, that query doesn't do the check to verify the value returned from the owner
column exists as in the id
column of the user
table.
Upvotes: 1
Reputation: 10336
You should join the two tables so you can get your data in one single statement:
SELECT
users.id,
COUNT(likes.id) AS mylikes -- specify a name for the computed column
FROM users
INNER JOIN likes
ON users.id = likes.owner
WHERE date > DATE_SUB(NOW(), INTERVAL 1 WEEK)
GROUP BY users.id
ORDER BY COUNT(likes.id) DESC -- MySQL would allow the alias name
LIMIT 5 -- because you want the top 5
If there's a tie, then you will lose a record (or more).
Upvotes: 1
Reputation: 757
You have to do a group by owner in the second query. You can't count rows without group them first.
$owner_result = $connector->query("SELECT id FROM users");
while($owner = $connector->fetchArray($owner_result))
{
$rs = $connector->query("SELECT count(id) AS num FROM likes
WHERE date > DATE_SUB(NOW(), INTERVAL 1 WEEK)
AND owner='$owner[id]'
Group BY owner
ORDER BY num DESC LIMIT 5");
while($rw = $connector->fetchArray($rs))
{echo "This owner ($owner[id]) has $rw[num] likes this week";}
}
Upvotes: 1