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

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.

Related

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

Cross Join for Missing table (Select all and Select or Insert the Missing Row Only)

I have two table and have to fill in a list of missing values in one of the table based on the other one. First table has student's information and the second table has Grade related info, Grade and Grade description.
Table One
ID Name yearWithUs Grade Course Level
1 Jim 2004 4 4
2 Jim 2004 4 1
2 Jim 2003 3 3
4 Jim 2002 2 3
4 Jim 2002 2 1
3 Jim 2001 1 2
3 Jim 2001 1 1
Table two -- logic is.. A Student in a higher Course Level can change to a lower Course Level at anytime during the semester. And It can only go downward 1 level at a time. Example: Jim in his second grade first was assigned to attend course in level 3. He need to attend course in level 2 first before he can attend course in level 1. Means. Row for course level 2 at jim's first grade is missing.
Table Two
ID Grade Grade_Desc Course Level Course Desc
1 1 First Grade 1 Basic
2 1 First Grade 2 Normal
3 1 First Grade 3 Hard
4 1 First Grade 4 Expert
5 2 Second Grade 1
6 2 Second Grade 2
7 2 Second Grade 3
8 2 Second Grade 4
. . .
. . .
. . .
Logic of Table Two
ID Grade Grade_Desc Course Level Possible Move
1 1 First Grade 1 Null
2 1 First Grade 2 1
3 1 First Grade 3 2
4 1 First Grade 4 3
Ouptput one ... how to use select statement to return Jim's Grade?
ID Name Grade_Desc Grade yerWithUs Course Level
1 Jim Fourth Grade 4 2004 4
2 Jim Fourth Grade 4 2004 3
3 Jim Fourth Grade 4 2004 2
4 Jim Fourth Grade 4 2004 1
5 Jim Third Grade 3 2003 3
6 Jim Second Grade 2 2002 3
7 Jim Second Grade 2 2002 2
8 Jim Second Grade 2 2002 1
9 Jim First Grade 2 2001 2
10 Jim First Grade 2 2001 1
Output Two..How to retrieve only the missing row into a new temp table?
ID Name Grade_Desc Grade yearWithUs Course Level
2 Jim Fourth Grade 4 2004 3
3 Jim Fourth Grade 4 2004 2
7 Jim Second Grade 2 2002 2
I am currently is using a messy Cursor Statement to do it. The structure looks really messy and hard to debug return errors. I did a lot of research, and saw people use Cross Join to fill the missing portion which looks really clean (See example below)... I have tried the script it myself in many different way by using the cross join example below...obviously, I failed. I found a similar question in stackoverflow..but I am not able to understand how does it work and why without looking at the data....I need help to understand how to use cross join to rerun missing row? and I am open to any other possible solution.
"SELECT calendar.Date,
Category.Cat,
Score = ISNULL(Scores.Score, 0)
FROM Calendar
CROSS JOIN Catogory
LEFT JOIN Scores
ON Scores.Cat = Category.Cat
AND Scores.Date = Calendar.Date
WHERE Calendar.DayOfMonth = 1;"
Inserting missing rows with a join
Thank You
This will produce that output:
select distinct name, grade, Grade_Desc
from one
cross join two
If select is all you want then:
Select row_number() over(order by (select 1)) as id, * from
(Select distinct name from t1)t1
cross join t2
Here is fiddle http://sqlfiddle.com/#!6/a8a42/3
Try this out:
Create #Temp
DECLARE #Name VARCHAR(100) = 'Jim'
SELECT ROW_NUMBER() OVER (ORDER BY B.Grade DESC,B.CourseLevel DESC) ID,
A.Name,
B.Grade_Desc,
B.Grade,
A.YearWithUs,
B.[Course Level]
INTO #temp
FROM
(
SELECT DISTINCT Name,YearWithUs,Grade
FROM TableOne
WHERE Name = #Name
) A
INNER JOIN TableTwo B
ON A.Grade = B.Grade
Output One
SELECT *
FROM #temp
Output Two into #OutputTwo(temp table)
SELECT A.* INTO #OutputTwo
FROM #temp A
LEFT JOIN TableOne B
ON A.Grade = B.Grade
AND A.[Course Level] = B.[Course Level]
WHERE A.Grade IS NULL AND A.[Course Level] IS NULL

