SQL Count() not giving desired results - mysql

I am having some difficulties figuring out this SQL statement.
Here is the schema of the table.
studentID |subjectID | attendanceStatus | classDate |
1234567 ... 1 .....
1234567 ... 0
Basically I want to count the attendance percentage based on the studentID and display them in columns like this
studentID | subjectID | attendancePercentage
attendancePercentage is the number of 0s / total entries for that student
Here is what I did and it wasn't giving the desired results.
SELECT studentID, COUNT(attendanceStatus = 0) AS Absent,
COUNT( attendanceStatus = 1) As Present
FROM attendance WHERE studentID = '1234567';
That failed.
I hope that I made sense of what I am trying to achieve.

I think you need use sum instead.
SELECT studentID ,
SUM(CASE WHEN attendanceStatus = 0 THEN 1
ELSE 0
END) AS Absent ,
SUM(CASE WHEN attendanceStatus = 1 THEN 1
ELSE 0
END) AS Present
FROM attendance
WHERE studentID = '1234567'

Related

SQL Sub queries Seat Exchange

I am trying to understand how this subquery works. The questions are as follows
Mary is a teacher in a middle school and she has a table seat storing students' names and their corresponding seat ids.The column id is continuous increment.
Mary wants to change seats for the adjacent students.
SELECT
(CASE
WHEN MOD(id, 2) != 0 AND counts != id THEN id + 1
WHEN MOD(id, 2) != 0 AND counts = id THEN id
ELSE id - 1
END) AS id,
student
FROM
seat,
(SELECT
COUNT(*) AS counts
FROM
seat) AS seat_counts
ORDER BY id ASC;
I am trying to understand the how the above query works. So in the CASE it checks if the id is odd or even and checks against the count to see if it is the last element. But how does the ORDER BY ASC work? Because for the first time it selects student Dorris and id 2. but then how is id 2 assigned to Abbot. Thanks.
SQL Table
id | student
1 | Abbot
2 | Doris
3 | Emerson
4 | Green
5 | Jeames
The Result will look like
id | student
1 | Dorris
2 | Abbot
3 | Green
4 | Emerson
5 | Jeames
OK what this is doing is the following -- if an id number is odd and it is not the max number then add one to it, otherwise subtract one from it.
It should be clear that would swap all but the last pair.
I think it is badly written I would write it like this:
WITH student_count(max) as
(
SELECT COUNT(*) FROM seat
)
SELECT
CASE
WHEN student_count.max != id AND MOD(id, 2) != 0 THEN id + 1
WHEN student_count.max != id AND MOD(id, 2) = 0 THEN id - 1
ELSE id
END AS id,
student
FROM seat
CROSS JOIN student_count
ORDER BY id ASC;
I would recommend you to check the results by removing ORDER BY statement. When you remove ORDER BY statement, result will be:
2 Abbot
1 Doris
4 Emerson
3 Green
5 Jeames
Which is completely right for your case. Basically, your query just alters id's values based on the CASE statement. When you add ORDER BY id ASC statement it just orders the result above.
select name,
case when mod(seat_id,2) = 1 and seat_id <> (select max(seat_id) from students) then seat_id + 1
when mod(seat_id,2)= 0 then seat_id - 1
when mod(seat_id,2) = 1 and seat_id = (select max(seat_id) from students) then seat_id
end swap
from students
SELECT
(CASE
WHEN MOD(id, 2) != 0 AND counts != id THEN id + 1
WHEN MOD(id, 2) != 0 AND counts = id THEN id
ELSE id - 1
END) AS id,
student
FROM
seat,
(SELECT
COUNT(*) AS counts
FROM
seat) AS seat_counts
ORDER BY id ASC;

MySQL return min value but not null

