MySQL Attendance System - mysql

I have made an attendance system for a project, However I'm stuck right now by trying to create a query.
SELECT
COUNT(Students.idStudents) total,
SUM(case when Attendance.status LIKE 'present' then 1 else 0 end) present,
SUM(case when Attendance.status LIKE 'late' then 1 else 0 end) late,
SUM(case when Attendance.status is null then 1 else 0 end) absents
FROM Students, Schools, Tags LEFT JOIN Attendance
ON Attendance.tagCode = Tags.tagCode
WHERE Schools.idSchools = Students.idSchools
AND Tags.idStudents = Students.idStudents
This code works and generates an attendance. However this will show all the dates.
When I add in another line to specify date
AND Attendance.date = DATE(NOW());
It will not show anything..
There's 'Present', 'Late' status for the attendance however if the student's record in that table doesn't exist, it is considered as absent.
How do I do that?

Assuming you're using a case insensitive collation, your purported solution can be rewritten as follows:
SELECT COUNT(p.idStudents) total
, SUM(CASE WHEN a.status = 'present' THEN 1 ELSE 0 END) present -- [or just SUM(a.status = 'present')]
, SUM(CASE WHEN a.status = 'late' THEN 1 ELSE 0 END) late
, SUM(CASE WHEN a.status IS NULL THEN 1 ELSE 0 END) absents
FROM Students p
JOIN Schools s
ON s.idSchools = p.idSchools
JOIN Tags t
ON t.idStudents = p.idStudents
LEFT
JOIN Attendance a
ON a.tagCode = t.tagCode
AND a.date = CURDATE() ;
For next time: Your ERD shows 10 tables, but only 4 feature in this problem. If a table isn't likely to be part of the proposed solution, don't show it. Don't provide pictures. Instead, where possible, provide proper DDLs (and/or an sqlfiddle), TOGETHER WITH THE DESIRED RESULT SET based upon a minimal, but properly representative data set.
Welcome to SO.

Figured it out..
SELECT
COUNT(Students.idStudents) total,
SUM(case when Attendance.status LIKE 'present' then 1 else 0 end) present,
SUM(case when Attendance.status LIKE 'late' then 1 else 0 end) late,
SUM(case when Attendance.status is null then 1 else 0 end) absents
FROM Students, Schools, Tags LEFT JOIN (SELECT * FROM Attendance WHERE Attendance.date = NOW()) AS Attendance
ON Attendance.tagCode = Tags.tagCode
WHERE Schools.idSchools = Students.idSchools
AND Tags.idStudents = Students.idStudents

The problem with the original query was you were asking for students where their attendance was "NOW" where no student was ever now but they are current attending classes. They may have signed in at 8am and you run the query at 10am. You'd need to manipulate the datetime to choose your start time and end time based on the current day.
timestampadd(HOUR, 08, CURDATE()) - this will give you 8am, you'd then query for when attendance.date is greater than or equal to 8am and then potentially less than or equal to 4pm?

Related

Mysql Query: How many sum() recommended in single query?

I have 70 different types of accounts. And I am fetching the data as per the account type.
The query like this,
$mainData = "SELECT
count(*) AS totalRows,
sum(pay) as totalPay
sum(case when account_type = 1 then 1 else 0 end) AS account_1_Total,
sum(case when account_type = 1 then pay else 0 end) AS account_1_Pay,
sum(case when account_type = 2 then 1 else 0 end) AS account_2_Total,
sum(case when account_type = 2 then pay else 0 end) AS account_2_Pay,
{all_account_types_here}
FROM account_table";
In the end, those sum() are about more than 140.
So the question is, how many sum() is recommended in a single query?
Thanks!
EDITED:
The GROUP BY is the solution of it.

MySQL subtract from SUM in CASE statement

