Attendance system using PHP & MySQL - php

I have an Attendance system in one of my module project using PHP and MySQL, the MySQL table looks something like this:
Now, timetable is used to store the Time Table for classes in a day for a section and which teacher is assigned to it. The student_info table contains general information about students and the section they belong to. The attendancetable is used to record those who are absent using time and student id as primary key.
I could get a count of how many classes were taken in a semester as follows:
SELECT count(*) as total FROM timetable WHERE flag > ? AND semester = ? AND section = ? AND timeid BETWEEN ? AND ?
Then computed how many times a student attended and also calculate the percentage of attendance.
SELECT stu.* ,
(SELECT COUNT(*)
FROM attendancetable att
WHERE att.s_id = stu.class_roll
AND att.semester = ?
AND att.timeid BETWEEN ? AND ? ) AS absent
FROM student_info stu
WHERE stu.section = ?
AND stu.logYear = ?
AND stu.deleted = ?
ORDER BY stu.class_roll
Now, I want to also display a kind of attendance sheet as follows:
I tried SQL Inner Join but didn't get the way I wanted.
I was thinking that the first row can be output form the following query:
SELECT timeid
FROM timetable
WHERE flag > ?
AND semester = ?
AND section = ?
AND timeid BETWEEN ? AND ?
[UPDATE] Found a way to do this, given as an answer, don't know if that's the right way.
Thank You in advance for helping.

I am probably a bit late with my answer, but I have been playing around a bit too and came up with a solution that is largely done within MySql: my solution builds a dynamic SELECT statement that is executed at the end to provide me with the table, having columns spanning a predefined era:
SET #str='SELECT sid';
SELECT #str:=concat(#str,', MAX(CASE tid WHEN ',t_id,
' THEN \'OK\' ELSE \'\' END) d',t_id)
-- start date end date
FROM times WHERE dati BETWEEN '20170810.0000' and '20171022.2359';
SET #str=concat(#str,' FROM att GROUP BY sid');
PREPARE qu FROM #str;
EXECUTE qu;
What still needs to be done is the translation of date Ids in the column headings to proper dates and, likewise, the translation of student Ids into names or univ_roll numbers.
i have also taken the liberty of changing the table layout a little bit: the primary key in the timetable is now just an integer, the actual time is stored separately in another column, providing flexibility in case dates or times might change.
Here is a little rextester demo: http://rextester.com/LPFF99061 (the attendance table has only been filled for the first few dates).

I'm not sure if I should edit the question with these updates. But this is a partial solution that I did come out with. Would be glad if someone could help optimize this more.
So, I started with this piece of code.
SELECT timeid
FROM timetable
WHERE flag > ?
AND semester = ?
AND section = ?
AND timeid BETWEEN ? AND ?
From the above code I could get the dates where classes are taken. Next, I check if a student is present or absent on the dates mentioned as follows:
SELECT attended FROM attendancetable
WHERE s_id = ?
AND semester = ?
AND timeid = ?
With this I was able to see if a student is absent or not. In php it look something like this:
//Getting the dates
$query = "SELECT timeid FROM timetable
WHERE flag > ?
AND semester = ?
AND section = ?
AND timeid BETWEEN ? AND ?";
$params = array(0, 1, 'A', '2017-01-01', '2017-06-30' );
$stmt = $db->prepare($query);
$stmt->execute($params);
$dates = $stmt->fetchall();
//checking if students is present or absent
$query = "SELECT attended FROM attendancetable
WHERE s_id = ?
AND semester = ?
AND timeid = ?";
//Now I'm going to loop through the $dates
foreach($dates as $date){
$params = array(1, 'A', $date['timeid']);
$stmt = $db->prepare($query);
$stmt->execute($params);
$result = $stmt->fetchall();
($result) ? $present = 1 : $present = 0;
}
In the above way, I was able to compute if a student of a particular section is present/absent for a given class as per the timetable. Now, if I want for all the students in a section I could first query the student_info table and then compute the foreach loop for each individual student
SELECT class_roll FROM student_info
WHERE logYear = ?
AND section = ?
AND deleted = ?
ORDER BY class_roll LIMIT ?, ?
Each student is then run through the foreach loop to check if they are absent or present. Finally, I could get this:
I tested the execution time locally for about 200 students it came to about 1.6 seconds when the number of classes is only 39. I do test from previous data also for a whole semester and the code exceed 30 seconds and max_execution_time error triggered. As of now, I set a 25 number of students limits per page.
UPDATE: The max_execution_time error seems to appear only on the first run, but later when I try to re-produce its not giving anymore error and the time taken to complete the task is below 2 seconds.

Related

working on hotel reservation system and I get stuck on check avaibility room

