I'm trying, in a single query, to get an entire set of rows when only one of those rows meets certain criteria. In the case below, I want to do a query for Mike Smith. If I find Mike Smith has taken a test (by test_id) then I want to include all the results for that test_id. So a successful query would return the first 7 rows. Is this possible without running multiple queries? Below is the example entire contents of my table.
I can't use
Select * where first_name = 'Mike';
as this will only return Mike's test scores;
I don't know how to select all test scores (for multiple tests) when I have a result for Mike.
+------------+------------+-----------+-------+------+
| test_id | first_name | last_name | class | rank |
+------------+------------+-----------+-------+------+
| 1 | John | Doe | 2012 | 1 |
+------------+------------+-----------+-------+------+
| 1 | Jack | Smith | 2014 | 50 |
+------------+------------+-----------+-------+------+
| 1 | Mike | Smith | 2014 | 60 |
+------------+------------+-----------+-------+------+
| 2 | Mike | Smith | 2014 | 70 |
+------------+------------+-----------+-------+------+
| 2 | John | Smith | 2014 | 80 |
+------------+------------+-----------+-------+------+
| 3 | Jake | Smith | 2014 | 80 |
+------------+------------+-----------+-------+------+
| 3 | Mike | Smith | 2014 | 90 |
+------------+------------+-----------+-------+------+
| 4 | Jake | Smith | 2014 | 78 |
+------------+------------+-----------+-------+------+
Use the EXISTS clause, eg
SELECT * FROM `test_table` a WHERE EXISTS (
SELECT 1 FROM `test_table` b
WHERE first_name = 'Mike'
AND last_name = 'Smith'
AND b.test_id = a.test_id
)
Alternatively, you can INNER JOIN the table to itself, eg
SELECT a.* FROM `test_table` a
INNER JOIN `test_table` b
ON a.test_id = b.test_id
WHERE b.first_name = 'Mike' AND b.last_name = 'Smith'
Demo here - http://sqlfiddle.com/#!2/c8646/1
I think this might be what you're after:
SELECT *
FROM test_scores
WHERE test_id IN
(
SELECT test_id
FROM test_scores
WHERE first_name = 'Mike' AND last_name = 'Smith'
GROUP BY test_id
)
Note: I just assumed the table name was 'test_scores'
Related
I have a ( Joomla) database table called field_values, the contents are below;
+----+----------+---------+---------+
| id | field_id | item_id | value |
+----+----------+---------+---------+
| 1 | 2 | 446 | Jones |
| 2 | 2 | 447 | Smith |
| 3 | 2 | 448 | Jenkins |
| 4 | 3 | 446 | Paul |
| 5 | 3 | 447 | Peter |
| 6 | 3 | 448 | Sally |
| 7 | 4 | 446 | London |
| 8 | 4 | 447 | Dublin |
| 9 | 4 | 448 | Paris |
+----+----------+---------+---------+
I'm only displaying 9 rows from the table, but I actually have thousands, so the successful query would need to take this into account.
Columns explained;
id (primary / auto-increment)
field_id (FK to another fields table, 2 = surname, 3 = first name, 4 = location)
item_id (FK to another users table)
value (contents of field)
How can I select all the values from the above table but display them as follows;
+------------+-----------+----------+
| first_name | last_name | location |
+------------+-----------+----------+
| Paul | Jones | London |
| Peter | Smith | Dublin |
| Sally | Jenkins | Paris |
+------------+-----------+----------+
The id field isn't really necessary in the desired results above, I just added it to emphasise that each row is unique.
I'm not sure if I need to use a subquery or group by, maybe neither?
Thanks in advance.
A pivot query should work here:
SELECT
MAX(CASE WHEN field_id = 3 THEN value END) AS first_name,
MAX(CASE WHEN field_id = 2 THEN value END) AS last_name,
MAX(CASE WHEN field_id = 4 THEN value END) AS location
FROM yourTable
GROUP BY
item_id
ORDER BY
item_id;
Your current table structure is a denormalized key value store, a style which WordPress uses in some of its tables.
you could avoid subquery and grou by.
You could use the same table 3 times
select a.id, b.value firts_name, a.value last_name , c.value location
from field_values a
inner join field_values b on a.item_id = b.item_id and b.field_id = 3
inner join field_values bc on a.item_id = c.item_id and b.field_id = 4
where a.item_id = 2
I Have a table called EMPLOYEE
| employee_id | name | supervisor_id |
| 123 | Ace Ven | NULL |
| 124 | Ben Agent | 123 |
| 125 | Sam Marks | 123 |
| 126 | Bob Teabag | 125 |
| 127 | Matthew Smith | 125 |
| 128 | Toby McQuire | 123 |
I am trying to find the supervisors and list the amount of employees they have. as you can see the supervisor_id is the same as employee_id. Should come out like this
name | total_employees |
Ace Ven | 3 |
Sam Marks | 2 |
I tried
SELECT supervisor_id, name, count(supervisor_id) AS total_employees FROM EMPLOYEE GROUP BY name HAVING (total_employees > 0);
which didn't work at all (sorry about the format, can't seem to get it to work).
Subquery counts how many records/employees are for each supervisor_id, then this result simply joined table again for retrieving their names:
select your_table.name, t.total_employees from your_table
inner join
(select supervisor_id, count(*) as total_employees from your_table where supervisor_id is not null group by supervisor_id) t
on your_table.employee_id = t.supervisor_id
A simple join will suffice...
Select x.name
, count(*) total
from employee x
join employee y
on y.supervisor_id = x.employee_id
group
by x.employee_id;
Semesters Table
+----+------+
| ID | Name |
+----+------+
| 1 | 1st |
| 2 | 2nd |
+----+------+
Subjects Table
+----+-------------+-------------+
| ID | Semester Id | Name |
+----+-------------+-------------+
| 1 | 1 | Mathematics |
| 2 | 1 | English |
| 3 | 2 | Mathematics |
| 4 | 2 | English |
+----+-------------+-------------+
Tests Table
+----+------------+-------+
| ID | Subject ID | Score |
+----+------------+-------+
| 1 | 1 | 70 |
| 2 | 1 | 75 |
| 3 | 2 | 75 |
| 4 | 2 | 70 |
| 5 | 3 | 75 |
| 6 | 3 | 70 |
| 7 | 4 | 70 |
| 8 | 4 | 75 |
+----+------------+-------+
I can get the scores of the 2nd test by using MAX on the ID of the tests, and then grouping them by the subject id. However, then I have to get the minimums of the scores grouped by the semester.
Is it possible to get the lowest scoring 2ND test of each semester in a single SQL statement?
The result set would look like this.
+----------+-------------+-------+
| Semester | Subject | Score |
+----------+-------------+-------+
| 1st | English | 70 |
| 2nd | Mathematics | 70 |
+----------+-------------+-------+
This is in MySQL.
You are looking for the second lowest tests per semester.
Build row numbers for the ordered tests per semester and stay with those numbered #2. One way to do this is a correlated subquery. Another would be variables.
select
sem.name as semester,
sub.name as subject,
tst.score
from semesters sem
join subjects sub on sub.semester_id = sem.id
join tests tst on tst.subject_id = sub.id
where
(
select count(*)
from subjects sub2
join tests tst2 on tst2.subject_id = sub2.id
where sub2.semester_id = sub.semester_id
and sub2.id <= sub.id
and tst2.score <= tst.score
) = 2
order by sub.semester_id;
In case of ties one of the rows is picked, just as shown in your example.
Working with variables is probably faster than above query. You will easily find the method by looking for how to emulate ROW_NUMBER in MySQL. (Other DBMS use ROW_NUMBER which is much simpler, but MySQL doesn't feature this function.)
select a.name as semester
, b.name as subject
, min(c.score) as lowestscore
from subjects as b
join semesters as a on a.id = b.semester_id
join tests as c on c.subject_id = b.id
group by a.name, b.name
You will probably want to order by or something but this should give you what you are looking for. You could add more discriminators for range of semesters or subjects, but this will yield the name of the semester, the name of the subject and the minimum score for that semester/subject.
here is my data set and current code: http://sqlfiddle.com/#!2/f9a605/3
| id | courseid | username | day | score |
--------------------------------------------
| 1 | 2 | tim | may 5 | 85 |
| 2 | 2 | mike | may 6 | 86 |
| 3 | 2 | tim | may 7 | 82 |
| 4 | 3 | tim | may 8 | 80 |
| 5 | 2 | mike | may 9 | 79 |
| 6 | 2 | joe | may 10| 81 |
I want to select the values for each user's lowest score where courseid=2 ordered by lowest score so result should look like:
| mike | may 9 | 79 |
| joe | may 10 | 81 |
| tim | may 8 | 82 |
my current code looks like this:
SELECT courseid, username, day, MIN(score) FROM result where courseid = '2' GROUP BY username order by MIN(score) limit 10
my current result looks like this:
| mike | may 6 | 79 |
| joe | may 10 | 81 |
| tim | may 5 | 82 |
The days are wrong.
How do I get the correct day corresponding to the minimum score for that person when courseid =2?
This can be done using a self join on 2 conditions one with username and second with the minimum score
SELECT r.courseid, r.username, r.`day`,r.score
FROM result r
JOIN (SELECT username, MIN(score) score
FROM result
where courseid = '2'
GROUP BY username) r1
ON(r.username = r1.username and r.score = r1.score)
order by r.score
limit 10
Also in your expected result tim should have date of may7
Fiddle Demo
You can use a subquery:
select courseid, username, day, score
from result x
where score = (select min(y.score)
from result y
where y.username = x.username
and x.courseid = y.courseid)
and courseid = 2
order by score
Fiddle:
http://sqlfiddle.com/#!2/f9a605/36/0
I have a table (call_history) that has multiple records against a single ID in another table (call_detail). I am trying to return a result that will give me a single row for each ID but the most recent entry.
So for example, group all rows against a single ID, but return ONLY the most recent row for the updated_at field (which is a date field).
So far my query...
SELECT MAX(cd.id) as id, cd.first_name, cd.summary, cd.due_at, ch.body, ch.updated_at
FROM call_detail as cd
LEFT JOIN call_history as ch on cd.id = ch.ticket_id
WHERE cd.status = 'open' AND (NOW() > due_at)
GROUP BY cd.id HAVING COUNT(*) > 1
ORDER BY cd.due_at DESC
...returns 'kind' of what I want, but it gives me the oldest entry against the updated_at field. I need it the other way around.
Update
My table structure is as follows:
Call_Detail
id | summary | description | due_at | first_name | last_name
1 | Call 1 | some text | 20/02/2014 17:00:00 | Joe | Bloggs
2 | Call 2 | some text | 18/02/2014 15:00:00 | Fred | Durst
3 | Call 3 | some text | 02/03/2014 01:00:00 | Joe | Bloggs
Call_History
id | ticket_id | body | updated_at | first_name | last_name
1 | 1 | update 1 | 17/02/2014 16:00:00 | Joe | Bloggs
2 | 1 | update 2 | 17/02/2014 16:02:00 | Fred | Durst
3 | 2 | update 1 | 16/02/2014 12:02:00 | Tom | Thumb
4 | 1 | update 3 | 17/02/2014 16:10:00 | Joe | Bloggs
5 | 2 | update 2 | 17/02/2014 01:02:00 | Jack | Reacher
etc...
I need to retrieve the following output:
ticket_id | summary | due_at | first_name | body | updated_at
1 | Call 1 | 20/02/2014 17:00:00 | Joe | Update 3 | 17/02/2014 16:10:00
2 | Call 2 | 18/02/2014 15:00:00 | Fred | Update 2 | 17/02/2014 01:02:00
Try this query. You need the most recent record from call_history so first you should make a subquery with these dates (see CH_MAX subquery) and then JOIN with ticket_id and updated_at:
SELECT cd.id as id,
cd.first_name,
cd.summary,
cd.due_at,
ch.body,
ch.updated_at
FROM call_detail as cd
LEFT JOIN
( SELECT ticket_id, MAX(updated_at) as max_updated_at
FROM call_history
GROUP BY ticket_id
) as CH_MAX ON cd.id = CH_MAX.ticket_id
LEFT JOIN call_history as ch ON cd.id = ch.ticket_id
AND CH_MAX.max_updated_at = ch.updated_at
WHERE cd.status = 'open' AND (due_at<NOW())
ORDER BY cd.due_at DESC
SQLFiddle demo