I have an SQL code.
SELECT DISTINCT SQL_CALC_FOUND_ROWS
sales.code as code,
SUM(CASE WHEN sales.type = 51 THEN sales.amount ELSE 0 END) AS amount,
sales.type as type
FROM sales
Now the thing is that I need to subtract the sales.amount from the total SUM IF the sales.type != 51 AND sales.amount > 0. How could I do it? I guess I need another CASE statement inside the first one after ELSE statement, but I don't know how this works.
As example if the type is not 51 and it has sales.amount bigger than 0, then I should subtract it from the amount.
Thanks.
Where is this select distinct coming from? Use GROUP BY:
SELECT s.code as code,
SUM(CASE WHEN s.type = 51 THEN s.amount ELSE 0 END) AS amount
FROM sales s
GROUP BY s.code;
Then the answer to your question is to change the ELSE clause:
SELECT s.code as code,
SUM(CASE WHEN s.type = 51 THEN s.amount ELSE - s.amount
END) AS amount
FROM sales s
GROUP BY s.code;

Pivoting Data Using 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 have tried
SELECT SongID,
SUM(CASE WHEN DateOfPurchase = '2007-03-31' THEN DateOfPurchase ElSE 0 END) AS 'Q1',
SUM(CASE WHEN DateOfPurchase = '2007-06-30' THEN DateOfPurchase ELSE 0 END) AS 'Q2',
SUM(CASE WHEN DateOfPurchase = '2007-09-30' THEN DateOfPurchase ELSE 0 END) AS 'Q3',
SUM(CASE WHEN DateOfPurchase = '2007-12-31' THEN DateOfPurchase ELSE 0 END) AS 'Q4'
FROM Purchases
GROUP BY SongID;
Along with other variants:
SELECT SongID,
SUM(CASE WHEN DateOfPurchase = '2007-03-31' THEN CustomerID ElSE 0 END) AS 'Q1',
SUM(CASE WHEN DateOfPurchase = '2007-06-30' THEN CustomerID ELSE 0 END) AS 'Q2',
SUM(CASE WHEN DateOfPurchase = '2007-09-30' THEN CustomerID ELSE 0 END) AS 'Q3',
SUM(CASE WHEN DateOfPurchase = '2007-12-31' THEN CustomerID ELSE 0 END) AS 'Q4'
FROM Purchases
GROUP BY SongID;
And:
SELECT SongID,
SUM(CASE WHEN DateOfPurchase = '2007-03-31' THEN SongID ElSE 0 END) AS 'Q1',
SUM(CASE WHEN DateOfPurchase = '2007-06-30' THEN SongID ELSE 0 END) AS 'Q2',
SUM(CASE WHEN DateOfPurchase = '2007-09-30' THEN SongID ELSE 0 END) AS 'Q3',
SUM(CASE WHEN DateOfPurchase = '2007-12-31' THEN SongID ELSE 0 END) AS 'Q4'
FROM Purchases
GROUP BY SongID;
Which, the last 2 almost get me what I need. But, there is only one purchase per SongID and Quarter. They show me either the CustomerID or SongID instead. I have a basic understand of what I need to do. But without being able to alter the table to show how many purchases there have actually been I'm not sure what to do. Any suggestions?
Your current query is very close, I would suggest a few minor changes. You are hard-coding the date but what happens if you have a date in quarter one that is not equal to 2007-03-31 it won't show up.
I would use the QUARTER() function in MySQL, this will return the quarter 1-4 based on the date value.
Second, I don't think you want to sum the SongID, in your CASE expression I would replace the SongID with 1 similar to the following:
SELECT SongID,
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 SongID;
See SQL Fiddle with Demo
You can do this quarterly report easily using MySQL Pivot table generator.
While creating your report, you will be asked to add the "Column settings "
Please choose the "Purchasing table" as the "Table", the column that contains the date values as the "Field" . Once selecting a date value, a function menu should appear, please select "Quarter" to get your pivot table divided in quarters as you requested. please note that you have the option to create a report for one specific year if you like and in this case you should check the "Exact Year" Box and add a specific year otherwise leave the "Exact year" box unchecked .
The final report will be able to see something like this report :
http://mysqlpivottable.net/MySQL-Pivot-Table-Demo/tables/Quarterly_Sales_Report/

