SQL Counting A Column With Another Column - sql

My directions are: "Print an ordered list of instructors and the number of A's they assigned to the students. Order the output by number of A's (lowest to highest) and ignore instructors with no A's assigned"
This is what I have (http://pastebin.com/pKwc6cSK):
Use Student_Course59
SELECT INSTRUCTOR
FROM Section RIGHT OUTER JOIN Grade_report
ON Section.SECTION_ID = Grade_report.SECTION_ID
WHERE GRADE IS NOT NULL
AND INSTRUCTOR IS NOT NULL
Here are my Tables:
I cant figure out how to count how many A's a specific instructor has assigned.
Thanks so much!!

I think you should use this query to find a specific instructor has assigned.
SELECT INSTRUCTOR FROM Section
RIGHT OUTER JOIN Grade_report
ON Section.SECTION_ID = Grade_report.SECTION_ID
WHERE Grade_report.GRADE IS NOT NULL AND
Section.INSTRUCTOR IS NOT NULL

From the screen shot it looks like you're running a version of SQL Server. If this is SQL Server 2005 or higher you can now use SUM as a "window function" which means it can take the OVER (PARTITION BY) meaning something like this might work:
Note the syntax is taken from your example, but if you'd provided a fiddler example, it would have worked better :)
SELECT DISTINCT INSTRUCTOR, SUM(CASE WHEN Grade = 'A' THEN 1 ELSE 0 END) OVER (PARTITION BY INSTRUCTOR) AS SUM
FROM Section
LEFT JOIN Grade_report ON Section.SECTION_ID = Grade_report.SECTION_ID
WHERE INSTRUCTOR IS NOT NULL
Note that I do not also know how your grade is represented so the value for 'A' might be different of course.
What it is doing is selecting all instructors and their grades. When the grade is 'A' it'll assign 1 otherwise 0 and then it'll SUM them up "partitioned by" (essentially a group by) instructor
You can solve a lot of problems using the OVER (PARTITION BY) in SQL Server so even if my syntax is incorrect for your case, try checking out the window functions and seeing if they cannot help you onwards.

Related

SQL View only returns one row

I'm trying to create a view that returns the amount of courses each Californian student is enrolled in. There are 4 CA students listed in my 'Students' table, so it should return that many rows.
create or replace view s2018_courses as
select Students.*, COUNT(Current_Schedule.ID) EnrolledCourses
from Students, Current_Schedule
where Students.ID = Current_Schedule.ID AND state='CA';
This query, however, only returns a single row with one student's information, and the total number of courses all CA students are in (in this case 14, since each student is enrolled in 3-5 classes).
I created a view similar to this recently (in a different DB) and it worked well and executed multiple rows, so I'm not sure what is going wrong? Sorry if this is a confusing question, I'm new to SQL and StackOverflow!! Thank you in advance for any advice!
You are missing an aggregation step in your query/view:
CREATE OR REPLACE VIEW s2018_courses AS
SELECT
s.ID, COUNT(cs.ID) EnrolledCourses
FROM Students s
INNER JOIN Current_Schedule cs
ON s.ID = cs.ID
WHERE
state = 'CA'
GROUP BY
s.ID;
The logical problem with your current query is that you are using COUNT(*) without GROUP BY, and MySQL is interpreting this to mean that you want to take the count of the entire table. Note also that I only select ID in my query above, because this is what is being used to aggregate.

How does GROUP BY use COUNT(*)

I have this query which finds the number of properties handled by each staff member along with their branch number:
SELECT s.branchNo, s.staffNo, COUNT(*) AS myCount
FROM Staff s, PropertyForRent p
WHERE s.staffNo=p.staffNo
GROUP BY s.branchNo, s.staffNo
The two relations are:
Staff{staffNo, fName, lName, position, sex, DOB, salary, branchNO}
PropertyToRent{propertyNo, street, city, postcode, type, rooms, rent, ownerNo, staffNo, branchNo}
How does SQL know what COUNT(*) is referring to? Why does it count the number of properties and not (say for example), the number of staff per branch?
This is a bit long for a comment.
COUNT(*) is counting the number of rows in each group. It is not specifically counting any particular column. Instead, what is happening is that the join is producing multiple properties, because the properties are what cause multiple rows for given values of s.branchNo and s.staffNo.
It gets even a little more "confusing" if you include a column name. The following would all typically return the same value:
COUNT(*)
COUNT(s.branchNo)
COUNT(s.staffNo)
COUNT(p.propertyNo)
With a column name, COUNT() determines the number of rows that do not have a NULL value in the column.
And finally, you should learn to use proper, explicit join syntax in your queries. Put join conditions in the on clause, not the where clause:
SELECT s.branchNo, s.staffNo, COUNT(*) AS myCount
FROM Staff s JOIN
PropertyForRent p
ON s.staffNo = p.staffNO
GROUP BY s.branchNo, s.staffNo;
GROUP BY clauses partition your result set. These partitions are all the sql engine needs to know - it simply counts their sizes.
Try your query with only count(*) in the select part.
In particular, COUNT(*) does not produce the number of distinct rows/columns in your result set!
Some people might think that count(*) really count all the columns, however the sql optimizer is smarter than that.
COUNT(*) returns the number of rows in a specified table without getting rid of duplicates. Which mean that you can't use Distinct with count(*)
Count(*) will return the cardinality (elements in table) of the specified mapping.
What you have to remember is that when using count over a specific column, null won't be allowed while count(*) will allow null in the rows as it could be any field.
How does SQL know what COUNT(*) is referring to?
I'm pretty sure, however not 100% sure as I can't find in doc, that the sql optimizer simply do a count on the primary key (not null) instead of trying to handle null in rows.

SQL Maximum number of doctors in a department

my problem is this:
I have a table named
Doctor(id, name, department)
and another table named
department(id, name).
a Doctor is associated with a department (only one department, not more)
I have to do a query returning the department with the maximum number of doctors associated with it.
I am not sure how to proceed, I feel like I need to use a nested query but I just started and I'm really bad at this.
I think it should be something like this, but again I'm not really sure and I can't figure out what to put in the nested query:
SELECT department.id
FROM (SELECT FROM WHERE) , department d, doctor doc
WHERE doc.id = d.id
A common approach to the "Find ABC with the maximum number of XYZs" problem in SQL is as follows:
Query for a list of ABCs that includes each ABC's count of XYZs
Order the list in descending order according to the count of XYZs
Limit the result to a single item; that would be the top item that you need.
In your case, you can do it like this (I am assuming MySQL syntax for taking the top row):
SELECT *
FROM department dp
ORDER BY (SELECT COUNT(*) FROM doctor d WHERE d.department_id=dp.id) DESC
LIMIT 1
You can use Group BY
Select top (1) department.id,count(Doctor.*) as numberofDocs
from department inner join Doctor on Doctor.id = department.id
Group by department.id
Order by count(Doctor.*) desc
I generally avoid using sub queries in MySQL due to a well known bug in MySQL. Due to the bug, MySQL executes the inner query for every single outer query result. Therefore, if you have 10 departments, then doctor query would be executed 10 times. The bug may have been fixed in MySQL 5.6. In this particular case the number of departments may not be large, therefore performance may not be your main concern. However, the following solution should work for MySQL and much more optimized. The answer by dasblinkenlight is almost the same, just got ahead of me :). But MySQL does not support the command top.
select dep.id, dep.name, count(doc.id) as dep_group_count from Doctor doc join department dep on doc.department = dep.id group by doc.department order by dep_group_count DESC LIMIT 1

