only students who have the best mark - mysql

I have a simple relation between student and marks :
Student :
+------------+-------------+
| idStudent | NameStudent |
+------------+-------------+
| 1 | Student A |
| 2 | Student B |
| 3 | Student C |
+------------+-------------+
Marks :
+----------+------+-----------+
| idMarks | mark | idStudent |
+----------+------+-----------+
| 1 | A | 1 |
| 2 | A | 1 |
| 3 | A | 1 |
| 4 | A | 2 |
| 5 | A | 2 |
| 6 | C | 3 |
| 7 | A | 3 |
+----------+------+-----------+
I only want to have students who have a "A" in every exam they passed
SELECT *, COUNT(mark)
FROM student S
INNER JOIN marks M ON S.idStudent = M.idStudent
WHERE M.mark = "A"
GROUP BY S.idStudent
I tried this but I reach deadlock when I try to compare the number of exam they passed and the number of A they have...
I also tried with subqueries but it didn't work

You could use HAVING:
SELECT S.idStudent
FROM student S
INNER JOIN marks M ON S.idStudent = M.idStudent
GROUP BY S.idStudent
HAVING COUNT(mark)=SUM(mark='A');-- compare all marks with marks that are only 'A'
-- if equal then every mark is 'A'

Group by on the student
Find all distinct marks obtained by the student and put them in a comma separated string using Group_concat function
Just filter out (using Having clause) the ones having only one overall distinct mark, that is A
Use the following query:
SELECT S.idStudent,
S.NameStudent,
GROUP_CONCAT(DISTINCT M.mark) AS unique_marks
FROM student S
INNER JOIN marks M ON S.idStudent = M.idStudent
GROUP BY S.idStudent, S.NameStudent
HAVING unique_marks = 'A'

If you just need the student IDs, you can LEFT JOIN students to marks, including a condition in the join clause for the mark not being A. Then, the WHERE clause would only include students with non-matching rows:
SELECT Student.idStudent
FROM Student
LEFT JOIN Marks BelowA ON
BelowA.idStudent = Student.idStudent
AND BelowA.mark <> 'A'
WHERE BelowA.idStudent IS NULL

Related

getting related subjects for student in mysql

I am trying to get subjects, which student has been enrolled using MySQL.
Student Table :
subjects table :
student enrolled subjects :
My query :
select *,(select subject_id from stu_subjects ss where ss.student_id =
s.student_id ) as subject_id
from student s
when executing above query I'm getting below error :
So i modified my query as below (added limit 1) :
select *,(select subject_id from stu_subjects ss where ss.student_id =
s.student_id limit 1) as subject_id
from student s
Final Output :
If you see the output I'm getting only one subject mapped to a student. But I need every subject which student has enrolled. So how can I achieve it?
What I'm trying to achieve is :
Althought Jagrut Sharma's answer is correct, this version specifies the separator of the subjects' ids, and uses an explicit JOIN rather than the implicit one.
Edit: It appears that , is the default separator, so you can omit it from GROUP_CONCAT. I'll leave it there in my query just so you know how to change it, if needed.
SELECT st.student_id, st.student_name
GROUP_CONCAT(sb.subject_id SEPARATOR ',')
FROM stu_subjects sb JOIN student st ON sb.student_id = st.student_id
GROUP BY st.student_id;
Use this query:
select a.student_id, b.student_name, group_concat(a.subject_id) as student_enrollment
from stu_subjects a, student b
where a.student_id = b.student_id
group by a.student_id, b.student_name;
Illustration:
-- table: student
> select * from student;
+------------+--------------+
| student_id | student_name |
+------------+--------------+
| 1 | Student-1 |
| 2 | Student-2 |
+------------+--------------+
-- table: subject
> select * from subject;
+------------+--------------+
| subject_id | subject_name |
+------------+--------------+
| 1 | Maths |
| 2 | Science |
| 3 | English |
| 4 | Telugu |
| 5 | Social |
+------------+--------------+
-- table: stu_subjects
> select * from stu_subjects;
+------------+------------+------------+
| stu_sub_id | student_id | subject_id |
+------------+------------+------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 4 |
| 5 | 2 | 1 |
+------------+------------+------------+
-- query
> select a.student_id, b.student_name, group_concat(a.subject_id) as student_enrollment
from stu_subjects a, student b
where a.student_id = b.student_id
group by a.student_id, b.student_name;
+------------+--------------+--------------------+
| student_id | student_name | student_enrollment |
+------------+--------------+--------------------+
| 1 | Student-1 | 3,1,2 |
| 2 | Student-2 | 4,1 |
+------------+--------------+--------------------+
You can use simple join query to achieve this.
SELECT st.student_id,st.student_name,sb.subject_name
FROM student st
JOIN stu_subjects ss ON (ss.student_id = st.student_id)
JOIN Subject sb ON (ss.subject_id = sb.subject_id )
Also as you mentioned,if you want only for subject MySQL, then add below where condition
WHERE subject_name ='MySQL'

