Select data from multiple tables using SQL - sql

I have three tables as follows,
student table
{sid, sname}
e.g.
1 Billy
2 Bob
3 David
subject table
{subid, subname}
e.g.
15 Maths
16 Science
62 Art
grades table
{sid, subid, marks, grades}
e.g.
1 15 69 B
1 16 56 C
1 62 23 W
2 15 45 C
2 16 99 A
2 62 80 A
3 15 20 W
3 16 40 S
3 62 71 B
I want to retrieve data as follows,
Billy Maths 69 B Science 56 C Art 23 W
Bob Maths 45 C Science 99 A Art 80 A
David Maths 20 W Science 40 S Art 71 B
Is this possible in SQL? If so, how? Please help.

Yes, it is possible, you would need to use concatting functions and join. Your database of choice might have different functions to GROUP_CONCAT and CONCAT, but it should work in MariaDB and MySQL
Here is an example of how to do this.
SELECT student.sname, GROUP_CONCAT(CONCAT(subject.subname, ' ', grades.marks, ' ', grades.grades) SEPARATOR ' ')
FROM student
JOIN grades ON grades.sid = student.sid
JOIN subject ON subject.subid = grades.subid
GROUP BY student.sid
Here is an example of it running https://dbfiddle.uk/RtiHsEmQ

Is it doable, yes it is. Should you do it in the style you showed, probably not. That is known as cross tab or pivoting.
For your very specific case here is an ugly way of doing that:
select st.sname,
max(case when sb.subid = 15 then sb.subname end) as 'S1',
max(case when sb.subid = 15 then g.Marks end) as 'M1',
max(case when sb.subid = 15 then g.grades end) as 'G1',
max(case when sb.subid = 16 then sb.subname end) as 'S2',
max(case when sb.subid = 16 then g.Marks end) as 'M2',
max(case when sb.subid = 16 then g.grades end) as 'G2',
max(case when sb.subid = 62 then sb.subname end) as 'S3',
max(case when sb.subid = 62 then g.Marks end) as 'M3',
max(case when sb.subid = 62 then g.grades end) as 'G3'
from grades g
inner join student st on st.sid = g.sid
inner join subject sb on sb.subid = g.subid
group by st.sid, st.sname
order by st.sname;
I don't know what cross tab libraries may exist in mySQL to make it easier. Say in postgreSQL, you might use tablefunc.
DBFiddle demo
EDIT: Now that you removed the DBMS tag, it might be postgereSQL, then you could JSON functions to build that up even in case of unknown subjects beforehand (or using dynamic SQL is an option).

Related

How to select values from table based on field with the same id?

Let's say I have following sql tables:
Table "student"
id
first_name
last_name
1
Lillian
Nees
2
William
Lorenz
3
Mary
Moore
4
Giselle
Collins
5
James
Moultrie
6
John
Rodriguez
Table "exam_result":
exam_result_id
subject_id
student_id
mark
1
2
1
49
2
2
2
21
3
1
3
81
4
4
1
33
5
4
2
19
6
3
2
46
7
1
5
55
8
3
5
75
9
2
5
60
11
1
6
86
12
2
6
92
13
3
6
48
14
4
6
78
I need to select all students, who have all their exam marks <50 or haven't taken any exam at all.
So, in this case I need students with id 1, 2 and 4.
Closest thing I came up with is the following query, but it gives students with id 1, 2, 4 and 6. I don't need a student with id 6, since he has only 1 failed exam, not all of them.
SELECT DISTINCT s.id, s.first_name, s.last_name
FROM university.student s
LEFT JOIN
university.exam_result er ON
s.id = er.student_id
WHERE er.mark < 50 OR er.mark IS NULL;
I need it to work in both PostgreSQL and MariaDB, so I don't want anything specific to them.
The query that works is:
SELECT DISTINCT s.id, s.first_name, s.last_name
FROM university.student s
WHERE NOT EXISTS(SELECT 1 FROM university.exam_result er WHERE s.id = er.student_id AND er.mark > 49);
Thanks, #jarlh for your comment.
I would use a NOT EXISTS condition combined with a check for the marks
select s.*
from student s
where not exists (select *
from exam_result er
where er.student_id = s.id)
or 50 >= all (select er.mark
from exam_result er
where er.student_id = s.id)
One option is doing this with aggregation, by ensuring the count of marks less than 50 is equal to all the count of their marks. When the student has 0 marks, the condition will still be satisfied.
SELECT s.id, s.first_name, s.last_name
FROM student s
LEFT JOIN exam_result er ON s.id = er.student_id
GROUP BY s.id, s.first_name, s.last_name
HAVING COUNT(CASE WHEN er.mark < 50 THEN 1 END) = COUNT(er.mark)
Check the MariaDB demo and PostgreSQL demo.

getting count from multiple tables on the basis of gender and scored percentages

