getting related subjects for student in mysql - 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'

Related

only students who have the best mark

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

Mysql query to get max age by section and if two or more has same age return student with smallest id

I have a table of students with temporary test values like this:
Table students
+----+-------------+-------+-----------+
| id | section_id | age | name |
+----+-------------+-------+-----------+
| 1 | 1 | 18 | Justin |
+----+-------------+-------+-----------+
| 2 | 2 | 14 | Jillian |
+----+-------------+-------+-----------+
| 3 | 2 | 16 | Cherry |
+----+-------------+-------+-----------+
| 4 | 3 | 19 | Ronald |
+----+-------------+-------+-----------+
| 5 | 3 | 21 | Marie |
+----+-------------+-------+-----------+
| 6 | 3 | 21 | Arthur |
+----+-------------+-------+-----------+
I want to query the table such that I want to get all the maximum ages of each section. However, if two students have the same age, the table produced will return the student with smallest id.
Return:
+----+------------+-----+--------+
| id | section_id | age | name |
+----+------------+-----+--------+
| 1 | 1 | 18 | Justin |
+----+------------+-----+--------+
| 3 | 2 | 16 | Cherry |
+----+------------+-----+--------+
| 5 | 3 | 21 | Marie |
+----+------------+-----+--------+
I tried this query:
SELECT ANY_VALUE(id), ANY_VALUE(section_id), MAX(age), ANY_VALUE(name) FROM
(SELECT id, section_id, age, name FROM students ORDER BY id) as X
GROUP BY section_id
Unfortunately, there are instances that id does not match the age and name.
I have on my end:
sql_mode = only_full_group_by
and I don't have a privilege to edit that, hence the any_value function but I have no idea how to use it.
This will do what you want.
It starts by finding the maximum age per section (including duplicates).
Then it joins those results with the minimum id per section (to eliminate duplicates).
And finally, select all fields for the matching id and section combinations.
SELECT s3.*
FROM students s3
INNER JOIN (
SELECT MIN(s2.id) AS id, s2.section_id
FROM students s2
INNER JOIN (
SELECT s1.section_id, MAX(s1.age) AS age
FROM students s1
GROUP BY s1.section_id
) s1 USING (section_id, age)
GROUP BY s2.section_id
) s2 USING (id, section_id);
Working SQL fiddle: https://www.db-fiddle.com/f/aezgAYM6A5KnXykceB7At1/0
I would simply use a correlated subquery:
select s.*
from students s
where s.id = (select s2.id
from students s2
where s2.section_id = s.section_id
order by s2.age desc, s2.id asc
limit 1
);
This is pretty much the simplest way to express the logic. And with an index on students(section, age, id), it should be the most performant as well.

Join on same column name

Hello there I want to get data from two tables that share same column name. My table structure are
Table patients
---------------------------------------
| id | affiliate_id | somecolumn |
---------------------------------------
| 1 | 8 | abc |
---------------------------------------
| 2 | 8 | abc |
---------------------------------------
| 3 | 9 | abc |
---------------------------------------
Table Leads
---------------------------------------
| id | affiliate_id | someothern |
---------------------------------------
| 1 | 8 | xyz |
---------------------------------------
| 2 | 8 | xyz |
---------------------------------------
| 3 | 3 | xyz |
---------------------------------------
Now my requirement was to get COUNT(ID) from both tables in a single query. I want result like
----------------------------------------------------
| affiliate_id | total_patients | total_leads |
----------------------------------------------------
| 8 | 2 | 2 |
----------------------------------------------------
| 9 | 1 | 0 |
----------------------------------------------------
| 3 | 0 | 1 |
----------------------------------------------------
I wrote following query
SELECT `p`.`affiliate_id`, COUNT(p.id) AS `total_patients`,
COUNT(cpl.id) AS `total_leads`
FROM `patients` AS `p`
INNER JOIN `leads` AS `cpl` ON p.affiliate_id =cpl.affiliate_id
GROUP BY `p`.`affiliate_id`
But I am not getting result . This query results giving only one affiliate with same number of total_patients and total_leads
The problem is that you need to get a list of the distinct affiliate_id first and then join to your other tables to get the result:
select a.affiliate_id,
count(distinct p.id) total_patients,
count(distinct l.id) total_leads
from
(
select affiliate_id
from patients
union
select affiliate_id
from leads
) a
left join patients p
on a.affiliate_id = p.affiliate_id
left join leads l
on a.affiliate_id = l.affiliate_id
group by a.affiliate_id;
See SQL Fiddle with Demo
Two ways:
Select l.affiliate_id ,
count(distinct p.id) patientCount,
count(distinct l.id) LeadCOunt
From patients p Join leads l
On l.affiliate_id = p.Affiliate_id
Group By l.affiliate_id
or, (assuming affiliates are in their own table somewhere)
Select Affiliate_id,
(Select Count(*) From Patients
Where Affiliate_id = a.Affiliate_id) patientCount,
(Select Count(*) From Leads
Where Affiliate_id = a.Affiliate_id) LeadCount
From affiliates a

For each row returned, also return a list of rows that match on some other columns