I have a table where there are columns students and grade obtained(A-F). A student can appear for test more than once. Sometimes students register but do not appear for test so the grade is not entered but student record entry is made.
I want to get best grade of each student. When I do min(grade) if there is any record with null, null gets selected instead of 'A-F' which indicate proper results. I want to get min of grade if grade exists or null if there are no grades.
SELECT `name`,min(grade) FROM `scores` group by `name`
Id | Name | Grade
1 | 1 | B
2 | 1 |
3 | 1 | A
4 | 2 | C
5 | 2 | D
For name 1 it is fetching second record not the third one having 'A'.
As per the conversations in the comments, the easiest solution may be to convert your empty strings to null, and let the builtin min function do the heavy lifting:
ALTER TABLE scores MODIFY grade VARCHAR(1) NULL;
UPDATE scores
SET grade = null
WHERE grade = '';
SELECT name, MIN(grade)
FROM scores
GROUP BY name
If this is not possible, a dirty trick you could use is to have a case expression convert the empty string to a something you know will come after F:
SELECT name,
MIN(CASE grade WHEN '' THEN 'DID NOT PARTICIPATE' ELSE grade END)
FROM scores
GROUP BY name
And if you really need the empty string back, you can have another case expression around the min:
SELECT name, CASE best_grade WHEN 'HHH' THEN '' ELSE best_grade END
FROM (SELECT name,
MIN(CASE grade WHEN '' THEN 'HHH' ELSE grade END) AS
best_grade
FROM scores
GROUP BY name) t
Change your query slightly to -
SELECT `name`,min(grade) FROM `scores` WHERE grade <> "" group by `name`
If the name has a grade/s assigned to it then the lowest will be returned else the resultset will be null

Joining Tables and Pivoting Data with MySQL

Objective 1:
Your sales data is stored in the Purchases table.
Your sales staff wants to see the sales data in a pivoted form, broken down by quarter.
If your Purchases table doesn't have sales data, create some. Be sure the data spans four quarters.
Next, write a query to pivot the data as follows:
Album Q1 Q2 Q3 Q4
OK Computer 2 5 3 7
Sea Change 8 6 2 1
Do not create a separate table or a view. Do not alter any tables.
Save your query as dba1lesson10project1.sql and hand in the project.
This is What I need to do. But, the table it wants me to work with looks like this. And it states in the assignment I cannot alter it at all.
CustomerID DateOfPurchase SongID
1 2007-03-31 3
3 2007-06-30 4
4 2007-09-30 4
5 2007-12-31 5
I know I need to join three tables together so I can group by the title. Which are my Songs, Albums, and Purchases tables.
SELECT Albums.Title FROM Albums
LEFT JOIN Songs
INNER JOIN Purchases
ON Songs.SongID = Purchases.SongID
ON Albums.Title = Purchases.SongID,
SELECT Title,
SUM(CASE WHEN QUARTER(DateOfPurchase) = 1 THEN 1 ELSE 0 END) AS 'Q1',
SUM(CASE WHEN QUARTER(DateOfPurchase) = 2 THEN 1 ELSE 0 END) AS 'Q2',
SUM(CASE WHEN QUARTER(DateOfPurchase) = 3 THEN 1 ELSE 0 END) AS 'Q3',
SUM(CASE WHEN QUARTER(DateOfPurchase) = 4 THEN 1 ELSE 0 END) AS 'Q4'
From Purchases
GROUP BY Title;
I'm kind of at a loss here when it comes to Joining three separate tables then pivoting the data
I've tried the code above in multiple other variants which has failed me past the table joining portion.
Any help would be much appreciated.
My suggestion before attempting to PIVOT the data would be to first, write the query to return the columns that you need, this will involve joining your tables. You didn't provide your table definitions so I am making a few guesses on the structure. If your tables are structured similar to the following:
CREATE TABLE Purchases
(`CustomerID` int, `DateOfPurchase` datetime, `SongID` int)
;
CREATE TABLE Albums
(`AlbumId` int, `Title` varchar(11))
;
CREATE TABLE Songs
(`SongID` int, `AlbumID` int)
;
Then you would SELECT from the tables using a JOIN similar to this code:
select a.title,
p.DateOfPurchase
from albums a
inner join songs s
on a.albumid = s.albumId
inner join purchases p
on s.songid = p.songid
This query will return to you the album Title as well as the DateOfPurchase which you need to pivot the data. Once you have the JOINS worked out, then you can replace the p.DateOfPurchase with your aggregate function and CASE expression and add the GROUP BY clause to get the final result:
select a.title,
SUM(CASE WHEN Quarter(p.DateOfPurchase) = 1 THEN 1 ElSE 0 END) AS Q1,
SUM(CASE WHEN Quarter(p.DateOfPurchase) = 2 THEN 1 ELSE 0 END) AS Q2,
SUM(CASE WHEN Quarter(p.DateOfPurchase) = 3 THEN 1 ELSE 0 END) AS Q3,
SUM(CASE WHEN Quarter(p.DateOfPurchase) = 4 THEN 1 ELSE 0 END) AS Q4
from albums a
inner join songs s
on a.albumid = s.albumId
inner join purchases p
on s.songid = p.songid
group by a.title;
See SQL Fiddle with Demo. This gives a result:
| TITLE | Q1 | Q2 | Q3 | Q4 |
| OK Computer | 1 | 0 | 0 | 0 |
| Sea Change | 0 | 1 | 1 | 0 |