sqlquery insert data from one table to other with id from columnname

I have an old database with some complex joining of the data. As given below
Subjects
Id Name
-------------------------------
1 Math
2 Science
3 English
Results
Id StudentId Math MathMax Science ScienceMax English EnglishMax TotalMarks Max
-----------------------------------------------------------------------------------------
1 81 5 10 6 10 3 10 14 30
2 82 8 10 8 10 9 10 25 30
3 83 7 10 8 10 7 10 22 30
Now I am trying to convert it to more easy and readable database. So I come up with the tables like
Results
ResultId StudentId TotalMarks MaxMarks
-------------------------------------------
1 81 14 30
2 82 25 30
3 83 22 30
ResultDetails
Id ResultId SubjectId Marks MaxMarks
--------------------------------------------------------
1 1 1 5 10
2 1 2 6 10
3 1 3 7 10
& so one
Now the real question I can insert data in the new Results table but I am confused on the second table. I can't understand that how to pass column name of one table and get the id of that name from second table and insert it in the third one.
I am trying on but can't understand the right commands to achieve this. My database already have 50000+ records and I have to merge them according to this new tables.
Assuming this is a one-off conversion of data, and you've already populated your new Results table, something as simple as the following should work:
INSERT INTO ResultDetails(ResultId, SubjectId, Marks, MaxMarks)
SELECT
R.StudentId,
1 AS SubjectId,
OldR.Math AS Marks,
OldR.MathMax AS MaxMarks
FROM Results R
INNER JOIN OldResults OldR ON R.StudentId = OldR.StudentId
INSERT INTO ResultDetails(ResultId, SubjectId, Marks, MaxMarks)
SELECT
R.StudentId,
2 AS SubjectId,
OldR.Science AS Marks,
OldR.ScienceMax AS MaxMarks
FROM Results R
INNER JOIN OldResults OldR ON R.StudentId = OldR.StudentId
INSERT INTO ResultDetails(ResultId, SubjectId, Marks, MaxMarks)
SELECT
R.StudentId,
3 AS SubjectId,
OldR.English AS Marks,
OldR.EnglishMax AS MaxMarks
FROM Results R
INNER JOIN OldResults OldR ON R.StudentId = OldR.StudentId
It's not a very elegant solution, but it doesn't need to be for a one-off conversion.

SQL Server: how to get this result from this table (example inside)