how to perform an outer join in mysql

I have a table A that contains tree columns, id, users ids and vehicle id. And a table B that contains vehicleid, and vehicle name.
Table A
---------------------------
| Id | User_id |Vehicle_id|
---------------------------
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 4 |
| 4 | 2 | 2 |
| 5 | 2 | 3 |
| 6 | 4 | 5 |
---------------------------
Table B
-------------------
| Id |Vehicle_name|
-------------------
| 1 | Car |
| 2 | Bike |
| 3 | Plane |
| 4 | Boat |
| 5 | Rocket |
-------------------
Given a user id, I need to get all vehicle names, that doesn't match with table A. I've tried Outer joins, but I can't manage to do get the info that i need.
For example: Given user id 1, the query should return Car and Rocket.
thanks in advance
This is simple enough using not in or not exists:
select b.*
from b
where not exists (select 1
from a
where a.vehicle_id = b.id and a.user_id = #a_user_id
);
I also thought of using a cross join and was able to get the output in case you are more comfortable with join logic.
SELECT CJOIN.USER_ID, CJOIN.VEHICLE_ID, CJOIN.VEHICLE_NAME
FROM
(SELECT DISTINCT A.USER_ID, B.ID AS VEHICLE_ID, B.VEHICLE_NAME FROM TABLE_A A CROSS JOIN TABLE_B B) CJOIN
LEFT JOIN
TABLE_A D
ON CJOIN.USER_ID = D.USER_ID AND CJOIN.VEHICLE_ID = D.VEHICLE_ID
WHERE D.USER_ID IS NULL AND D.VEHICLE_ID IS NULL;
First, I got all possible combinations of USER_ID x VEHICLE_ID by a cross join and used this table in a left join to pull records for which there is no match.

Getting multiple rows as a single row in MySQL

I have a basic multi-school management system that I am working on for fun in order to learn querying from a MySQL database.
I am doing a number of joins from tables such as students, schools,student_grade, grade_school etc. I now want to get a list of all subjects the student has been assigned. Since there are multiple possible rows, I have a student_subject table to hold the student_id and then the subject_id values.
The following query gets me the student's name, and their grade:
SELECT a.id,a.firstnames,a.surname,d.gradename
FROM students a
LEFT JOIN student_grade b ON a.id=b.studentid
LEFT JOIN grade_school c ON c.id=b.gradeid
LEFT JOIN grades d ON c.gradeid=d.id
WHERE a.schoolid=? AND b.gradeid=? ORDER by a.surname ASC
This results in each row returned to be the student's name and their grade, like this:
ID | Firstname | Surname | Grade
1 | John | Doe | 4
5 | Sarah | Marshall | 7
How can I get all the subjects the student is assigned and put them all in a column "subject_ids" to get a result as follows:
ID | Firstname | Surname | Grade | Subjects
1 | John | Doe | 4 | 5,54,2,56
5 | Sarah | Marshall | 7 | 2,4,12,17
My table structures are as follows:
Table `students`
id | firstnames | surname | schoolid
Table `student_grade`
id | studentid | gradeid
Table `grade_school`
id | gradeid| schoolid
Table `grades`
id | gradename|
Table `student_subject`
id | studentid| subjectid
4 | 1 | 5
5 | 1 | 54
6 | 1 | 2
7 | 1 | 56
8 | 5 | 2
9 | 5 | 4
10 | 5 | 12
11 | 5 | 17
Try this:
SELECT a.id,a.firstnames,a.surname,d.gradename, GROUP_CONCAT(s.subjectid)
FROM students a
LEFT JOIN student_grade b ON a.id=b.studentid
LEFT JOIN grade_school c ON c.id=b.gradeid
LEFT JOIN grades d ON c.gradeid=d.id
LEFT JOIN student_subject s ON s.studentid=a.id
WHERE a.schoolid=? AND b.gradeid=? ORDER by a.surname ASC
also check this or this
The GROUP_CONCAT() function will do what you want here.
This question is a dup of Can I concatenate multiple MySQL rows into one field?

Left Join a Single Random Record MySQL

