SQL Server : resultset zero totals for values that don't exist - sql

SQL Fiddle
I'm trying to force through zero values for grades that don't exist in my Results table, but do exist in my list of possible Grades table.
I've managed to join tables successfully in order to almost achieve this using this previous post as guidance up to a point.
As you can see from my fiddle the resultset is displaying NULL values for my Year and Subject columns and I would like these to display the relevant subject.

Don't use * in SELECT instead use specify the colums you need and use ISNULL and make it as zero like:
SELECT ISNULL(t.amount,0)
FROM [yourtable] y
left join [someOtherTable] t
ON y.id=t.id

What you need is a table of Subjects and Years, then cross join for grades...
create table Subjects(SubjectID INT, Subject Varchar(50));
create table YearSub (SubjectID INT, Year INT, CrateDate Date); -- Map your available subjects for each year in here
Once you have thiese, make a CTE to hold the full list of available subjects, years and grades
with AllGrades as
(select SubjectID, Year, Grade
from YearSub YS
cross join Grades
)
select ...
from AllGrades
left join ...
And so on

i think this is what you wanted.
first you get all combinition of student & grades by using CROSS JOIN
then you LEFT JOIN to the resutl table to the get the count
select s.year, subject grade, grade, count(wag) as Total
from student s
cross join grades g
left join results r on s.upn = r.upn and s.upn = r.upn
group by s.year, g.grade

Related

outer join with not in and for each in sql

This one has only one table with three columns student, lect and score. for each lecture I need to find the students who have not got any score.
I have written the below query which uses outer joins, but it can do so only for one lect at a time.
Eg: see below I passed 'L02'
How do I get this working for all the lect values as in (L01,L02,L03...etc)
select distinct * from
(
select distinct Student from import1
where lect ='L02'
)i1
right outer join
(select distinct Student from import1) i2
on i1.Student=i2.Student
where i1.Student is null
output of above is
This works for L02. But, how do I modify above to include for all lect values without hardcoding the values of lect?
Sample data from table:
Need a dataset of all possible student/lecture pairs. If the one table contains all the students and lectures that need to be considered, this dataset can be built with:
SELECT Student, Lect FROM (SELECT DISTINCT Student FROM import1) AS S, (SELECT DISTINCT Lect FROM import1) AS L
Otherwise, need a table of all students and a table of all lectures then query:
SELECT Student, Lect FROM Students, Lectures
Now join that query to the scores table with compound link on both identifier fields and use appropriate filter criteria.
SELECT Query1.Student, Query1.Lect, import1.Score
FROM Query1 LEFT JOIN import1
ON (Query1.Lect = import1.Lect) AND (Query1.Student = import1.Student)
WHERE Score Is Null;
Tested with an Access database.

Subtracting values of columns from two different tables

I would like to take values from one table column and subtract those values from another column from another table.
I was able to achieve this by joining those tables and then subtracting both columns from each other.
Data from first table:
SELECT max_participants FROM courses ORDER BY id;
Data from second table:
SELECT COUNT(id) FROM participations GROUP BY course_id ORDER BY course_id;
Here is some code:
SELECT max_participants - participations AS free_places FROM
(
SELECT max_participants, COUNT(participations.id) AS participations
FROM courses
INNER JOIN participations ON participations.course_id = courses.id
GROUP BY courses.max_participants, participations.course_id
ORDER BY participations.course_id
) AS course_places;
In general, it works, but I was wondering, if there is some way to make it simplier or maybe my approach isn't correct and this code will not work in some conditions? Maybe it needs to be optimized.
I've read some information about not to rely on natural order of result set in databases and that information made my doubts to appear.
If you want the values per course, I would recommend:
SELECT c.id, (c.max_participants - COUNT(p.id)) AS free_places
FROM courses c LEFT JOIN
participations p
ON p.course_id = c.id
GROUP BY c.id, c.max_participants
ORDER BY 1;
Note the LEFT JOIN to be sure all courses are included, even those with no participants.
The overall number is a little tricker. One method is to use the above as a subquery. Alternatively, you can pre-aggregate each table:
select c.max_participants - p.num_participants
from (select sum(max_participants) as max_participants from courses) c cross join
(select count(*) as num_participants from participants from participations) p;

sql server - how to modify values in a query statement?

