How can I select data from multiple SQL tables? - sql

I have 2 tables, as described below:
Grades
student id,
exem1,
exam2,
exam3
Names
student id,
names
I want to display the names of students, their average on the 3 exams, and a letter grade. The letter grade is computed as follows:
90+ is an “A”, 80 - <90 will be a “B”, and so on. How should i do it.?

> SELECT n.student_names, ((g.exam1+g.exam2+g.exam3)/3) AS 'AVERAGE_RESULT', (CASE
WHEN ((g.exam1+g.exam2+g.exam3)/3) =90
THEN 'A'
WHEN (((g.exam1+g.exam2+g.exam3)/3) BETWEEN 80 AND 89)
THEN 'B'
WHEN (((g.exam1+g.exam2+g.exam3)/3) BETWEEN 70 AND 79)
THEN 'C'
ELSE 'D') END AS 'Student_grades',
FROM names n, grades g
WHERE n.students_id = g.students_id;
Using BETWEEN to include the ranges

SELECT n.NAME AS "Name", CASE WHEN ((g.exam1+g.exam2+g.exam3)/3) = 90 then 'A'
WHEN ((g.exam1+g.exam2+g.exam3)/3) < 90 then 'B'
ELSE 'C'
END AS "Grade"
FROM NAMES AS n INNER JOIN GRADES AS g
ON g.student_id = n.student_id
Modify the ranges as you want.

Related

sql case statement of case results

