I have found a problem I can't solve by myself.
I have users in table AE_Users where I store their special Points
Additionaly I have AE_Event table where I store events the users can join to, and for these events I am storing play time in seconds (timestamp) which is taken care of by CRON script checking players on server.
There is another table named AE_EventJoin where user is stored when he joins one of the events (so there can be multiple records of one user joining multiple event IDs)
I am able to calculate one user's point count by using 3 SQLs in PHP script:
SELECT `SteamID` FROM AE_Users WHERE `SteamID` = $SteamID
SELECT SUM(`EventInfo`) FROM AE_Events WHERE ID IN (SELECT `EventID` FROM AE_EventJoin WHERE `JoinType` = 1 AND `SteamID` = '$SteamID')
SELECT `Points` FROM AE_Users WHERE `SteamID` = $SteamID
but now I need to get top 5 users with most points and I really couldn't solve how to query the DB to give them to me.
I don't want to query DB for all users and then sort them in PHP, that would be too ugly.
I believe there is a way on how to calculate the user points from single SQL query. I tried to build it myself, but it is not working as expected. I was able to come up with this:
SELECT T1.`SteamID`, T1.`Points` AS PointCount, SUM(T2.`EventInfo`) AS PointCount FROM AE_Users T1, AE_Events T2 WHERE T2.ID IN (SELECT `EventID` FROM AE_EventJoin WHERE `JoinType` = 1 AND `SteamID` = T1.`SteamID`) ORDER BY PointCount LIMIT 5
which returns this: phpMyAdmin result
and that is wrong. It return only one user and the first PointCount is user's special point count which is correct, but the second PointCount is totally wrong viz. picture
phpMyAdmin Event points
And I would like to merge those two variables into one PointCount but GROUP BY command didn't allow me to do it because of some error. It is not a big issue though, I can merge them in PHP easily.
So is there any way on how to get the information I need from tables with structure like I have?
You are missing a group by clause , so try this:
SELECT T1.`SteamID`, max(T1.`Points`) AS PointCount, SUM(T2.`EventInfo`) AS PointCount
FROM AE_Users T1
INNER JOIN AE_Events T2
ON T2.ID IN (SELECT `EventID` FROM AE_EventJoin
WHERE `JoinType` = 1
AND `SteamID` = T1.`SteamID`)
GROUP BY T1.`SteamID`
ORDER BY PointCount
LIMIT 5
Also, you used implicit(comma separated) join syntax, please try to avoid this and use the correct syntax of explicit joins like in my solution.
You didn't post your table structures so it was hard to guess what exactly do you need, but it is something similar to this.
This query can also be written with a join instead of IN()
SELECT T1.`SteamID`, max(T1.`Points`) AS PointCount, SUM(T2.`EventInfo`) AS PointCount
FROM AE_Users T1
INNER JOIN `EventID` T3
ON(t1.`steamID` = t3.`steamID` and `JoinType` = 1 AND `SteamID` = T1.`SteamID`)
INNER JOIN AE_Events T2
ON (T2.ID = T3.ID)
GROUP BY T1.`SteamID`
ORDER BY PointCount
LIMIT 5
Related
I am using datatables with a modified ssp.class.php to allow for joins and other custom features. In the example below I only want to return results from computers for the first x rows sorted by their id. Because of this, I list computers with my conditions first then LEFT JOIN users followed by logs (the information I am after).
It works great, BUT because of the left joins I have empty results. For instance, while my result set only contains logs from the correct computers... if logs has no rows for a particular user/computer combination I have a row with empty log data, but with user/computer data which serves me no purpose.
Is my only option to include a WHERE condition to prevent null values in the logs join... WHERE logs.user_id != '' or is there some other logic I can do in the select that I am missing?
SELECT (*see note)
FROM
( SELECT account_id, computer_id, computer_name
FROM computers
ORDER BY computer_id ASC LIMIT 0, ".$_SESSION['user']['licenses']."
) as c
LEFT JOIN users
on users.computer_id = c.computer_id
LEFT JOIN logs
on logs.user_id = users.user_id
You can use just JOIN for the table logs.
You put a LIMIT in the Derived Table accessing the computers table returning nrows. When an Inner Join or a final WHERE-condition filters some rows you will get less than nrows.
If this is not ok for you and you always want nrows, the only way is to move the LIMIT after doing Inner Joins:
SELECT (*see note)
FROM computers as c
JOIN users
on users.computer_id = c.computer_id
JOIN logs
on logs.user_id = users.user_id
ORDER BY computer_id ASC LIMIT 0, ".$_SESSION['user']['licenses']."
But this will probably be [much] slower...
I have two tables:
users: user_id, user_zip
settings: user_id, pref_ex_loc
I need to find the single most popular 'pref_ex_loc' from the settings table based on a particular user_zip, which will be specified as the variable $userzip.
Here is the query that I have now and obviously it doesn't work.
$popularexloc = "SELECT pref_ex_loc, user_id COUNT(pref_ex_loc) AS countloc
FROM settings FULL OUTER JOIN users ON settings.user_id = users.user_id
WHERE users.user_zip='$userzip'
GROUP BY settings.pref_ex_loc
ORDER BY countloc LIMIT 1";
$popexloc = mysql_query($popularexloc) or die('SQL Error :: '.mysql_error());
$exlocrow = mysql_fetch_array($popexloc);
$mostpopexloc=$exlocrow[0];
echo '<option value="'.$mostpopexloc.'">'.$mostpopexloc.'</option>';
What am I doing wrong here? I'm not getting any kind of error from this either.
Give this a try:
select s.pref_ex_loc from settings s
join users u on (u.user_id = s.user_id)
where user_zip = $userzip
group by s.pref_ex_loc
order by count(*) desc
limit 1
As you said, this will give you the "single most popular 'pref_ex_loc' from the settings table based on a particular user_zip"
Well, for one thing you are missing a comma before the COUNT():
SELECT pref_ex_loc, user_id COUNT(...
You should have a comma between each field in your select-list:
SELECT pref_ex_loc, user_id, COUNT(...
I would recommend using COUNT(*) instead of COUNT(pref_ex_loc). In this case, either should give the right answer, but in MySQL COUNT(*) usually performs slightly better.
You're using outer join, but then in the WHERE clause you're testing one of the columns of users so it's effectively not an outer join anymore. In this query, I believe you simply need an INNER JOIN, unless you need to handle the possibility that none of the users reference any of your pref_ex_loc values. Read A Visual Explanation of SQL Joins.
Also, MySQL does not support FULL OUTER JOIN.
Your user_id in the select-list, when it is neither in the GROUP BY clause nor in an aggregate function, is an ambiguous field, taking its value from one arbitrary row in the group. You should remove user_id from the select-list.
Sort by the countloc DESC to get the greatest value first.
So here's what I see as a better query:
SELECT pref_ex_loc, COUNT(*) AS countloc
FROM settings INNER JOIN users ON settings.user_id = users.user_id
WHERE users.user_zip='$userzip' GROUP BY settings.pref_ex_loc
ORDER BY countloc DESC LIMIT 1
this will allow values (duplicate most popular) with the highest pref_ex_loc to be shown in the list.
It doesn't use LIMIT, because LIMIT forces the maximum number of rows to be shown. Now, here's the question, What if there are two or more rows that ties up with the most popular pref_ex_loc?
SELECT b.pref_ex_loc
FROM users a
INNER JOIN settings b
ON a.user_ID = b.user_ID
WHERE a.user_zip = 1 -- change the value here
GROUP BY b.pref_ex_loc
HAVING COUNT(*) =
(
SELECT MAX(totalCount)
FROM
(
SELECT b.pref_ex_loc, COUNT(*) totalCount
FROM users a
INNER JOIN settings b
ON a.user_ID = b.user_ID
WHERE a.user_zip = 1 -- change the value here
GROUP BY b.pref_ex_loc
) s
)
SQLFiddle Demo
SQLFiddle Demo (with duplicate most popular)
Try with this query:
SELECT user_id, COUNT(pref_ex_loc) AS countloc
FROM users LEFT JOIN settings ON users.user_id = settings.user_id
WHERE users.user_zip='$userzip' GROUP BY user_id ORDER BY countloc LIMIT 1
I've been racking my brain for hours trying work out how to join these two queries..
My goal is to return multiple venue rows (from venues) based on certain criteria... which is what my current query does....
SELECT venues.id AS ven_id,
venues.venue_name,
venues.sub_category_id,
venues.score,
venues.lat,
venues.lng,
venues.short_description,
sub_categories.id,
sub_categories.sub_cat_name,
sub_categories.category_id,
categories.id,
categories.category_name,
((ACOS( SIN(51.44*PI()/180)*SIN(lat*PI()/180) + COS(51.44*PI()/180)*COS(lat*PI()/180)*COS((-2.60796 - lng)*PI()/180)) * 180/PI())*60 * 1.1515) AS dist
FROM venues,
sub_categories,
categories
WHERE
venues.sub_category_id = sub_categories.id
AND sub_categories.category_id = categories.id
HAVING
dist < 5
ORDER BY score DESC
LIMIT 0, 100
However, I need to include another field in this query (thumbnail), which comes from another table (venue_images). The idea is to extract one image row based on which venue it's related to and it's order. Only one image needs to be extracted however. So LIMIT 1.
I basically need to insert this query:
SELECT
venue_images.thumb_image_filename,
venue_images.image_venue_id,
venue_images.image_order
FROM venue_images
WHERE venue_images.image_venue_id = ven_id //id from above query
ORDER BY venue_images.image_order
LIMIT 1
Into my first query, and label this new field as "thumbnail".
Any help would really be appreciated. Thanks!
First of all, you could write the first query using INNER JOIN:
SELECT
...
FROM
venues INNER JOIN sub_categories ON venues.sub_category_id = sub_categories.id
INNER JOIN categories ON sub_categories.category_id = categories.id
HAVING
...
the result should be identical, but i like this one more.
What I'd like to do next is to JOIN a subquery, something like this:
...
INNER JOIN (SELECT ... FROM venue_images
WHERE venue_images.image_venue_id = ven_id //id from above query
ORDER BY venue_images.image_order
LIMIT 1) first_image
but unfortunately this subquery can't see ven_id because it is evaluated first, before the outer query (I think it's a limitation of MySql), so we can't use that and we have to find another solution. And since you are using LIMIT 1, it's not easy to rewrite the condition you need using just JOINS.
It would be easier if MySql provided a FIRST() aggregate function, but since it doesn't, we have to simulate it, see for example this question: How to fetch the first and last record of a grouped record in a MySQL query with aggregate functions?
So using this trick, you can write a query that extracts first image_id for every image_venue_id:
SELECT
image_venue_id,
SUBSTRING_INDEX(
GROUP_CONCAT(image_id order by venue_images.image_order),',',1) as first_image_id
FROM venue_images
GROUP BY image_venue_id
and this query could be integrated in your query above:
SELECT
...
FROM
venues INNER JOIN sub_categories ON venues.sub_category_id = sub_categories.id
INNER JOIN categories ON sub_categories.category_id = categories.id
INNER JOIN (the query above) first_image on first_image.image_venue_id = venues.id
INNER JOIN venue_images on first_image.first_image_id = venue_images.image_id
HAVING
...
I also added one more JOIN, to join the first image id with the actual image. I couldn't check your query but the idea is to procede like this.
Since the query is now becoming more complicated and difficult to mantain, i think it would be better to create a view that extracts the first image for every venue, and then join just the view in your query. This is just an idea. Let me know if it works or if you need any help!
I'm not too sure about your data but a JOIN with the thumbnails table and a group by on your large query would probably work.
GROUP BY venues.id
I'm fairly new to MYSQL!
I need to make a SQL query where i check how many likes a row has (between two tables)
I found another question that looked like mine, but i can't get it to return anything (even though it doesn't create an error.
query:
SELECT *
FROM likes
INNER JOIN (SELECT likes.like_id,
COUNT(*) AS likes
FROM likes
INNER JOIN uploads ON likes.upload_id=uploads.upload_id
WHERE uploads.upload_date >= DATE_SUB(CURDATE(), INTERVAL 8 DAY)
GROUP BY uploads.upload_id) x ON x.like_id = likes.like_id
ORDER BY x.likes DESC
Link to the original question:
MySQL, Need to select rows that has the most frequent values in another table
Help is much appreciated
Kind regards,
Mathias
Since you didn't post your table structure I'll have to guess..
select someid, count(*) cnt from
(
select * from table1 t1 join table2 t2 on t1.someid = t2.someid
) as q0 group by someid order by cnt desc;
It will need tweaking to fit your schema.
I have the following database structure:
Sites table
id | name | other_fields
Backups table
id | site_id | initiated_on(unix timestamp) | size(float) | status
So Backups table have a Many to One relationship with Sites table connected via site_id
And I would like to output the data in the following format
name | Latest initiated_on | status of the latest initiated_on row
And I have the following SQL query
SELECT *, `sites`.`id` as sid, SUM(`backups`.`size`) AS size
FROM (`sites`)
LEFT JOIN `backups` ON `sites`.`id` = `backups`.`site_id`
WHERE `sites`.`id` = '1'
GROUP BY `sites`.`id`
ORDER BY `backups`.`initiated_on` desc
The thing is, with the above query I can achieve what I am looking for, but the only problem is I don't get the latest initiated_on values.
So if I had 3 rows in backups with site_id=1, the query does not pick out the row with the highest value in initiated_on. It just picks out any row.
Please help, and
thanks in advance.
You should try:
SELECT sites.name, FROM_UNIXTIME(b.latest) as latest, b.size, b.status
FROM sites
LEFT JOIN
( SELECT bg.site_id, bg.latest, bg.sizesum AS size, bu.status
FROM
( SELECT site_id, MAX(initiated_on) as latest, SUM(size) as sizesum
FROM backups
GROUP BY site_id ) bg
JOIN backups bu
ON bu.initiated_on = bg.latest AND bu.site_id = bg.site_id
) b
ON sites.id = b.site_id
In the GROUP BY subquery - bg here, the only columns you can use for SELECT are columns that are either aggregated by a function or listed in the GROUP BY part.
http://dev.mysql.com/doc/refman/5.5/en/group-by-hidden-columns.html
Once you have all the aggregate values you need to join the result again to backups to find other values for the row with latest timestamp - b.
Finally join the result to the sites table to get names - or left join if you want to list all sites, even without a backup.
Try with this:
select S.name, B.initiated_on, B.status
from sites as S left join backups as B on S.id = B.site_id
where B.initiated_on =
(select max(initiated_on)
from backups
where site_id = S.id)
To get the latest time, you need to make a subquery like this:
SELECT sites.id as sid,
SUM(backups.size) AS size
latest.time AS latesttime
FROM sites AS sites
LEFT JOIN (SELECT site_id,
MAX(initiated_on) AS time
FROM backups
GROUP BY site_id) AS latest
ON latest.site_id = sites.id
LEFT JOIN backups
ON sites.id = backups.site_id
WHERE sites.id = 1
GROUP BY sites.id
ORDER BY backups.initiated_on desc
I have removed the SELECT * as this will only work using MySQL and is generally bad practice anyway. Non-MySQL RDBSs will throw an error if you include the other fields, even individually and you will need to make this query itself into a subquery and then do an INNER JOIN to the sites table to get the rest of the fields. This is because they will be trying to add all of them into the GROUP BY statement and this fails (or is at least very slow) if you have long text fields.