I am trying to get a single row at random from the Sponsor table joined to the Company table. The follow query almost works but will sometimes return a NULL Sponsor.
Can anyone see what I am doing wrong here?
SELECT C.ID AS CompID, C.Name AS CompName, S.ID AS SponID, S.Name AS SponName
FROM Company C
LEFT JOIN Sponsor S ON S.ID = (SELECT ID FROM Sponsor WHERE Company = C.ID ORDER BY RAND() LIMIT 1)
Data Sample:
Company Table
| ID | Name |
| 1 | MyCompany |
Sponsor Table
| ID | Company | Name |
| 1 | 1 | Bruce |
| 2 | 1 | John |
Query Results in one of the following:
| CompID | CompName | SponsID | SponName |
| 1 | MyCompany | 1 | Bruce |
| CompID | CompName | SponsID | SponName |
| 1 | MyCompany | 2 | John |
| CompID | CompName | SponsID | SponName |
| 1 | MyCompany | NULL | NULL |
Because of RAND() your subquery is not deterministic and thus is executed for every row in Sponsor table and every time retuns a random ID which might match or not the ID of the current row. So it's not only possible, that no row will match the random ID. It's also possible that multiple rows will.
For the sample data with two sponsors the subquery may return folowing values:
(1, 1) will match the first row (1=1, 2=1)
(1, 2) will match both rows (1=1, 2=2)
(2, 1) will match no row (1=2, 2=1)
(2, 2) will match the second row (1=2, 2=2)
To guarantee that the subquery is executed only once, you can use it the SELECT clause. Then join the result as derived table with the Sponsor table:
SELECT C.*, S.Name AS SponName
FROM (
SELECT C.ID AS CompID, C.Name AS CompName, (
SELECT ID FROM Sponsor WHERE Company = C.ID ORDER BY RAND() LIMIT 1
) as SponID
FROM Company C
) C
LEFT JOIN Sponsor S ON S.ID = C.SponID
Demo: http://rextester.com/LSSJT25902

Mysql join on 3 tables output

I am learning joins and have the following tables.
Student
| ID | NAME |
-------------
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | D |
Pass
| ID | MARKS |
--------------
| 2 | 80 |
| 3 | 75 |
Fail
| ID | MARKS |
--------------
| 1 | 25 |
| 4 | 20 |
The output I want is this:
| NAME | MARKS |
----------------
| B | 80 |
| C | 75 |
| A | 25 |
| D | 20 |
I wrote a query like this:
select s.id,s.name,p.marks from student s
left join pass p on s.id=p.id
left join (select f.marks,f.id from fail f ) as nn on s.id=nn.id
order by marks desc;
The output I got is this:
| id | name | Marks|
--------------------
| 1 | B | 80 |
| 2 | C | 75 |
| 3 | A | Null |
| 4 | D | NUll |
Cant figure out why Null is coming. Any pointers?
You can use CASE statement for that:
SELECT Name,
CASE WHEN P.Marks IS NULL THEN f.Marks ELSE P.Marks END AS Marks
FROM Student s
LEFT JOIN Pass p ON s.ID = p.ID
LEFT JOIN Fail f ON s.ID = f.ID
ORDER BY Marks DESC;
Or you can also use IF statement:
SELECT Name,
IF(P.Marks IS NULL, F.Marks, P.Marks) AS Marks
FROM Student s
LEFT JOIN Pass p ON s.ID = p.ID
LEFT JOIN Fail f ON s.ID = f.ID
ORDER BY Marks DESC;
Output
| NAME | MARKS |
----------------
| B | 80 |
| C | 75 |
| A | 25 |
| D | 20 |
See this SQLFiddle
To learn more about JOINs see: A Visual Explanation of SQL Joins
You select only the passed marks, this is the reason of null-s appears near falied results.
If you want to select the failed marks you can use IF condition
select s.id,s.name,IF(p.marks = null, nn.marks, p.marks) as markss
from student s
left join pass p on s.id=p.id
left join fail nn on s.id=nn.id
order by markss desc;
Or you can use union of the passed and failed results.
select s.id,s.name, u.marks
from student s
left join ( (SELECT * FROM pass) UNION (SELECT * FROM fail) ) as n ON n.id = s.id
order by marks desc;
You need to understand how the different joins work to understand why you receive NULL for the marks column.
Take a look here:A Visual Explanation of SQL Joins
The relevant example for you is:
LEFT OUTER JOIN:
SELECT * FROM TableA
LEFT OUTER JOIN TableB
ON TableA.name = TableB.name
id name id name
-- ---- -- ----
1 Pirate 2 Pirate
2 Monkey null null
3 Ninja 4 Ninja
4 Spaghetti null null
The Null values you received for the marks column are rows that have no match in the left joined tables.
(the left part of the Venn diagram) the values that does have a value are the cross section between the tow groups of the Venn Diagram.
specifics for your example:
select s.id,s.name,p.marks
from student s
left join pass p on s.id=p.id
left join (select f.marks,f.id from fail f ) as nn on s.id=nn.id
order by marks desc;
The output i got is this:
id | name | Marks
-------------------
1 | B | 80
2 | C | 75
3 | A | Null
4 | D | NUll
This will return all student rows when.
students that have a passing gtade will display the grade and thous who don't will display null.
Try the below Query, use COALESCE
select s.id,s.name,COALESCE(p.marks , nn.marks) as marks
from student s
left join pass p on s.id=p.id
left join fail nn on s.id=nn.id
order by marks desc;
SQL Fiddle