Optimise SQL query for the report

This is SQL query I wrote, it work fine but it is slow.
SELECT D.Username,
SUM(CASE WHEN D.type = 'Yes' THEN 1 ELSE 0 END) as Yes,
SUM(CASE WHEN D.type = 'No' THEN 1 ELSE 0 END) as No,
SUM(CASE WHEN D.type = '' THEN 1 ELSE 0 END) as Other,
SUM(CASE WHEN S.mobile IS NULL THEN 0 ELSE 1 END) as Sales,
COUNT(*) as TOTAL FROM dairy as D
LEFT JOIN (SELECT DISTINCT mobile FROM sales) as S on D.MobileNo = S.mobile
WHERE source = 'Network' AND UNIX_TIMESTAMP(CheckDate) >= 1309474800 AND UNIX_TIMESTAMP(CheckDate) <= 1309561200
group by D.Username order by TOTAL DESC
As you can see it count number of Yes, No, Other and the matching MobileNo (D.MobileNo = S.mobile) sale.
I have tried adding index to type, username, mobile, MobileNO, CheckDate and source - the performance did not improve much.
Three points to notice in your query:
1. There's a chance the `LEFT JOIN` is giving you performance issues.
However, you need it, since it is possible that there are D.MobileNo values that will not be present in SELECT DISTINCT mobile FROM sales. Any other work around (yes, there are options) will most likely decrease performance. But your performance might be improved by observing the next items.
2. Make sure you have indexes in the key columns:
D.type
S.mobile
D.MobileNo
D.Username
D.Source
D.CheckDate
3. You might be having problems with filtering by `UNIX_TIMESTAMP(CheckDate)`
This might be the key issue. You might be having problems with filtering by UNIX_TIMESTAMP(CheckDate) instead of CheckDate, specially if Dairy has a large amount of records. The problem is that even if you have an index for CheckDate, it will probably not be used because of the function. Try to filter by CheckDate itself.
If this is time critical it can also make sense to store more data.
Here this means that you add INT columns for YesValue, NoValue, OtherValue and fill them with 0 or 1 in your application. By doing this you can remove the case calculations from your SELECT-part.
Also please post all indexes currently available and as the comments say the CREATE-statement for the table.

MySQL multiple count in single query

I have a table named 'sales' with following fields
salesid (type: int)
stime (type: datetime)
status (type: enum, values: remaining OR cancelled OR done)
... and other fields
I need a query that can output as
SalesDate TotalSales TotalDone TotalRemaining TotalCancelled
2010-11-06 10 5 3 2
2010-11-06 15 14 1 0
Anyone know how to achieve this?
Help appreciated, thanks.
You can use conditional summation to get the results you want.
select stime as sales_date
,sum(1) as total_sales
,sum(case when status = 'remaining' then 1 else 0 end) as total_done
,sum(case when status = 'cancelled' then 1 else 0 end) as total_remaining
,sum(case when status = 'done' then 1 else 0 end) as total_cancelled
from sales
group by stime;
You can also use COUNT(expr) and exploit the fact it doesn't include NULLS, but personally I find that the above best conveys the intent of the developer.
Use a CASE statement in your SELECT to create pseudo columns using the status values of remaining, cancelled and done
SELECT
date(stime),
COUNT(salesid),
SUM(CASE status WHEN 'done' THEN 1 ELSE 0 END) as TotalDone,
SUM(CASE status WHEN 'remaining' THEN 1 ELSE 0 END) as TotalRemaining,
SUM(CASE status WHEN 'cancelled' THEN 1 ELSE 0 END) as TotalCancelled
FROM sales
GROUP BY date(stime)
Note: This is what i was trying to refer to. I think this should work but i have no access to MySQL to try out the syntax and verify it. Sorry about that. But this should get you going.