I have a statement like this:
select lastname,firstname,email,floorid
from employee
where locationid=1
and (statusid=1 or statusid=3)
order by floorid,lastname,firstname,email
The problem is the column floorid. The result of this query is showing the id of the floors.
There is this table called floor (has like 30 rows), which has columns id and floornumber. The floorid (in above statement) values match the id of the table floor.
I want the above query to switch the floorid values into the associated values of the floornumber column in the floor table.
Can anyone show me how to do this please?
I am using Microsoft sql server 2008 r2.
I am new to sql and I need a clear and understandable method if possible.
select lastname,
firstname,
email,
floor.floornumber
from employee
inner join floor on floor.id = employee.floorid
where locationid = 1
and (statusid = 1 or statusid = 3)
order by floorid, lastname, firstname, email
You have to do a simple join where you check, if the floorid matches the id of your floor table. Then you use the floornumber of the table floor.
select a.lastname,a.firstname,a.email,b.floornumber
from employee a
join floor b on a.floorid = b.id
where a.locationid=1 and (a.statusid=1 or a.statusid=3)
order by a.floorid,a.lastname,a.firstname,a.email
You need to use a join.
This will join the two tables on a certain field.
This way you can SELECTcolumns from more than one table at the time.
When you join two tables you have to specify on which column you want to join them.
In your example, you'd have to do this:
from employee join floor on employee.floorid = floor.id
Since you are new to SQL you must know a few things. With the other enaswers you have on this question, people use aliases instead of repeating the table name.
from employee a join floor b
means that from now on the table employee will be known as a and the table floor as b. This is really usefull when you have a lot of joins to do.
Now let's say both table have a column name. In your select you have to say from which table you want to pick the column name. If you only write this
SELECT name from Employee a join floor b on a.id = b.id
the compiler won't understand from which table you want to get the column name. You would have to specify it like this :
SELECT Employee.name from Employee a join floor b on a.id = b.id or if you prefer with aliases :
SELECT a.name from Employee a join floor b on a.id = b.id
Finally there are many type of joins.
Inner join ( what you are using because simply typing Join will refer to an inner join.
Left outer join
Right outer join
Self join
...
To should refer to this article about joins to know how to use them correctly.
Hope this helps.

sql count with inner join

I have a table for absentees, and that table am storing the studentids of those who have been absent.
From this table I had to find total presentees and total absentees, for this I just joined the Sections table which contains the maximum capacity of particular Section.
For this my query was
select COUNT(Attendance.studentid) as Absentees
,Sections.Max-count(studentid) as Presentees
from Attendance
inner join Students
on students.StudentId=Attendance.StudentId
inner join Sections
on Sections.CourseId=students.CourseId
group by Sections.Max
Its working fine, the same way how can I find the gender wise presentees/absentees......gender column is in Students table, can anyone give me some idea, thanks in advance
Just add the gender column to your select ... columns and the group by, you'll end up with one row for each gender:
select COUNT(Attendance.studentid) as Absentees,
Sections.Max-count(studentid) as Presentees,
Students.Gender as Gender
from Attendance
inner join Students
on Students.StudentId=Attendance.StudentId
inner join Sections
on Sections.CourseId=Students.CourseId
group by Sections.Max, Students.Gender

SQL Query Help - Return row in table which relates to another table row with max(column)

I have two tables:
Table1 = Schools
Columns: id(PK), state(nvchar(100)), schoolname
Table2 = Grades
Columns: id(PK), id_schools(FK), Year, Reading, Writing...
I would like to develop a query to find the schoolname which has the highest grade for Reading.
So far I have the following and need help to fill in the blanks:
SELECT Schools.schoolname, Grades.Reading
FROM Schools, Grades
WHERE Schools.id = (* need id_schools for max(Grades.Reading)*)
SELECT
Schools.schoolname,
Grades.Reading
FROM
Schools INNER JOIN Grades on Schools.id = Grades.id_schools
WHERE
Grades.Reading = (SELECT MAX(Reading) from Grades)
Here's how I solve this sort of problem without using a subquery:
SELECT s.*
FROM Schools AS s
JOIN Grades AS g1 ON g1.id_schools = s.id
LEFT OUTER JOIN Grades AS g2 ON g2.id_schools <> s.id
AND g1.Reading < g2.Reading
WHERE g2.id_schools IS NULL
Note that you can get more than one row back, if more than one school ties for highest Reading score. In that case, you need to decide how to resolve the tie and build that into the LEFT OUTER JOIN condition.
Re your comment: The left outer join looks for a row with a higher grade for the same school, and if none is found, all of g2.* columns will be null. In that case, we know that no grade is higher than the grade in the row g1 points to, which means g1 is the highest grade for that school. It can also be written this way, which is logically the same but might be easier to understand:
SELECT s.*
FROM Schools AS s
JOIN Grades AS g1 ON g1.id_schools = s.id
WHERE NOT EXISTS (
SELECT * FROM Grades g2
WHERE g2.id_schools <> s.id AND g2.Reading > g1.Reading)
You say it's not working. Can you be more specific? What is the answer you expect, and what's actually happening, and how do they differ?
edit: Changed = to <> as per suggestion in comment by #potatopeelings. Thanks!
This should do it
select * from Schools as s
where s.id=(
select top(1) id_schools from grades as g
order by g.reading desc)