I want to take letter grades convert to a number average and then convert back to a letter.
The
select
avg(case when grade = 'A' then 4
when grade = 'B' then 3
when grade = 'C' then 2
when grade = 'F' then 1 end)
from student
where id = 'test'
this works perfectly
I want to now convert the average back to a letter.
I tried and it works but I cant help but think there isn't a better easier way to accomplish this
select case(when (select
avg(case when grade = 'A' then 4
when grade = 'B' then 3
when grade = 'C' then 2
when grade = 'F' then 1)
from student where id = 'test') = 4 then 'A'
Use another CASE expression on the first one:
SELECT
CASE WHEN num_avg <= 1.0 THEN 'F'
WHEN num_avg <= 2.0 THEN 'C'
WHEN num_avg <= 3.0 THEN 'B'
ELSE 'A' END AS avg_letter_grade
FROM
(
SELECT AVG(CASE grade WHEN 'A' THEN 4.0
WHEN 'B' THEN 3.0
WHEN 'C' THEN 2.0
WHEN 'F' THEN 1.0 END) AS num_avg
FROM student
WHERE id = 'test'
) t;
The letter grade assignments I used in the outer query are not in agreement with what my understanding of typical grade scales are, but it is in line with your scale, so I chose to use it.

Error in using Case Expression in select statement while we used join in statements

This code doesn't work and showing error "From keyword not found where expected"
select m.marks,
CASE
WHEN m.marks<65 then 'F'
WHEN m.marks>65 then 'P'
END m.grade
from student s INNER JOIN marks m ON s.id=m.id;
But this works
select marks,
CASE
WHEN marks<65 then 'F'
WHEN marks>65 then 'P'
END grade
from marks
The m is an alias for the marks table, and you use that when referencing columns in that table. You are applying it to a column alias:
END m.grade
where it does not belong, so - in that place only - remove the m.:
select m.marks,
CASE
WHEN m.marks<65 then 'F'
WHEN m.marks>65 then 'P'
END grade
from student s INNER JOIN marks m ON s.id=m.id;
As #Gordon pointed out in a comment, you are checking >65 and <65, so a student with exactly 65 will not get either F or P - the grade for them will be null. You probably want:
WHEN m.marks < 65 then 'F'
WHEN m.marks >= 65 then 'P'
or
WHEN m.marks >= 65 then 'P'
ELSE 'F'
... though if marks is null the second version will treat that as F too, while the first will still return null.
You cant use a column value as column Alias
Select 1 AS Alias_1,
CASE
WHEN 1=1 THEN 1
WHEN 2=2 THEN 2
END AS Alias_2_AnyName
from dual d;
If you use
Select 1 AS Alias_1,
CASE
WHEN 1=1 THEN 1
WHEN 2=2 THEN 2
END AS d.value
from dual d;
Oracle is expecting an Alias Name after your case block end, but instead you are adding another column value(As key could be omitted)
m.grade is the issue, it should be just grade as it is an alias
select m.marks,
CASE
WHEN m.marks<65 then 'F'
WHEN m.marks>65 then 'P'
END grade
from student s INNER JOIN marks m ON s.id=m.id;

Order oracle query result in sequence as of OR clauses

I have an oracle table with structure something like:
School {
Student_Id,
Student_Name,
Class,
Sport,
Club
}
I want to write a query to fetch all students who either belong to X class, or Y Sport, or Z club.
But I want to order the result based on the sequence of my OR conditions.
That is all students belonging to X class will come first, before the students of Y sport. Then students of Z club will come.
Also, no duplicate results. That is if John is from class X & also belongs to sport Y, then he should only be only appear once and on top of all results of sports Y.
This is how I understood the question:
SQL> with school (student_name, class, sport, club) as
2 (select 'Scott', 'x', 'a', 'c' from dual union all
3 select 'Mike' , 'b', 'c', 'z' from dual union all
4 select 'Kate' , 'x', 'y', 'z' from dual union all
5 select 'John' , 'x', 'b', 'd' from dual union all
6 select 'Vito' , 'd', 'e', 'g' from dual
7 )
8 select * from school
9 where class = 'x' or sport = 'y' or club = 'z'
10 order by case when class = 'x' then 1 end,
11 case when sport = 'y' then 2 end,
12 case when club = 'z' then 3 end;
STUDENT CLASS SPORT CLUB
------- ----- ----- -----
Kate x y z
Scott x a c
John x b d
Mike b c z
SQL>
If that's not it, please, post sample data and expected result.
I would write this as:
select s.*
from school s
where class = 'X' or sport = 'Y' or club = 'Z'
order by (case when class = 'X' then 1
when sport = 'Y' then 2
when club = 'Z' then 3
end)
If you don't want to repeat the conditions, you can use a subquery, CTE, or -- in Oracle 12C -- a lateral join:
select s.*
from school s cross join lateral
(select (case when class = 'X' then 1
when sport = 'Y' then 2
when club = 'Z' then 3
end) as ord
from dual
) x
where x.ord is not null
order by x.ord
select student_id
, case when class='X' then 1
when sport = 'Y' then 2
when club='Z' then 3
else 4
end as Ordr
from School
WHERE class='X' or sport = 'Y' or club='Z'
Order by ordr
Explanation:
The WHERE class='X' or sport = 'Y' or club='Z' simply implements your desired filtering.
The custom ordering is implemented by creating an additional column called ordr and using it in the ORDER BY clause. This column is created using a case statement. The order of the how this is written is important because a CASE expression evaluates to the first true condition and, if there is no true condition, it evaluates to the ELSE part.
So all students taking class X will get an ordr of 1, regardless of their sport and club.
If a student does not take class X, the expression will try to evaluate if the student takes sport Y and if that is the case, that student will get assigned an ordr of 2 regardless other column values.
Then if the student does not take either class X nor sport Y, the case expression will check if the student is in club Z. If that is true he will be asigned an ordr of 3.
Lastly, if a student is neither in class X, does sport Y or is in club Z, he wil get assigned an ordr of 4.
ORDER BY is ASCENDING by degfault meaning 1 will show up before 2 and so on.
What this algorithm does not do, which you can tell by the above explanation, is prioritize a student that takes class X, sport Y, club Z over someone that only takes class X.

How to combine values in table

The database has the schema students(name TEXT, score INTEGER), and there is a table called grades:
Grade MIN_score MAX_score
A 4 5
B 3 4
C 2 3
I want to select the names of all students and their grades according to the table, and turn A and B to 'pass' in the resulting table.
Below is my partial solution without turning A and B to 'pass' in the resulting table, and I wonder how to achieve that additional function.
SELECT name, grade
FROM students
LEFT JOIN grades
ON grade BETWEEN MIN_score and MAX_score;
Don't use between. You'll get duplicates.
select s.name, s.score,
(case when g.grade in ('A', 'B') then 'Pass' end) as status
from students s join
grades g
on s.score > g.MIN_score and s.score <= MAX_score;
You need to be very careful about the join condition so a score of "4" is not treated as both an "A" and a "B" (as between would do).
You need to use case when expression, e.g.:
select case when grade in ('A', 'B') then 'Pass' else '' end
I believe you query should be something like this:
select name, score, case when grade in ('A', 'B') then 'Pass' else '-' end
from students
join grades on score between MIN_score and MAX_score

Calculate percentage for each value of a column sql

I want to rewrite this sql query so that he shows a record with 0 for the corresponding age range if there are no matches and I want that he counts the percentages for each value of Member instead of the '0' at this moment, can anyone help me how I can achieve this?
SELECT COUNT(Name) * 100 /
(select COUNT(*) from 'cities'
WHERE city= 'Hoeselt' AND Member = '0' ) AS 'perc',
CASE
WHEN age <= 30 THEN '18-30'
WHEN age <= 50 THEN '31-50'
ELSE '50+'
END AS age, COUNT(*) AS n
FROM 'cities'
WHERE city= 'Hoeselt' AND elected='yes' AND Member= '0'
GROUP BY CASE
WHEN age <= 30 THEN '18-30'
WHEN age <= 50 THEN '31-50'
ELSE '50+'
END
Hard to be certain that this will work for you without the DDL.
This is a great tool for helping people give you the best solution.
http://sqlfiddle.com/#!6
;WITH AgeCat AS
(
SELECT MinAge = 18
,MaxAge = 30
,Descr = '18-30' UNION ALL
SELECT 31, 49, '31-49' UNION ALL
SELECT 50, 200, '50+'
)
SELECT DISTINCT
C.Descr
,Perc = COUNT(*) OVER (PARTITION BY 0) / COUNT(*) OVER (PARTITION BY A.Descr) * 100
FROM AgeCat A
JOIN Cities C ON C.Age BETWEEN A.MinAge AND A.MaxAge
WHERE city = 'Hoeselt'
AND elected = 'yes'
AND Member = '0'
My approach to this is to use a CTE to define the age group. Next select all the age groups as a "driver" table, left joining in the cities information. Then, you have the age group even when there are no matches:
with c as (
select c.*,
(CASE WHEN age <= 30 THEN '18-30'
WHEN age <= 50 THEN '31-50'
ELSE '50+'
END) as agegrp
from cities
)
select COUNT(Name) * 100 / (select COUNT(*) from cities WHERE city= 'Hoeselt' AND Member = '0') as perc,
driver.agegrp,
COUNT(*) as n
from (select distinct agegrp from c) as driver left outer join
c
on driver.agegrp = c.agegrp
group by driver.agegrp