there is my db structure:
Rooms:
roomId-roomName-roomCapacity
Reservation:
rsId-rsroomId-date_in-date_out
I want to create a search function for searching available rooms.
I can't get the ones which are available, which are not booked for those days.
I have tried lots of things but till now can't solve this issue.
There is my latest code:
function search ($name, $date_in, $date_out) {
$this->db->select('*')->from('Rooms');
$this->db->join('Reservation', 'rooms.roomid = reservation.rsroomId', 'LEFT');
$this->db->like('date_in', $date_in);
$this->db->like('date_out', $date_out);
$this->db->like('roomName', $name);
return $this->db->get()->result_array();
}
Thank You
I am not very familiar with the CodeIgniter way to build a query, but I understand what you want to achieve. The raw query would look something like this:
SELECT Rooms.*
FROM Rooms
LEFT JOIN Reservation
ON Rooms.roomId = Reservation.rsroomId
AND date_in < '$date_out' AND date_out > '$date_in'
WHERE rsId IS NULL;
To select all overlapping reservations in the JOIN ON condition, you must compare the date_in of existing reservations with the $date_out of the desired reservation, and vice versa. If an existing reservation checks out before you want to check in, it does not overlap. If an existing reservation checks in after you want to check out, it does not overlap.
After carefully crafting these JOIN ON conditions to get the overlapping reservations, the WHERE rsId IS NULL condition ensures that you get only rooms for which there is no such existing overlapping reservation.
That last part is easy in CodeIgniter, but getting all the three JOIN ON conditions into $this->db->join() is going to be messy. The like() and where() functions automatically escape your values, but here you need to add this protection against SQL injection manually. Perhaps something like this:
function search ($date_in, $date_out) {
// Ensuring these values are safe in raw SQL:
$date_in = $this->db->escape($date_in);
$date_out = $this->db->escape($date_out);
$joinCondition = 'rooms.roomid = reservation.rsroomId';
$joinCondition .= " AND date_in < $date_out AND date_out > $date_in";
$this->db->select('*')->from('Rooms');
$this->db->join('Reservation', $joinCondition, 'LEFT');
$this->db->where('rsId', NULL);
return $this->db->get()->result_array();
}
An alternative is to put question marks in the raw query, so CodeIgniter can escape your values via query bindings:
function search ($date_in, $date_out) {
$sql = "SELECT Rooms.*
FROM Rooms
LEFT JOIN Reservation
ON Rooms.roomId = Reservation.rsroomId
AND date_in < ? AND date_out > ?
WHERE rsId IS NULL";
$binds = [$date_out, $date_in];
return $this->db->query($sql, $binds)->result_array();
}

How to merge two UPDATE queries into one, different WHERE and SET?

I was wondering if it's possible to combine these two queries as they do not work separately (one of the two only works). They are
$addquery = "UPDATE winners SET mem_name='$addname' WHERE mem_name='$deletename'";
$addresult= mysqli_query($connect, $addquery);
$query = "UPDATE winners INNER JOIN members SET winners.mem_id = members.mem_id
WHERE winners.mem_name = members.mem_name";
$result = mysqli_query($connect, $query);
Can this be done in just one query? Thank you!!!
I am not saying you should do it, but judging from the flow of the code you provided, this is how you could do it.
UPDATE winners w
SET w.mem_name = '$addname'
, w.mem_id = IFNULL(SELECT m.mem_id
FROM members AS m
WHERE m.mem_name = '$addname'
ORDER BY m.mem_id DESC
LIMIT 1
, w.mem_id
)
WHERE w.mem_name = '$deletename'
;
Note, the ORDER BY is technically optional; your question does not state whether mem_name is guaranteed unique in members. If it is unique, the order by should not be needed; if it is not, it at least adds some consistency to the expected value retrieved.
If you have control over the database design, I would suggest removing mem_name from winners altogether. It is/would be redundant data if you were managing the relation primarily by mem_id to begin with.

Select all rows from one table and one specific value for each row from another table

Good afternoon lads, I am trying to make a page where I can check which bosses I did today. I have two tables (table with bosses and table with boss times). Now I need to show all bosses but for each of them I only want to show the closest time when the boss is going to spawn.
The select so far looks like that:
$timePlus = strtotime($currentTime) + 60*60*2.2;
$timePlusForm = date("H:i:s", $timePlus);
$userNametoLower = strtolower($userName);
$userTableName = "".$userNametoLower."_bosses";
$currentTime = date("H:i:s", time());
"SELECT `bossTime`.`ID`, `bossTime`.`bossID`, `bossTime`.`time`, `$userTableName`.`ID`, `$userTableName`.`name`,
`$userTableName`.`zone`, `$userTableName`.`map`, `$userTableName`.`waypointCode`, `$userTableName`.`bossDone`
FROM `bossTime` LEFT JOIN `$userTableName` ON `$userTableName`.`ID` = `bossTime`.`bossID`
WHERE `bossTime`.`time` BETWEEN '$currentTime' AND '$timePlusForm'
GROUP BY `bossTime`.`bossID`
ORDER BY `bossTime`.`time` ASC";
The problem is that this select does not pick the next closest value from time table. I also tried BETWEEN and it also didn't work (some bosses got correct closest time but other got the second closest). Any idea how to solve this is welcomed.
I removed GROUP BY and changed the condition to WHERE bossTime.time >= '$currentTime' AND bossTime.time <='$timePlusForm' and for some reason it works

