Adam K
Adam K

Reputation: 67

Combine Two Complex SQL Queries Into One

My mySQL database has a schools and a users table. Each school has an id that identifies it. When a user joins a school, the user's school column is set to the id of that school. I need to execute a request which does the following:

  1. Gets a random school from the database that has users joined to it. (Must have at least one student in the database whose school column is set to that school's id.)
  2. Gets the user at that school with the highest (successes / attempts) value.
  3. If multiple users at the school are tied for the highest value, the query will select a random user and return it.

All I need in return is the user object. I know I could use PHP for some of the work, but I would like to do the majority in the SQL query. If possible, I would like to do this all in one request. Is this possible? So far I've come up with:

SELECT  *
FROM  `schools` 
INNER JOIN  `users` ON schools.id = users.school
ORDER BY RAND( ) 
LIMIT 1

at which point I could save the selected school's id in PHP as $schoolID and execute another request:

SELECT * 
FROM  `users` 
WHERE school =2
AND attempts !=0
ORDER BY (
successes / attempts
) DESC , RAND( ) 
LIMIT 1

Is there a way to combine them into one SQL query to save server load?

Structure:

Users Table:

+------------+----------+------+-----+---------+----------------+
| Field      | Type     | Null | Key | Default | Extra          |
+------------+----------+------+-----+---------+----------------+
| id         | int(11)  | NO   | PRI |         | auto_increment |
| username   | char(35) | NO   |     |         |                |
| school     | int(11)  | YES  |     |         |                |
| successes  | int(11)  | NO   |     | 0       |                |
| attempts   | int(11)  | NO   |     | 0       |                |
+------------+----------+------+-----+---------+----------------+

Schools Table:

+------------+----------+------+-----+---------+----------------+
| Field      | Type     | Null | Key | Default | Extra          |
+------------+----------+------+-----+---------+----------------+
| id         | int(11)  | NO   | PRI |         | auto_increment |
| name       | char(35) | NO   |     |         |                |
+------------+----------+------+-----+---------+----------------+

Sample Data: Users table:

id  username    school  successes   attempts
1   josh        1       0           0
2   james       2       0           1
3   ashley      2       2           2
4   john        1       1           1
5   will        3       4           8
6   jack        3       1           2

Schools table:

id  name
1   school1
2   school2
3   school3 
4   school4

Only schools 1-3 should be able to be chosen from schools, and only user 4 should show if school 1 was chosen, user 3 should show if school 2 was chosen, and either user 5 or 6 should show if school 3 was chosen.

Upvotes: 0

Views: 1702

Answers (1)

mseifert
mseifert

Reputation: 5670

Revised again so that it will NOT retrieve all records in the inner query. Safe to use with larger data sets. The inner select gets a random school which has a valid user record. The outer select selects on that school record and sorts by success / attempts and limits to one. Tested with your data.

SELECT s.id, s.name, users.username, users.successes, users.attempts 
FROM ((SELECT schools.id, schools.name from schools INNER JOIN users on schools.id = users.school
    WHERE users.attempts !=0
    ORDER BY RAND()
    LIMIT 1) s)
INNER JOIN users on s.id = users.school
ORDER BY users.successes / users.attempts DESC
LIMIT 1

Upvotes: 3

Related Questions