I would like to ask you guys how would you do a query to show the data of this table:
week name total
==== ====== =====
1 jon 15.2
1 jon 10
1 susan 10
1 howard 9
1 ben 10
3 ben 30
3 susan 10
3 mary 10
5 jon 10
6 howard 12
7 tony 25.1
8 tony 7
8 howard 10
9 susan 6.2
9 howard 9
9 ben 10
11 howard 10
11 howard 10
like this:
week name total
==== ====== =====
1 ben 10
1 howard 9
1 jon 25.2
1 mary 0
1 susan 10
1 tony 0
3 ben 30
3 howard 0
3 jon 0
3 mary 10
3 susan 10
3 tony 0
5 ben 0
5 howard 0
5 jon 10
5 mary 0
5 susan 0
5 tony 0
6 ben 0
6 howard 12
6 jon 0
6 mary 0
6 susan 0
6 tony 0
7 ben 0
7 howard 0
7 jon 0
7 mary 0
7 susan 0
7 tony 25.1
8 ben 0
8 howard 10
8 jon 0
8 mary 0
8 susan 0
8 tony 7
9 ben 10
9 howard 9
9 jon 0
9 mary 0
9 susan 6.2
9 tony 0
11 ben 0
11 howard 20
11 jon 0
11 mary 0
11 susan 0
11 tony 0
I tried something like:
select t1.week_id ,
t2.name ,
sum(t1.total)
from xpto as t1 ,
xpto as t2
where t1.week_id = t2.week_id
group by t1.week_id, t2.name
order by t1.week_id, t2.name
But I'm failing to understand the "sum" part and I can't figure out why...
Any help would be very appreciated. Thanks in advance, and sorry for my english.
you might try something like the following:
select week = w.week ,
name = n.name ,
sum_total = coalesce( sum( d.total ) , 0 )
from ( select distinct week from my_table ) w
cross join ( select distinct name from my_table ) n
left join my_table d on d.week = w.week
and d.name = n.name
group by w.week ,
n.name
order by 1,2
The cross join of first two derived tables constructs their cartesian product: all week and all names from the table, regardless of whether or not a particular week/name combination exists.
We then take that, join it against the actual data rows and summarize it, using coalesce() to collapse any null results from the aggregate function sum() to 0.
As I understood you you want to show all weeks and all names across all table regardless whether they were entered for the week or not. To do so you will need to first build a list of all names and week, cross join them and then join them to the list of totals, like this:
;with names as (select distinct name from xpto),
weeks as (select distinct week from xpto),
totals as (select week, name, sum(total) as total
from xpto group by week, name)
select w.week, n.name, coalesce(total, 0) as total
from names n cross join weeks w
left join totals t on t.name=n.name and w.week = t.week
order by 1,2
SQL Fiddle
I've edited my answer because I now understand what you want to do a bit better.
I prefer doing things in several steps rather than trying to do several transformations of data with a single join or subquery. So I would approach this like this:
;
with Weeks as (
select distinct Week_id
from xpto
)
, Names as (
select distinct Name
from xpto
)
, Scores as (
select week_id
, name
, score = sum(t1.score)
from xpto t1
group by
t1.week_id
, t1.name
)
, WeeksAndNames as (
select week_id
, name
from Weeks
cross join Names
)
-- The final query!
select wan.week_id
, wan.name
, total = COALESCE(s.total,0)
from WeeksAndNames wan
left join Scores s
on wan.week_id = s.week_id
and wan.name = s.name
order by
wan.week_id
, wan.name
Lengthy, I'll grant you, and you can probably condense it. But this shows each step you need to go through to transform your data into the list you want.

Sql query for Top N rows using a group by

Hi i tried many ways to solve this but missing some thing..
I have two tables Student and Score
Sid Cid Score
6 1 90
1 1 80
4 1 80
3 1 70
2 1 60
6 2 80
1 2 70
2 2 60
4 2 60
5 2 50
4 3 80
7 3 80
6 3 70
8 3 60
2 3 50
Sid Sname Sbday Ssex
1 As 1980 female
2 Al 1986 male
3 An 1989 male
4 ja 1986 male
5 ma 1983 female
6 phi 1986 male
7 Geo 1993 male
8 lil 1990 female
9 cha 1993 male
I need to Return Sid and Sname of the students who have the top 2 highest score for each course.
If existed, return Sid and Sname of the student who has the highest score among all the male students for each course.
Here top 2 highest score is not just top two records in a group for ex : top 2 highest score in 1st group is 90, 80 ,80 .
I need out put like this
Sid Cid Score
6 1 90
1 1 80
4 1 80
6 2 80
1 2 70
2 2 60
4 2 60
4 3 80
7 3 80
6 3 70
I tried the following code :
select A.Sid , S.SNAME, Score,Cid
from Score a,STUDENTS S
where 2 >(select count(Cid)
from Score
where Cid=a.Cid
and Score>a.Score)
AND A.SID = S.SID
order by Cid,Score desc
For the first question (list the students who have the top 2 highest score for each course), you should try this:
SELECT SC.Sid , ST.SNAME, SC.Score, SC.Cid
FROM ( SELECT *, DENSE_RANK() OVER(PARTITION BY Cid ORDER BY Score DESC) TopScore
FROM Score) AS SC
INNER JOIN Students AS ST
ON SC.Sid = ST.Sid
WHERE SC.TopScore <= 2
For your second quetion (the student who has the highest score among all the male students for each course), do the following:
SELECT A.Sid , A.SNAME, A.Score, A.Cid
FROM ( SELECT SC.Sid , ST.SNAME, SC.Score, SC.Cid, DENSE_RANK() OVER(PARTITION BY Cid ORDER BY Score DESC) TopScore
FROM Score AS SC
INNER JOIN Students AS ST
ON SC.Sid = ST.Sid
WHERE ST.Ssex = 'male') A
WHERE A.TopScore = 1
Hope it helps.