mysql: for a single column count number of rows for occurrence of specific values

I am writing a report using MySQL query. It has multiple columns that I report. Within that, there is one column containing many different values (only one value pre row). There are two specific values in that column that I need to count. The final results are then grouped by type:
Table
ID Name has passed Type
1 abc yes (1) z
2 xyz yes (1) x
3 cde no (0) y
4 abc yes (1) z
5 cde no (0) z
6 xyz no (0) y
My expected result is:
For Type x
total records = 1
yes count = 1
total abc = 0
total cde = 0
For Type y
total records = 2
yes count = 0
total abc = 0
total cde = 1
For Type z
total records = 3
yes count = 2
total abc = 2
total cde = 1
Notice that we don't count name xyz or any other name.
SELECT
type,
COUNT(*) AS total_records,
COUNT(IF(has_passed, 1, NULL)) AS yes_count,
COUNT(IF(name = 'abc', 1, NULL)) AS total_abc,
COUNT(IF(name = 'cde', 1, NULL)) AS total_cde
FROM
table
GROUP BY
type
Off the top of my head (don't have access to a database for testing right now, sorry)... Maybe something like this would work (assuming you're storing 0 and 1 for "passed")?
SELECT
type,
SUM(passed) as "Yes count",
SUM(case when name = 'abc' then 1 else 0 end) as "Total abc",
SUM(case when name = 'cde' then 1 else 0 end) as "Total cde",
COUNT(1) as "Total records"
FROM
myTable
GROUP BY
type
;

sql query help on multiple count columns and group by

i have the following table Students:
id | status | school | name
----------------------------
0 | fail | skool1 | dan
1 | fail | skool1 | steve
2 | pass | skool2 | joe
3 | fail | skool2 | aaron
i want a result that gives me
school | fail | pass
---------------------
skool1 | 2 | 0
skool2 | 1 | 1
I have this but it's slow,
SELECT s.school, (
SELECT COUNT( * )
FROM school
WHERE name = s.name
AND status = 'fail'
) AS fail, (
SELECT COUNT( * )
FROM school
WHERE name = s.name
AND status = 'pass'
) AS pass,
FROM Students s
GROUP BY s.school
suggestions?
Something like this should work:
SELECT
school,
SUM(CASE WHEN status = 'fail' THEN 1 ELSE 0 END) as [fail],
SUM(CASE WHEN status = 'pass' THEN 1 ELSE 0 END) as [pass]
FROM Students
GROUP BY school
ORDER BY school
EDIT
Almost forgot, but you could also write the query this way:
SELECT
school,
COUNT(CASE WHEN status = 'fail' THEN 1 END) as [fail],
COUNT(CASE WHEN status = 'pass' THEN 1 END) as [pass]
FROM Students
GROUP BY school
ORDER BY school
I'm not sure if there's any performance benefit with second query. My guess would be if there is it's probably very small. I tend to use the first query because I think it's more clear but both should work. Also, I don't have a MySql instance handy to test with, but according to #Johan the ORDER BY clauses are unnecessary.
SELECT q.school, q.fail, q.failpass-q.fail as pass
FROM
(
SELECT s.school, sum(if(status = 'fail',1,0)) as fail, count(*) as failpass
FROM students s
GROUP BY s.school
) q
This way you save one conditional sum.
In MySQL a GROUP BY already orders the results, so a separate ORDER BY is not needed.