How can I write a query to do this? - sql

I'm working with this database: link.
I want to write a query to get the student name who belongs to class 2 (class_id = 2) and has the highest average, and the highest degree in a specific course(course_id = 9) at the same time, and if there is no student with these data then return nothing.
I managed to write half of it and get the student with the highest average in class 2, but I didn't know how to add the 2nd condition(highest degree in course 9), I tried in many ways it didn't work.
This is my query: (sorry for the messy code, I'm still learning)
SELECT s.studentname
FROM students s
JOIN section se
ON s.sectionid = se.sectionid
JOIN courses_student cs
ON s.student_id = cs.student_id
WHERE se.classes_id = 2
GROUP BY s.studentname
HAVING ( Avg(cs.exam_season_one)
+ Avg(cs.exam_season_two)
+ Avg(cs.degree_season_one)
+ Avg(cs.degree_season_two) ) / 4 = (SELECT
Max(( Avg(cs.exam_season_one)
+ Avg(cs.exam_season_two)
+ Avg(cs.degree_season_one)
+ Avg(cs.degree_season_two)
) / 4)
FROM students s
JOIN section se
ON s.sectionid =
se.sectionid
JOIN courses_student cs
ON s.student_id =
cs.student_id
WHERE se.classes_id = 2
GROUP BY s.studentname)
The way I calculate the average is:
( Avg(cs.exam_season_one)
+ Avg(cs.exam_season_two)
+ Avg(cs.degree_season_one)
+ Avg(cs.degree_season_two) ) / 4
When the way I calculate the degree in a specific course is:
(cs.exam_season_one
+ cs.exam_season_two
+ cs.degree_season_one
+ cs.degree_season_two ) / 4
So how can I edit my query to add the second condition? or is there a better way than this?
PS: I use oracle

You can find the maximum and the average for each student and then use analytic functions to find the greatest for both conditions:
SELECT studentname
FROM (
SELECT studentname,
ROW_NUMBER() OVER (ORDER BY avg_degree DESC) AS rn_avg,
ROW_NUMBER() OVER (ORDER BY max_degree DESC) AS rn_max
FROM (
SELECT MAX(s.studentname) AS studentname,
AVG(
cs.exam_season_one + cs.exam_season_two + cs.degree_season_one + cs.degree_season_two
) / 4 AS avg_degree,
MAX(
CASE
WHEN cs.courses_id = 9
THEN cs.exam_season_one + cs.exam_season_two + cs.degree_season_one + cs.degree_season_two
END
) / 4 AS max_degree
FROM students s
JOIN section se
ON s.sectionid = se.sectionid
JOIN courses_student cs
ON s.student_id = cs.student_id
WHERE se.classes_id = 2
GROUP BY s.student_id
)
)
WHERE rn_avg = 1
AND rn_max = 1;
Again, don't GROUP BY studentname as you can have two students with the same name.
Which outputs:
STUDENTNAME
maher
sqlfiddle here

Related

How to check more than one unrelated conditions in SQL?

I have following query:
SQL> SELECT DISTINCT INSTRUCTORADDRESSMODPER.instructor_id, Instructor.instructor_name, InstructorRank.salary, Student.specification_id
2 FROM INSTRUCTORADDRESSMODPER
3 JOIN Student ON INSTRUCTORADDRESSMODPER.student_id = Student.student_id
4 JOIN Instructor ON INSTRUCTORADDRESSMODPER.instructor_id = Instructor.instructor_id
5 JOIN InstructorRank ON Instructor.instructor_rank = InstructorRank.instructor_rank
6 ORDER BY specification_id;
which has yielded following result:
I was trying to get result which shows same column values for instructors with same salary and same specification as highlighted in the figure. Now these two conditions require completely different checks and I don't even know how to get started.
You need something like this ?
SELECT instructor_id, instructor_name, instructor_name, specification_id
FROM (
SELECT DISTINCT INSTRUCTORADDRESSMODPER.instructor_id, Instructor.instructor_name, InstructorRank.instructor_name, Student.specification_id
, COUNT(distinct INSTRUCTORADDRESSMODPER.instructor_id)over(partition by InstructorRank.salary, Student.specification_id) cnt
FROM INSTRUCTORADDRESSMODPER
JOIN Student ON INSTRUCTORADDRESSMODPER.student_id = Student.student_id
JOIN Instructor ON INSTRUCTORADDRESSMODPER.instructor_id = Instructor.instructor_id
JOIN InstructorRank ON Instructor.instructor_rank = InstructorRank.instructor_rank
ORDER BY specification_id
)
WHERE cnt > 1
;
You can use the window function count as follows:
Select * from
(SELECT DISTINCT INSTRUCTORADDRESSMODPER.instructor_id, Instructor.instructor_name, InstructorRank.salary, Student.specification_id,
Count(1) over (partition by InstructorRank.salary, Student.specification_id) as cnt
FROM INSTRUCTORADDRESSMODPER
JOIN Student ON INSTRUCTORADDRESSMODPER.student_id = Student.student_id
JOIN Instructor ON INSTRUCTORADDRESSMODPER.instructor_id = Instructor.instructor_id
JOIN InstructorRank ON Instructor.instructor_rank = InstructorRank.instructor_rank)
Where cnt > 1
ORDER BY specification_id;
SQL> SELECT DISTINCT INSTRUCTORADDRESSMODPER.instructor_id,
Instructor.instructor_name,InstructorRank.salary, Student.specification_id
FROM INSTRUCTORADDRESSMODPER
WHERE 1=1
AND INSTRUCTORADDRESSMODPER.student_id = Student.student_id
AND INSTRUCTORADDRESSMODPER.instructor_id = Instructor.instructor_id
AND Instructor.instructor_rank = InstructorRank.instructor_rank
AND ORDER BY specification_id
AND ...;
With the above use, desired conditions can be added.

SQL Server : Pivot Table Odd result,

So, full disclosure, this is my first time using a pivot table and the result set has multiple columns as a result. In this example, we will use the result set without a pivot table applied.
This student has taken multiple courses. What the ideal end result would be, is to count how many courses this student has taken. So if I look I see that the Student has taken Algebra 1 5 times, English 1 2 times and Geometry 2 times.
The Next image is the query with the pivot applied, oh and to make things complicated, its a monster query before trying to attempt the pivot.
So we see in this, that Algebra 1 has a count of 0, Geometry has a count of 2 and English has a count of 0. This is the same query as before but just using this student.
Query:
SELECT *
FROM
(SELECT
[StudentNumber], psat.MathScaledScore, psat.EBRWScore,
SCReady.MathScaleScore, SCReady.ReadingScaleScore,
SpringMap.MathPercentile, SpringMap.ReadingPercentile,
Coursework.Course_name
FROM
[OnlineApplications].[dbo].[Users] U
LEFT JOIN
[OnlineApplications].[dbo].ContactInfoes C ON C.ContactInfoId = U.UserId
LEFT JOIN
(SELECT *
FROM [DOENRICH-SQL].[enrich_prod].dbo.HCS_view_Most_Recent_PSAT_Scores Scores) AS psat ON U.StudentNumber = psat.number
LEFT JOIN
(SELECT
stud.Number, sc.DateTaken,
CASE sc.ELALev
WHEN 'E8AE0E4D-AD36-41D8-AC89-627D19661803' THEN 'Exceeds Expectations'
WHEN 'C9F2CDA2-D904-438B-9DD3-94EFC9111A0E' THEN 'Approaches Expectations'
WHEN '9B39E28F-89C8-44AD-A8F2-1463192F88F1' THEN 'Does Not Meet Expectation'
WHEN '87247DB1-4A57-419E-9619-7B43B02B1135' THEN 'Meets Expectations'
END ELALev,
sc.ELASS AS ELAScaleScore, sc.elavss AS VerticalScaleScore,
sc.elassread AS ReadingScaleScore,
sc.ELARS10 AS RawScoreStandard10, sc.elASPR AS ELAStatePercentileRank,
MathLev = CASE MathLev
WHEN 'E8AE0E4D-AD36-41D8-AC89-627D19661803' THEN 'Exceeds Expectations'
WHEN 'C9F2CDA2-D904-438B-9DD3-94EFC9111A0E' THEN 'Approaches Expectations'
WHEN '9B39E28F-89C8-44AD-A8F2-1463192F88F1' THEN 'Does Not Meet Expectation'
WHEN '87247DB1-4A57-419E-9619-7B43B02B1135' THEN 'Meets Expectations'
END,
sc.MATHSS AS MathScaleScore, sc.MathVSS AS MathVerticalScaleScore,
sc.mathspr AS MathStatePercentileRank
FROM
[DOENRICH-SQL].[enrich_prod].dbo.t_sc_ready sc
JOIN
[DOENRICH-SQL].[enrich_prod].dbo.Student stud ON sc.StudentID = stud.ID
WHERE
DateTaken = '2019-05-17') AS SCReady ON SCReady.number = U.studentnumber
LEFT JOIN
(
select stud.Number, max(map.readingPercentile) as ReadingPercentile, max(map.mathPercentile) as MathPercentile
from [DOENRICH-SQL].[ENRICH_PROD].[dbo].t_map map
join [DOENRICH-SQL].[ENRICH_PROD].[INFORM].[Map_GradeLevelID] mapping on map.GradeLevelID = mapping.DestID
JOIN [DOENRICH-SQL].[ENRICH_PROD].[dbo].[Student] stud on map.StudentID = stud.ID
where DateTaken >= '2018-08-01'
group by stud.Number
) as SpringMap on SpringMap.number = U.Studentnumber
left join (
SELECT * FROM OPENQUERY(PSPROD,'
Select B.STUDENT_NUMBER , A.COURSE_NAME, A.GRADE_LEVEL, A.SCHOOLNAME, A.GRADE
from PS.STOREDGRADES A
join PS.STUDENTS B ON A.STUDENTID = B.ID
AND STORECODE in (''Q1'',''Q2'',''Q3'',''Q4'',''F1'')
AND (COURSE_NAME LIKE ''%Algebra 1%'' OR COURSE_NAME LIKE ''%Geometry Honors%'' OR COURSE_NAME LIKE ''%English 1%'')
group by B.STUDENT_NUMBER , A.COURSE_NAME, a.STORECODE, A.GRADE, A.PERCENT, A.GRADE_LEVEL, A.SCHOOLNAME
ORDER BY STUDENT_NUMBER, STORECODE DESC
'
)
) as Coursework on Coursework.STUDENT_NUMBER = U.StudentNumber
join [OnlineApplications].[dbo].ScholarsApps Sapps on Sapps.ScholarsAppId = u.UserId
where AppYear = 2019 and StudentNumber <> '' and StudentNumber = '17476'
) T
PIVOT (
COUNT (COURSE_NAME)
FOR course_name IN (
[Algebra 1],
[Geometry Honors],
[English 1])
)
as Pivot_table
Again, very complicated query before and I'm not clear if I'm using the pivot function correctly.
I'd love to have this pivot with the counts of the courses.

Query-ing data to get average grade

I am trying to get the average grade by querying data from different tables and while it seems like everything is as it is supposed to be, the query does not work. Tables are the following:
grade(id_grade, id_subject, student_code and grade)
student(student_code, first_name, last_name, birthdate, average_grade)
subject(id_subject, credits, subject_code, name, teacher)
Average grade is calculated using the following formula: (sum of all matching grades (that correspond to student code in student) * average grade from student table + credits from subject * newest added grade) / (sum of all matching grades + credits from the subject).
The query is the following:
UPDATE student AS s
SET
average_grade = (
(SUM(g.grade) * s.average_grade + sub.credits * g.grade) /
(SUM(g.grade) + sub.credits) )
FROM grade AS g, subject AS sub
WHERE s.student_code = g.student_code AND
g.id_subject = sub.id_subject AND s.student_code = 141837;
Keep in mind that this is PostgreSQL so some input may differ from other SQL-s, but this is what I was able to come up with.
I also tried the following as well, but also yielded no results:
UPDATE student AS s
LEFT JOIN grade AS g ON (s.student_code = g.student_code)
LEFT JOIN subject AS sub ON (g.id_subject = sub.id_subject)
SET average_grade = (
(SUM(g.grade) * s.average_grade + sub.credits * g.grade) /
(SUM(g.grade) + sub.credits))
WHERE s.student_code = 141837;
Any help is highly appreciated!
You need to aggregate as a subquery. I don't think the average calculation is correct; I think you want something more like this:
UPDATE student s
SET average_grade = g.average_grade
FROM (SELECT g.student_code,
SUM(g.grade * s.credits) / SUM(s.credits) as average_grade
FROM grade g JOIN
subject s
ON g.id_subject = s.id_subject
GROUP BY g.student_code
) g
WHERE s.student_code = g.student_code AND
s.student_code = 141837;

How to use alias of a subquery to get the running total?

I have a UNION of 3 tables for calculating some balance and I need to get the running SUM of that balance but I can't use PARTITION OVER, because I must do it with a sql query that can work in Access.
My problem is that I cannot use JOIN on an alias subquery, it won't work.
How can I use alias in a JOIN to get the running total?
Or any other way to get the SUM that is not with PARTITION OVER, because it does not exist in Access.
This is my code so far:
SELECT korisnik_id, imePrezime, datum, Dug, Pot, (Dug - Pot) AS Balance
FROM (
SELECT korisnik_id, k.imePrezime, r.datum, SUM(IIF(u.jedinstven = 1, r.cena, k.kvadratura * r.cena)) AS Dug, '0' AS Pot
FROM Racun r
INNER JOIN Usluge u ON r.usluga_id = u.ID
INNER JOIN Korisnik k ON r.korisnik_id = k.ID
WHERE korisnik_id = 1
AND r.zgrada_id = 1
AND r.mesec = 1
AND r.godina = 2017
GROUP BY korisnik_id, k.imePrezime, r.datum
UNION ALL
SELECT korisnik_id, k.imePrezime, rp.datum, SUM(IIF(u.jedinstven = 1, rp.cena, k.kvadratura * rp.cena)) AS Dug, '0' AS Pot
FROM RacunP rp
INNER JOIN Usluge u ON rp.usluga_id = u.ID
INNER JOIN Korisnik k ON rp.korisnik_id = k.ID
WHERE korisnik_id = 1
AND rp.zgrada_id = 1
AND rp.mesec = 1
AND rp.godina = 2017
GROUP BY korisnik_id, k.imePrezime, rp.datum
UNION ALL
SELECT uu.korisnik_id, k.imePrezime, uu.datum, '0' AS Dug, SUM(uu.iznos) AS Pot
FROM UnosUplata uu
INNER JOIN Korisnik k ON uu.korisnik_id = k.ID
WHERE korisnik_id = 1
GROUP BY uu.korisnik_id, k.imePrezime, uu.datum
) AS a
ORDER BY korisnik_id
You can save a query (let's name it Query1) for the UNION of the 3 tables and then create another query that returns each row in the first query and calculates the sum of the rows that are before it (optionally checking that they are in the same group).
It should be something like this:
SELECT *, (
SELECT SUM(Value) FROM Query1 AS b
WHERE b.GroupNumber=a.GroupNumber
AND b.Position<=a.Position
) AS RunningSum
FROM Query1 AS a
However, it's more efficient to do that in the report.

Rollup / recursive addition SQL Server 2008

I have a query with rollup that outputs data like (the query is a little busy, but I can post if necessary)
range subCounts Counts percent
1-9 3 100 3.0
10-19 13 100 13.0
20-29 30 100 33.0
30-39 74 100 74.0
NULL 100 100 100.0
How is it possible to keep a running summation total of percent? Say I need to find the bottom 15 percentile, in this case 3+13=16 so I would like for the last row to be returned read
range subCounts counts percent
10-19 13 100 13.0
EDIT1: here the query
select '$'+cast(+bin*10000 + ' ' as varchar(10)) + '-' + cast(bin*10000+9999 as varchar(10)) as bins,
count(*) as numbers,
(select count(distinct patient.patientid) from patient
inner join tblclaims on patient.patientid = tblclaims.patientid
and patient.admissiondate = tblclaims.admissiondate
and patient.dischargedate = tblclaims.dischargedate
inner join tblhospitals on tblhospitals.hospitalnpi = patient.hospitalnpi
where (tblhospitals.hospitalname = 'X')
) as Totals
, round(100*count(*)/cast((select count(distinct patient.patientid) from patient
inner join tblclaims on patient.patientid = tblclaims.patientid
and patient.admissiondate = tblclaims.admissiondate
and patient.dischargedate = tblclaims.dischargedate
inner join tblhospitals on tblhospitals.hospitalnpi = patient.hospitalnpi
where (tblhospitals.hospitalname = 'X')) as float),2) as binsPercent
from
(
select tblclaims.patientid, sum(claimsmedicarepaid) as TotalCosts,
cast(sum(claimsmedicarePaid)/10000 as int) as bin
from tblclaims inner join patient on patient.patientid = tblclaims.patientid
and patient.admissiondate = tblclaims.admissiondate
and patient.dischargedate = tblclaims.dischargedate
inner join tblhospitals on patient.hospitalnpi = tblhospitals.hospitalnpi
where tblhospitals.hospitalname = 'X'
group by tblclaims.patientid
) as t
group by bin with rollup
OK, so for whomever might use this for reference I figured out what I needed to do.
I added row_number() over(bin) as rownum to the query and saved all of this as a view.
Then I used
SELECT *,
SUM(t2.binspercent) AS SUM
FROM t t1
INNER JOIN t t2 ON t1.rownum >= t2.rownum
GROUP BY t1.rownum,
t1.bins, t1.numbers, t1.uktotal, t1.binspercent
ORDER BY t1.rownum
by joining t1.rownum >=t2.rownum you can get the rolling count sort of thing.
This isn't exactly what i was looking for, but it's on the same track:
http://blog.tallan.com/2011/12/08/sql-server-2012-windowing-functions-part-1-of-2-running-and-sliding-aggregates/ and http://blog.tallan.com/2011/12/19/sql-server-2012-windowing-functions-part-2-of-2-new-analytic-functions/ - check out PERCENT_RANK
CUME_DIST
PERCENTILE_CONT
PERCENTILE_DISC
Sorry for the lame answer