Error with a join query - mysql

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

Related

How to write a query that retrieves 3 replies for each user in my users table?

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

Display only top sum-up results using group by

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

Retrieve last 10 results ASC Left Join MySql

I'm trying to retrieve the last 10 posts from a posts table ordered ASC, but the last left joined query doesn't retrieve anything.
Basic: it retrieves results ordered DESC
SELECT
p.post, p.id_post, u.name
FROM
posts p
LEFT JOIN
users u ON u.id_user = p.id_user
WHERE
p.id_user = 4
ORDER BY
p.date DESC
LIMIT 10
Ordered ASC: it doesn't work at all:
SELECT
num.*
FROM
(SELECT
p.post, p.id_post, u.name
FROM
posts p
LEFT JOIN
users u ON u.id_user = p.id_user
WHERE
p.id_user = 4
ORDER BY
p.date DESC
LIMIT 10) num
ORDER BY
p.date ASC
What am I doing wrong?
In my php variables I use $row['id_post'] $row['post'] $row['name']. I don't want to use array_reverse() just plain sql
You need to return the date in the subquery:
SELECT pu.post, pu.id_post, pu.name
FROM (SELECT p.*, u.name
FROM posts p LEFT JOIN
users u
ON u.id_user = p.id_user
WHERE p.id_user = 4
ORDER BY p.date DESC
LIMIT 10
) pu
ORDER BY pu.date ASC;
Your second query should have returned an error message to the effect that date is not recognized as a column. You should be capturing and reading error messages, if you want to write an effective application.

mysql: group functions on subqueries with limits

I have a group of users who perform tasks on which they are scored. I'm trying to create a report showing the average of each user's last 50 tasks.
user table: userid, username, usertype
task table: taskid, score, tasktype, userid
If I do:
SELECT u.userid, u.username, (SELECT AVG(score)
FROM task t
WHERE t.userid = u.userid AND t.tasktype = 'task1'
ORDER BY t.taskid DESC LIMIT 50) AS avgscore
FROM user u
WHERE u.usertype = 'utype';
that doesn't work because it does the limit 50 after it does the average of everything.
What I need is this:
SELECT u.userid, u.username, AVG(SELECT t.score
FROM task t
WHERE t.userid = u.userid AND t.tasktype = 'task1'
ORDER BY t.taskid DESC LIMIT 50) AS avgscore
FROM user u
WHERE u.usertype = 'utype';
but that is not valid syntax
I've tried sub-sub queries, but can't get it that way either, as I always get a problem with the limit, or a join, or unknown fields when I reference u.userid in the sub-subquery.
Is there a way to do this?
Use a subquery within the subquery:
SELECT u.userid, u.username,
(SELECT AVG(score)
FROM (select t.*
from task t
WHERE t.userid = u.userid AND t.tasktype = 'task1'
ORDER BY t.taskid DESC
LIMIT 50
) t
) AS avgscore
FROM user u
WHERE u.usertype = 'utype';
EDIT:
I didn't realize that MySQL would not recognize the u.userid. It should according to ANSI rules for the scoping of table aliases.
You can take a different approach which is find the 50th taskid value, and then take everything above that:
select ut.userid, ut.username, avg(t.score)
from (SELECT u.userid, u.username,
(SELECT substring_index(substring_index(group_concat(taskid order by taskid desc
), ',', 50), ',', -1)
from task t
WHERE t.userid = u.userid AND t.tasktype = 'task1'
) + 0 as taskid50
FROM user u
WHERE u.usertype = 'utype'
) ut join
task t
on ut.userid = t.userid and
ut.taskid50 >= t.taskid and t.tasktype = 'task1'
group by ut.userid, ut.username;
try this
SELECT u.userid, u.username, AVG(t.score ) AS avgscore
FROM user u
INNER JOIN task t
ON t.userid = u.userid
WHERE u.usertype = 'utype' AND t.tasktype = 'task1'
GROUP BY u.userid
ORDER BY t.taskid DESC LIMIT 50;

Optimize sub-query selecting last record of each group

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