For example:
I have a database of students and last classes, and for each student in a class, I also return a list of all the other students in the class. Sample table would be like
StudentID ClassID
a 1
b 1
c 1
a 2
a 3
c 2
b 3
I want to select of studentID = a classes, but also know what other students will be in his class as so:
StudentID ClassID Classmates
a 1 a,b,c
a 2 a,c
a 3 a,b
I tried doing a query like this:
SELECT * FROM
(SELECT *, GROUP_CONCAT(StudentID)
FROM enrolled GROUP BY studentID, ClassID)
AS temporary WHERE temporary.StudentID=a
The problem is that GROUP BY condenses the rows, so returning a list of b's classes wouldnt show anything because a is prioritized in the studentID column.
I researched everywhere and couldn't find anything -- help?
Perhaps you're looking for something like this:
select e.studentid, e.classid, dt.classmates
from (
select classid, group_concat(studentid) as classmates
from enrolled
group by classid
) as dt
join enrolled e on e.classid = dt.classid
where e.studentid = 'a'
The basic idea is to generate the classmates lists by grouping on just classid and then join that to enrolled so that you can select the student you're interested in.
When you use where e.studentid = 'a', you get this:
+-----------+---------+------------+
| studentid | classid | classmates |
+-----------+---------+------------+
| a | 1 | a,b,c |
| a | 2 | a,c |
| a | 3 | a,b |
+-----------+---------+------------+
filtering on e.studentid = 'b' yields:
+-----------+---------+------------+
| studentid | classid | classmates |
+-----------+---------+------------+
| b | 1 | a,b,c |
| b | 3 | a,b |
+-----------+---------+------------+
and filtering on e.studentid = 'c' gives you this:
+-----------+---------+------------+
| studentid | classid | classmates |
+-----------+---------+------------+
| c | 1 | a,b,c |
| c | 2 | a,c |
+-----------+---------+------------+

Third join to get first_Name and Last_Name of account

I have the following query which matches Account_ID's on Accounts table with the AccountID's on project assigned table and displays the accounts assigned to a project:
SELECT proj.ProjectID, A.Project_Title, B.Account_ID, B.Username, B.Access_Type
FROM Project_Assigned proj
INNER JOIN Account B
ON proj.AccountID = B.Account_ID
INNER JOIN Project A
ON proj.ProjectID = A.Project_ID
WHERE proj.ProjectID = 1;
What I want to do now is get the First_Name, Last_Name from Client table and Agency_Employee table and display the information matched against the Account_ID's. Both Client_ID and Employee_ID are foreign keys of Account_ID. How would I add this information into the join above?
I have attempted to add an additional join but I always get a result set match 0 which I know shouldn't be the case.
Client Table:
+-----------+------------+-----------+
| Client_ID | First_Name | Last_Name |
+-----------+------------+-----------+
| 4 | Phil | Jones |
+-----------+------------+-----------+
Employee Table:
+-------------+------------+-----------+
| Employee_ID | First_Name | Last_Name |
+-------------+------------+-----------+
| 2 | John | Smith |
| 5 | Bob | Jones |
| 6 | Fred | Tucker |
+-------------+------------+-----------+
Account Table:
+------------+----------+
| Account_ID | Username |
+------------+----------+
| 1 | Dan |
| 2 | rjm |
| 3 | pw |
| 4 | Philly |
| 5 | bob |
| 6 | fred |
+------------+----------+
Project Assigned Table:
+-----------+-----------+
| ProjectID | AccountID |
+-----------+-----------+
| 1 | 1 |
| 1 | 2 |
| 1 | 4 |
+-----------+-----------+
I'm not sure, but you seem to indicate that for an Account with ID 5, the Client will have ID 5 and the Employee will have ID 5? Slightly odd, and you probably want to read up on normalisation, but shouldn't this work?
SELECT
proj.ProjectID,
Project.Project_Title,
Account.Account_ID, Account.Username,
Client.First_Name AS Client_First_Name, Client.Last_Name AS Client_Last_Name,
Employee.First_Name AS Employee_First_Name, Employee.Last_Name AS Employee_Last_Name
FROM `Project_Assigned` proj
INNER JOIN `Account` ON (proj.AccountID = Account.Account_ID)
INNER JOIN `Project` ON (proj.ProjectID = Project.Project_ID)
LEFT JOIN `Client` ON (Account.Account_ID = Client.Client_ID)
LEFT JOIN `Employee` ON (Account.Account_ID = Employee.Employee_ID)
WHERE proj.ProjectID = 1;
edit
Okay, I've updated my answer.
You'll have two first names and two lastnames, one of each will always be null.
If you only want one contact name, try this:
SELECT
COALESCE(Client.First_Name, Employee.First_Name) FirstName,
COALESCE(Client.Last_Name, Employee.Last_Name) LastName
SELECT c.First_Name, c.Last_Name, e.First_Name, e.Last_Name
FROM client c, employee e, account a
WHERE c.Client_ID = a.Account_ID OR e.Employee_ID = a.Account_ID
OR
SELECT c.First_Name, c.Last_Name
FROM client c, account a
WHERE c.Client_ID = a.Account_ID
UNION
SELECT e.First_Name, e.Last_Name
FROM employee e, account a
WHERE e.Employee_ID = a.Account_ID