Returning only one result per student using COUNT()

First off, here's my database schema for the relevant tables:
table: students
record_number | forename | surname
table: applications
id | record_number | job_id | status_id
The record_number field in the applications table is a foreign key to the students table. I'm trying to write some SQL to generate a bar chart in APEX which shows the number of students who have an accepted internship and those who do not.
If a student has an accepted application, the status_id field is 7. Anything else and they do not have an accepted application. Here's what I've got so far:
SELECT NULL AS link, 'Placed' AS label, COUNT(*) AS value
FROM tbl_students students
JOIN tbl_applications applications USING (record_number)
WHERE applications.status_id = 7
UNION
SELECT NULL AS link, 'Unplaced' AS label, CASE COUNT(*) AS value WHEN value > 1 THEN 1 END
FROM tbl_students students
JOIN tbl_applications applications USING (record_number)
WHERE applications.status_id != 7
The problem is, a student can have many internships, and only one of them can be accepted at once. This is fine for the first query above. However, a student can have many unaccepted internships, and the second query counts them all instead of counting just once (to show that +1 student doesn't have an unaccepted internship).
How can I adapt the query to only count students without an accepted internhip once rather than counting them to however many unnacepted internships they have? I tried with the CASE statement but it's throwing a syntax error.
Thanks.
It sounds like you would want the second query to be something like this
SELECT NULL AS link, 'Unplaced' AS label, COUNT(DISTINCT students.record_number) AS value
FROM tbl_students students
JOIN tbl_applications applications USING (record_number)
WHERE applications.status_id != 7
This will count the number of distinct students.record_number values. Since that is the primary key of the students table, I would expect that to give you the number of students that have at least one unaccepted internship.
If that query does not produce the results you are looking for, it would be helpful if you could post some sample data and the expected output.

Counting number of occurrences in subquery

My task is to find the number of occurrences of late timesheet submissions for each employee in our database. There are two tables which I have primarily been looking at, but I'm having trouble putting the two together and coming up with a decent view of the COUNT of occurrences and the employee ID for which they are associated with.
I have created this query which provides me with the EmployeeID for each occurrence.
SELECT db.Employee.EmployeeID
FROM db.LateTimesheets
INNER JOIN db.Employee ON Employee.LastName = LateTimesheets.LastName AND Employee.FirstName = Late Timesheets.FirstName
Now, with this simple query I have a view of the EmployeeID repeated however many times these incidents have occured. However, what I ultimately want to end up with is a table that displays a count for each occurance, along with the EmployeeID for which this count is associated with.
I would assume I would need to use the COUNT() function to count the amount of rows for each EmployeeID, and then select that value along with EmployeeID. However, I am having trouble structuring the subquery correctly, and everything I have tried thus far has only generated errors with MS SQL Server Management Studio.
A simpler version of usr's answer would be the following which avoids the construction of the derived table:
Select db.Employee.EmployeeID, Count( db.LateTimesheets.somecolumn ) As Total
From db.Employee
Left Join db.LateTimesheets
On LateTimesheets.LastName = Employee.LastName
And Late Timesheets.FirstName = Employee.FirstName
Group By db.Employee.EmployeeID
I may have misunderstood the question, but wouldn't GROUP BY solve your problem?
SELECT COUNT(db.LateTimesheets.somecolumn), db.Employee.EmployeeID
FROM db.LateTimesheets
INNER JOIN db.Employee ON Employee.LastName = LateTimesheets.LastName
AND Employee.FirstName = Late Timesheets.FirstName
GROUP BY db.Employee.EmployeeID
Just replace somecolumn with the name of a column that's actually in the table.
select e.*, isnull(lt.Count, 0) as Count
from Employee e
left join (
select LastName, count(*) as Count from LateTimesheets
) LateTimesheets lt on e.LastName = lt.LastName
The trick is to do the grouping in a derived table. You don't want to group everything, just the LateTimesheets.
We need a left join to still get employees with no LateTimesheets.