Query two tables for different/separate values

I have two tables:
task
id name dueDate completed projectID
project
id name dueDate completed
I need to query both tables for rows with the same data. I tried doing a sample statement just looking for rows with completed=0, but never got any results. I think it has to do with using OR instead of AND, but it's just a little above my level right now...Any ideas?
TO CLARIFY, I'm not looking for duplicate rows, I'm looking for 'all tasks and projects with completed = 0'
The query is:
SELECT * FROM "task" t, "project" p WHERE t.dueDate = "2012-08-17" OR p.dueDate = "2012-08-17" AND t.completed = 0 OR p.completed = 0
I did manage to get one of the answers' code to work, however I realized that my entire app was written to talk to one table, and that it would be much easier to just combine the task and project table and use an isProject column to differentiate projects from tasks. This also adds the ability to nest projects inside of projects, because projects will now have a projectID column as well.
In the end, KISS prevails...
Thanks for all the help! I will mark the answer that worked, even though I won't be using it.
Try using parenthesis.
SELECT * FROM "task" t, "project" p WHERE (t.dueDate = "2012-08-17" OR p.dueDate = "2012-08-17") AND (t.completed = 0 OR p.completed = 0)
If You want only values matches from both tables with completed=0 from dueDate='2012-08-17':
You can use join to bound that tables results into one.
Inner join will return only results which matches on both sides.
So You can use it to match them in both tables by it and then filter for Your wanted value by classic where:
select * from task t inner join project p on t.dueDate = p.dueDate and t.completed = p.completed
where t.dueDate = '2012-08-17' and t.completed = 0
Try this instead:
SELECT dueDate, completed
FROM task AS t
WHERE (dueDate = "2012-08-17" AND completed = 0)
UNION ALL
SELECT dueDate, completed
FROM project AS p
WHERE (dueDate = "2012-08-17" AND completed = 0)
This should give you all records from each table where dueDate = "2012-08-17" and completed = 0.

A logical problem with two tables

Hey guys, I created a list for fixtures.
$result = mysql_query("SELECT date FROM ".TBL_FIXTURES." WHERE compname = '$comp_name' GROUP BY date");
$i = 1;
$d = "Start";
while ($row = mysql_fetch_assoc($result))
{
$odate = $row['date'];
$date=date("F j Y", $row['date']);
echo "<p>Fixture $i - $d to $date</p>";
}
As you can see from the query, the date is displayed from the fixtures table.
The way my system works is that when a fixture is "played", it is removed from this table. Therefore when the entire round of fixtures are complete, there wont be any dates for that round in this table. They will be in another table.
Is there anyway I can run an other query for dates at the same time, and display only dates from the fixtures table if there isnt a date in the results table?
"SELECT * FROM ".TBL_CONF_RESULTS."
WHERE compid = '$_GET[id]' && type2 = '2' ORDER BY date"
That would be the second query!
EDIT FROM HERE ONWARDS...
Is there anyway I can select the date from two tables and then only use one if there are matches. Then use the rows of dates (GROUPED BY) to populate my query? Is that possible?
It sounds like you want to UNION the two result sets, akin to the following:
SELECT f.date FROM tbl_fixtures f
WHERE f.compname = '$comp_name'
UNION SELECT r.date FROM tbl_conf_results r
WHERE r.compid = '$_GET[id]' AND r.type2 = '2'
GROUP BY date
This should select f.date and add rows from r.date that aren't already in the result set (at least this is the behaviour with T-SQL). Apparently it may not scale well, but there are many blogs on that (search: UNION T-SQL).
From the notes on this page:
//performs the query
$result = mysql_query(...);
$num_rows = mysql_num_rows($result);
//if query result is empty, returns NULL, otherwise,
//returns an array containing the selected fields and their values
if($num_rows == NULL)
{
// Do the other query
}
else
{
// Do your stuff as now
}
WHERE compid = '$_GET[id]' presents an oportunity for SQL Injection.
Are TBL_FIXTURES and TBL_CONF_RESULTS supposed to read $TBL_FIXTURES and $TBL_CONF_RESULTS?
ChrisF has the solution!
One other thing you might think about is whether it is necessary to do a delete and move to another table. A common way to solve this type of challenge is to include a status field for each record, then rather than just querying for "all" you query for all where status = "x". For example, 1 might be "staging", 2 might be "in use", 3 might be "used" or "archived" In your example, rather than deleting the field and "moving" the record to another table (which would also have to happen in the foreach loop, one would assume) you could simply update the status field to the next status.
So, you'd eliminate the need for an additional table, remove one additional database hit per record, and theoretically improve the performance of your application.
Seems like what you want is a UNION query.
$q1 = "SELECT DISTINCT date FROM ".TBL_FIXTURES." WHERE compname = '$comp_name'";
$q2 = "SELECT DISTINCT date FROM ".TBL_CONF_RESULTS.
"WHERE compid = '$_GET[id]' && type2 = '2'";
$q = "($q1) UNION DISTINCT ($q2) ORDER BY date";

Categories