I have this query which is a dependant query and taking much execution time
SELECT
u.id,
u.user_name,
ifnull((select longitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
ifnull((select latitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
(select created from map where user_id = 1 order by created desc limit 1) as LatestTime
FROM users as u
WHERE id IN(SELECT
user1_id FROM relation
WHERE users.id = 1)
ORDER BY id;
I tried this query in (dependant)
SELECT
u.id,
u.user_name,
m.map_id,
m.longitude,
m.latitude,
m.Date as created
FROM users as u
left join (select
map_id,
longitude,
latitude,
user_id,
max(created) as `Date`
from map
group by user_id) as m
on m.user_id = u.id
WHERE id IN(SELECT
user1_id FROM relation
WHERE users.id = 1)
ORDER BY id;
The problem is that the first query is dependent and working fine but taking much execution time. With the second query the problem is that it is not fetching the latest created time.
Now i want to optimise this query. The theme is that in subquery i am first making group then i am trying to get the last record of each group. and here is the tables structure.
users : id , user_name
map : map_id , user_id ,longitude , latitude, created
relations : id , user1_id , user2_id , relation
Where performance is needed, subqueries in the SELECT clause are indeed a pain and have to be banished :)
You can rewrite this part:
SELECT
u.id,
u.user_name,
ifnull((select longitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
ifnull((select latitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
(select created from map where user_id = 1 order by created desc limit 1) as LatestTime
FROM users as u
In:
SELECT
u.id,
u.user_name,
COALESCE(m1.longitude, 0) as longitude,
COALESCE(m1.latitude, 0) as latitude
FROM users u
LEFT JOIN map m1 ON m1.user_id = u.id
LEFT JOIN map m2 ON m2.user_id = m1.user_id AND m2.map_id > m1.map_id
WHERE m2.map_id IS NULL
I wrote a short explanation of the query structure in this answer. It's a really nice trick to learn as it is more readable, subquery-less and performance wiser.
I haven't looked at the IN part yet but will if the above didn't help.
Edit1: You can extract the created date and use a MAX() instead.
SELECT
u.id,
u.user_name,
COALESCE(m1.longitude, 0) as longitude,
COALESCE(m1.latitude, 0) as latitude,
created.LatestTime
FROM (SELECT MAX(created) FROM map WHERE user_id = 1) created
INNER JOIN users u ON TRUE
LEFT JOIN map m1 ON m1.user_id = u.id
LEFT JOIN map m2 ON m2.user_id = m1.user_id AND m2.map_id > m1.map_id
WHERE m2.map_id IS NULL
Related
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
My table records three different scores (site, park, division) for the same group of user.
A user's total score is "sum of all the site scores" + "sum of all the park scores" + "sum of all the division scores".
The code is the following and I am using MySQL:
select sum(points)as points, user_name
from
(SELECT sum(site_point) as points, user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(park_point) as points , user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(division_point) as points , user_name from visits v join users u
on u.user_id = v.user_id group by user_name
) V group by user_name order by sum(points) DESC ;
I want to display only the users whose score is in top 5 and their scores.
Ten users may have the same highest score. I need all of them being displayed.
I appreciate any help.
I think this is what you want:
select user_name,
sum(site_point + park_point + division_point) as points
from visits v join
users u
on u.user_id = v.user_id
group by user_name
order by points desc
limit 5;
A single aggregation simplifies the calculation.
You may not realize it, but the union is going to return incorrect results under some circumstances. Union removes duplicates, so if a user has the same partial scores, then the two rows becomes one.
EDIT:
It is a little trickier to get the top 5 scores, but possible:
select user_name,
sum(site_point + park_point + division_point) as points
from visits v join
users u
on u.user_id = v.user_id
group by user_name
having points >= (select distinct points
from (select sum(site_point + park_point + division_point)
from visits v join
users u
on u.user_id = v.user_id
group by user_name
) vu
order by points desc
limit 1 offset 4
)
order by points desc
limit 5;
Assuming that a user_name is unique for a given id you can simplify this a wee bit:
having points >= (select distinct points
from (select sum(site_point + park_point + division_point) as points
from visits v
group by user_id
) vu
order by points desc
limit 1 offset 4
)
And, if you want anyone who matches the top 5 users -- but to return more than 5 rows if there are ties -- then change the select distinct to select in the subquery.
Use the LIMIT command. More info here
So to tack it onto your SQL would result in:
select sum(points)as points, user_name
from
(SELECT sum(site_point) as points, user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(park_point) as points , user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(division_point) as points , user_name from visits v join users u
on u.user_id = v.user_id group by user_name
) V group by user_name order by sum(points) DESC LIMIT 5
In my DB, there are two types of images: challenges, and answers. They both have lat/lng location columns. In this query, I wish to select the Challenge or Answer that the user created most recently via an INNER JOIN.
(The business logic behind this: we basically want to obtain a list of users which includes the user's last known location, which is determined by their most recent Challenge or Answer - whichever is most recent. If a user does not have a last known location, they should not be included in this list.)
I am getting [Err] 1054 - Unknown column 'U.Id' in 'where clause':
SELECT
U.Id,
U.TotalPoints,
LastImage.Lat,
LastImage.Lng
FROM User U
INNER JOIN
(
SELECT Lat, Lng FROM
(
(SELECT Lat, Lng, CreatedOn FROM AnswerImage WHERE UserId = U.Id ORDER BY Id DESC LIMIT 1)
UNION ALL
(SELECT Lat, Lng, CreatedOn FROM ChallengeImage WHERE UserId = U.Id ORDER BY Id DESC LIMIT 1)
) LastImages ORDER BY CreatedOn DESC LIMIT 1
) LastImage
WHERE U.Type = 1 AND U.Status = 2
ORDER BY TotalPoints DESC;
I cannot seem to reference the User table (alias U) from within my derived 'LastImages' table (or whatever the proper term for it is).
Can anyone help? I've tried other methods, but none meet all my requirements:
A row is only returned if the user has at least 1 challenge or 1 answer (hence the UNION)
The most recent (as determined by ChallengeImage.CreatedOn and AnswerImage.CreatedOn) image is to be used in the join
Thanks!
SELECT u.id,
u.TotalPoints,
IF(IFNULL(a.CreatedOn, '1900-01-01') > IFNULL(c.CreatedOn, '1900-01-01'), a.Lat, c.Lat) AS Lat,
IF(IFNULL(a.CreatedOn, '1900-01-01') > IFNULL(c.CreatedOn, '1900-01-01'), a.Lng, c.Lng) AS Lng
FROM User AS u
LEFT JOIN
(SELECT UserId, Lat, Lng, CreatedOn
FROM AnswerImage AS a
JOIN (SELECT UserId, MAX(CreatedOn) AS CreatedOn
FROM AnswerImage
GROUP BY UserId) AS amax
USING (UserId, CreatedOn)) AS a
ON u.id = a.UserId
LEFT JOIN
(SELECT UserId, Lat, Lng, CreatedOn
FROM ChallengeImage AS c
JOIN (SELECT UserId, MAX(CreatedOn) AS CreatedOn
FROM ChallengeImage
GROUP BY UserId) AS cmax
USING (UserId, CreatedOn)) AS c
ON u.id = c.UserId
WHERE U.Type = 1 AND U.Status = 2
AND (a.Lat IS NOT NULL OR c.Lat IS NOT NULL)
ORDER BY TotalPoints DESC;
The subqueries are one of the common ways to get the last row per group in a table, see
Retrieving the last record in each group
Then you left join them with the User table so you'll get results even if a user doesn't have matches in both tables.
Your userid = u.id is at the wrong place indeed. Since you are pulling one record, what is reason for the order by in your last line?
Please give this a try. untested :(
select u.id, u.totalpoints, a.lat, a.lng
from user u
join (
select userid, lat, lng, createdon
from AnswerImage
union all
select userid, lat, lng, createdon
from ChallengeImage) a on a.userid = u.id
where u.type = 1 and u.status = 2
order by a.createdon desc
limit 1;
LEFT JOIN
(
SELECT user_id, review, COUNT(user_id) totalCount
FROM reviews
GROUP BY user_id
) b ON b.user_id= b.user_id
I am trying to fit WHERE LENGTH(review) > 100 in this somewhere but every I put it, it gives me problems.
The sub-query above counts all total reviews by user_id. I simply want to add one more qualification. Only count reviews greater than 100 length.
On a side note, I've seen the function CHAR_LENGTH -- not sure if that i what I need either.
EDIT:
Here is complete query working perfectly as expected for my needs:
static public $top_users = "
SELECT u.username, u.score,
(COALESCE(a.totalCount, 0) * 4) +
(COALESCE(b.totalCount, 0) * 5) +
(COALESCE(c.totalCount, 0) * 1) +
(COALESCE(d.totalCount, 0) * 2) +
(COALESCE(u.friend_points, 0)) AS totalScore
FROM users u
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount
FROM items
GROUP BY user_id
) a ON a.user_id= u.user_id
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount
FROM reviews
GROUP BY user_id
) b ON b.user_id= u.user_id
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount
FROM ratings
GROUP BY user_id
) c ON c.user_id = u.user_id
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount
FROM comments
GROUP BY user_id
) d ON d.user_id = u.user_id
ORDER BY totalScore DESC LIMIT 25;";
LENGTH() returns the length of the string measured in bytes. You probably want CHAR_LENGTH() as it will give you the actual characters.
SELECT user_id, review, COUNT(user_id) totalCount
FROM reviews
WHERE CHAR_LENGTH(review) > 100
GROUP BY user_id, review
You're also not using GROUP BY correctly.
See the documentation
The query that you want is:
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount,
sum(case when length(review) > 100 then 1 else 0 end
) as NumLongReviews
FROM reviews
GROUP BY user_id
) b ON b.user_id= b.user_id
This counts both the reviews and the "long" reviews. That count is done using a case statement nested in a sum() function.
SELECT r.game, u.username, r.userid, r.points, u.format
FROM ".TBL_RANKING." r
INNER JOIN ".TBL_USERS." u
ON u.id = r.userid
WHERE r.type = '1' AND r.game =
(SELECT name
FROM ".TBL_GAME."
WHERE active = '1'
ORDER BY rand()
LIMIT 1)
AND u.format =
(SELECT name
FROM ".TBL_FORMAT."
WHERE active = '1'
ORDER BY rand() LIMIT 1)
ORDER BY r.points DESC LIMIT 5
This query isn't working as it's supposed to. It's selecting the odd user and sometimes none at all.
The query should:
-select a random game from the game table
-select a random format from the format table
-select users ranked on that game but only from the format selected
so if the random selection was FIFA 12 Xbox 360, it would find all users that are from format type Xbox 360 and are ranked on FIFA 12.
The table structure is as follows:
*tbl_ranking*
id
userid
game
points
type
*tbl_users*
id
username
format
*tbl_game*
id
name
*tbl_format*
id
name
Can anyone see a problem here?
try to have sub queries using left join
SELECT r.game, u.username, r.userid, r.points, u.format
FROM TBL_RANKING r
INNER JOIN TBL_USERS u
ON u.id = r.userid
LEFT JOIN (SELECT name FROM ".TBL_GAME." WHERE active = '1' ORDER BY rand() LIMIT 1) temp1
ON r.game=temp1.name
LEFT JOIN (SELECT name FROM ".TBL_FORMAT." WHERE active = '1' ORDER BY rand() LIMIT 1) temp2
ON u.format=temp2.name
WHERE r.type = '1'
AND temp1.name != ''
AND temp2.name != ''
ORDER BY r.points DESC LIMIT 5