I have to use 3 tables
1. tbl_school_info
school_id school_name
1 St. Joseph
2 White Cross
2. tbl_student_info
student_id school_id student_name student_gender
1 1 maria liejal F
2 1 eerika carthy F
3 1 heron M
4 2 Glenn hui M
5 2 joseph M
6 2 christii F
7 2 hina moggy F
3. tbl_student_marks
marks_id school_id student_id scored_percentage
1 1 1 78
2 1 2 79
3 1 3 20
4 2 4 65
5 2 5 78
6 2 6 84
7 2 7 83
The result I need is the male, female and total student count in each school, male female passed student count and highest percentage scored male female students. The result will be like this ::
school_name || male_stud_cnt || female_stud_cnt || passed_male_cnt || passed_female_cnt || top_percentage_male ||top_percentage_female
St. Joseph 1 2 0 2 20 79
White Cross 2 2 2 2 78 84
The students whose score is below 35% has failed in the exam. How can I write query to get this result ? Is it possible to get such result using SQL query in MS SQL Server? I am unable to get the counts, how can I write such query ?
You can try using condtional aggregation with case when expression
with cte as
(
select school_name,a.student_id,student_gender,scored_percentage from
tbl_student_marks a inner join tbl_student_info b
on a.student_id=b.student_id
inner join tbl_school_info c on a.school_id=b.school_id
)
select school_name,
count(case when student_gender='M' then student_id end) as male_stud_cnt,
count(case when student_gender='F' then student_id end) as female_stud_cnt,
count(case when student_gender='M' and scored_percentage>35 then student_id end) as passed_male_cnt,
count(case when student_gender='F' and scored_percentage>35 then student_id end) as passed_female_cnt,
max(case when student_gender='M' then scored_percentage end) as top_percentage_male,
max(case when student_gender='F' then scored_percentage end) as top_percentage_female
from cte
group by school_name

Re-Organize Access Table by converting Rows to Columns

I'm pretty new to access and SQL and need some help re-organizing a table. I have the following table (sorry for the table below - having trouble posting):
ID GroupID Distance Code Start_Finish
1 44 7 A S1
2 44 14 A F1
3 45 12 B S1
4 45 16 B F1
5 45 31 C S2
6 45 36 C F2
7 45 81 B S3
8 45 88 B F3
And need for the table to be transformed into:
GroupID Code Start_Distance Finish_Distance
44 A 7 14
45 B 12 16
45 C 31 36
45 B 81 88
try something like this
Select GroupID, Code, min(distance) as Start_distance, max(distance) as Finish_distance
from Table
group by GroupID, Code
If the min and max functions don't give you what you need, try it with First() and Last() instead.
Oops - just noticed you have 2 different entries in the output for GroupID 45 Code B - is that a requirement? With that data structure and requirement, the problem gets much more difficult.
Now I see the final column in the 1st table - I think that can be used to get the output you want:
Select GroupID, Code, mid(start_finish,2) as T, min(distance) as Start_distance, max(distance) as Finish_distance
from Table
group by GroupID, Code, T
You can use conditional aggregation for this.
select GroupID
, CODE
, max(case when Left(Start_Finish, 1) = 'S' then Distance end) as Start_Distance
, max(case when Left(Start_Finish, 1) = 'F' then Distance end) as Finish_Distance
from SomeTable
group by GroupID
, CODE

How to change column name and value depending on condition?

I want to change column name and value depending on condition.
My table is..
Roll Name Mark
3 Chaity 87
1 Anis 75
4 Unknown 30
2 Badol 0
And I want to get like this
Roll Name Grade
3 Chaity A+
1 Anis A
4 Unknown F
2 Badol F
Where Mark and Grade mapping is as follows:
0 to 60 is F
61 to 79 is A
80 and above is A+
A simple CASE expression will do it:
SELECT
Roll,
Name,
Grade = CASE
WHEN Mark <= 60 THEN 'F'
WHEN Mark <= 79 THEN 'A'
ELSE 'A+'
END
FROM tbl
ONLINE DEMO
you can use CASE WHEN ... or create a Grade table
SELECT Roll, Name, Grade = case when Mark >= 80 then 'A+'
when Mark between 61 and 79 then 'A'
else 'F'
end
FROM yourtable
using a Grade table
SELECT t.Roll, t.Name, g.Grade
FROM yourtable t
outer apply
(
select x.Grade
from GradeTable x
where t.Mark between x.Mark_from and x.Mark_to
) g
But isn't the grading system a bit drastic ? You either get an A or failed :(

MS access 2010 SQL query Help

How can i turn a table from this form:
S_ID SUBJECT MARK
1 English 90
1 Math 40
1 Computer 30
2 English 85
2 Math 10
2 Computer 06
3 English 10
3 Math 20
3 Computer 40
To this form
S_ID English Math Computer
1 90 40 30
2 85 10 06
3 10 20 40
using SQL code,
I'm using MS Access 2010,
Thank you
SELECT S_ID ,
MAX(CASE WHEN subject = 'English' THEN mark ELSE null END) AS English,
MAX(CASE WHEN subject = 'Math' THEN mark ELSE null END) AS Math,
MAX(CASE WHEN subject = 'Computer' THEN mark ELSE null END) AS Computer
FROM myTable
GROUP BY S_ID
You want a cross-tab query which will yield a column per subject;
TRANSFORM Sum(MARK) AS TotalMark
SELECT
S_ID
FROM marks
GROUP BY S_ID
PIVOT SUBJECT;
if this is a one time exercise, you could use many statements, sequentially: something like this:
insert into new_table
select distinct s_id, 0,0,0
from old_table
then a series of updates
update new_table n
set english = (select english from old_table where s_id = n.s_id )
where s_id = n.s_id