Building a RFID log-in system in MySQL - mysql

I'm trying to build an automatic attendance site using RFID, currently MySQL already records the time and date a student enters the campus, but I want each attendance tagged as either "late" or "on-time" using the time they entered the campus.
For example Student A taps his RFID at the gate at 7:11 AM this would be tagged as LATE, Student B taps her RFID at the gate at 6:55AM and this would tagged as early in MySQL.
I am a beginner in JavaScript, MySQL and jQuery so I don't know which code to use. I'm self taught so I try to search for tutorials online, but for this one it seems that I cannot find tutorials for it.
Here's my code for the RFID tag.
<?php
require_once 'admin/connect.php';
$student = $_POST['student'];
$time = date("H:i", strtotime("+8 HOURS"));
$date = date("Y-m-d", strtotime("+8 HOURS"));
$q_student = $conn->query("SELECT * FROM `student` WHERE `student_no` = '$student'") or die(mysqli_error());
$f_student = $q_student->fetch_array();
$student_name = $f_student['firstname']." ".$f_student['lastname'];
$conn->query("INSERT INTO `time` VALUES('', '$student', '$student_name', '$time', '$date')") or die(mysqli_error());
echo "<h3 class = 'text-muted'>".$student_name." <label class = 'text-info'>at ".date("h:i a", strtotime($time))."</label></h3>";
This is what my database currently looks like when a students taps his/her ID:
https://imgur.com/a/dgwMJ5i
Hi! I'm Getting MYSQL Errors i dont know how to troubleshoot
.\libraries\classes\Display\Results.php#4196: PhpMyAdmin\Display\Results->_getSortedColumnMessage(
,
string 'student_name',
)
.\libraries\classes\Sql.php#1666: PhpMyAdmin\Display\Results->getTable(
,
array,
array,
boolean true,
)
.\libraries\classes\Sql.php#1468: PhpMyAdmin\Sql->getHtmlForSqlQueryResultsTable(
,
string './themes/pmahomme/img/',
NULL,
array,
boolean false,
integer 0,
integer 0,
boolean true,
,
array,
boolean true,
)
.\libraries\classes\Sql.php#2250: PhpMyAdmin\Sql->getQueryResponseForNoResultsReturned(
array,
string 'db_sars',
string 'time',
NULL,
integer 0,
,
NULL,
string './themes/pmahomme/img/',
NULL,
,
string 'SELECT date, student_no, student_name, time, CASE WHEN time > \'07:00\' THEN \'LATE\' ELSE \'ON TIME\' END AS tag FROM time WHERE date >= CURDATE() - INTERVAL 7 DAY AND time BETWEEN \'05:00\' AND \'09:00\' ORDER BY date, student_name',
NULL,
)
.\import.php#758: PhpMyAdmin\Sql->executeQueryAndGetQueryResponse(
array,
boolean false,
string 'db_sars',
string 'time',
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
string 'tbl_structure.php',
string './themes/pmahomme/img/',
NULL,
NULL,
NULL,
string 'SELECT date, student_no, student_name, time, CASE WHEN time > \'07:00\' THEN \'LATE\' ELSE \'ON TIME\' END AS tag FROM time WHERE date >= CURDATE() - INTERVAL 7 DAY AND time BETWEEN \'05:00\' AND \'09:00\' ORDER BY date, student_name',
NULL,
NULL,
)

(Please avoid using images to show key parts of your problem).
It looks like your time table contains these columns:
student
student_name
time
date
It looks like you have no information about whether a student is arriving or departing. That makes your logic a little trickier. Let's assume any row of your time table between 05:00 and 09:00 means an arrival, and that any arrival after 07:00 is considered late. You can get a list of student arrivals in the last week with this query. They'll have the tag LATE when needed or ON TIME otherwise.
SELECT `date`, student, student_name, `time`,
CASE WHEN time > '07:00' THEN 'LATE' ELSE 'ON TIME' END AS tag
FROM `time`
WHERE `date` >= CURDATE() - INTERVAL 7 DAY
AND `time` BETWEEN '05:00' AND '09:00'
ORDER BY `date`, `student`
I guess that's what you mean by tagging each attendance.
Edit If you want to show these tags LATE on your web site, you need no extra column in the database. Just use a query like the one I showed; the query itself contains the rule defining lateness and applies the tag. Then display the results from the query. It's generally not a good idea to alter a table to insert conditions you can derive from the existing content of each row. (That's different if you have hundreds of millions of rows and must denormalize your table to gain performance. But you're nowhere near that point.)
Pro tip Don't use reserved words like DATE and TIME for the names of tables or columns. Doing so makes it harder to track down bugs.

Related

MySQL select records using MAX(datefield) minus three days

Clearly, I am missing the forest for the trees...I am missing something obvious here!
Scenario:
I've a typical table asset_locator with multiple fields:
id, int(11) PRIMARY
logref, int(11)
unitno, int(11)
tunits, int(11)
operator, varchar(24)
lineid, varchar(24)
uniqueid, varchar(64)
timestamp, timestamp
My current challenge is to SELECT records from this table based on a date range. More specifically, a date range using the MAX(timestamp) field.
So...when selecting I need to start with the latest timestamp value and go back 3 days.
EX: I select all records WHERE the lineid = 'xyz' and going back 3 days from the latest timestamp. Below is an actual example (of the dozens) I've been trying to run.
MySQL returns a single row with all NULL values for the following:
SELECT id, logref, unitno, tunits, operator, lineid,
uniqueid, timestamp, MAX( timestamp ) AS maxdate
FROM asset_locator
WHERE 'maxdate' < DATE_ADD('maxdate',INTERVAL -3 DAY)
ORDER BY uniqueid DESC
There MUST be something obvious I am missing. If anyone has any ideas, please share.
Many thanks!
MAX() is an aggregated function, which means your SELECT will always return one row containing the maximum value. Unless you use GROUP BY, but it looks that's not what you need.
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_max
If you need all the entries between MAX(timestamp) and 3 days before, then you need to do a subselect to obtain the max date, and after that use it in the search condition. Like this:
SELECT id, logref, unitno, tunits, operator, lineid, uniqueid, timestamp
FROM asset_locator
WHERE timestamp >= DATE_ADD( (SELECT MAX(timestamp) FROM asset_locator), INTERVAL -3 DAY)
It will still run efficiently as long as you have an index defined on timestamp column.
Note: In your example
WHERE 'maxdate' < DATE_ADD('maxdate',INTERVAL -3 DAY)
Here you were are actually using the string "maxdate" because of the quotes causing the condition to return false. That's why you were seeing NULL for all fields.
Edit: Oops, forgot the "FROM asset_locator" in query. It got lost at some point when writing the answer :)

Calculate visits from pageviews in MySQL

I am recording each page that is viewed by logged in users in a MySQL table. I would like to calculate how may visits the site has had in within time period (eg day, week, month, between 2 dates etc.) in a similar way to Google Analytics.
Google Analytics defines a visit as user activity separated by at least 30 minutes of inactivity. I have the user ID, URL and date/time of each pageview so I need a query that can calculate a visit defined in this way.
I can easily count the pageviews between 2 dates but how can dynamically work out if a pageview from a user is within 30 minutes of another pageview and only count it once?
Here is a small sample of the data:
http://sqlfiddle.com/#!2/56695/2
Many thanks.
First, note that doing this kind of analysis in SQL is not the best idea indeed. It just has a very high computational complexity. There are many ways of eliminating the complexity from here.
Since we're talking about the analytics data, or something more akin to access logs of a typical web-server, we could as well just add a cookie value to it, and have a simple piece of front-end code that makes this cookie and gives it a random id, unless the cookie already exists. And sets the expiry of the cookie to whatever you want your session to be, which is 30 minutes by default. Note that you can change your session length in GA. Now your task is as simple as counting unique ids grouped by user. The complexity of N. The favourite complexity of most DBMSes.
Now if you really want to be solving the gaps-and-islands problem, you can just look at classical solutions of the problem, as well as some examples here on SO: SQL Server - Counting Sessions - Gaps and islands
Finally, the 'proper' way of tracking the session id would be generating a random string on every hit and setting it to a certain custom dimension, while having it as a session-level dimension for GA UA. Here's a more detailed explanation.
GA4 is gracious enough to surface the session id more properly, and here is how.
First, I would also index the uri column and make each column "not nullable":
CREATE TABLE IF NOT EXISTS `uri_history` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user` int(10) unsigned NOT NULL, /* cannot be NULL */
`timestamp` int(10) unsigned NOT NULL, /* cannot be NULL */
`uri` varchar(255) NOT NULL, /* cannot be NULL */
PRIMARY KEY (`id`),
KEY `user` (`user`),
KEY `timestamp` (`timestamp`),
KEY `uri`
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
However, I am a bit bewildered by your timestamp column having an int(10) definition and values such as 1389223839. I would expect an integer timestamp to be a value created with the UNIX_TIMESTAMP function call, but 1389223839 would then represent a value of '2014-01-08 18:30:39' for the 'America/New_York' time zone. I would have expected a sample timestamp to be more "contemporary." But I will have to assume that this column is a Unix timestamp value.
Let's say I was interested in gathering statistics for the month of June of this year:
SELECT * FROM uri_history
WHERE DATE(FROM_UNIXTIME(`timestamp`)) between '2022-06-01' and '2022-06-30'
ORDER BY `uri`, `user`, `timestamp`
From this point on I would process the returned rows in sequence recognizing breaks on the uri and user columns. For any returned uri and user combination, it should be very simple to compare the successive timestamp values and see if they differ by at least 30 minutes (i.e. 1800 seconds). In Python this would look like:
current_uri = None
current_user = None
current_timestamp = None
counter = None
# Process each returned row:
for row in returned_rows:
uri = row['uri']
user = row['user']
timestamp = row['timestamp']
if uri != current_uri:
# We have a new `uri` column:
if current_uri:
# Statistics for previous uri:
print(f'Visits for uri {current_uri} = {counter}')
current_uri = uri
current_user = user
counter = 1
elif user != current_user:
# We have a new user for the current uri:
current_user = user
counter += 1
elif timestamp - current_timestamp >= 1800:
# New visit is at least 30 minutes after the user's
# previous visit for this uri:
counter += 1
current_timestamp = timestamp
# Output final statistics, if any:
if current_uri:
print(f'Visits for uri {current_uri} = {counter}
Do i'm correct that you want count how many user visit the site within 30 minute for login user but only count as one per user event user visit more page in that period of time? If that so you could filter it then group by period of time visit within 30 minute.
First convert integer timestimp into date by using FROM_UNIXTIME, get minute visit, group minute has past, get period of start and end
SELECT DATE_FORMAT(FROM_UNIXTIME(timestamp), '%e %b %Y %H:%i:%s') visit_time,
FROM_UNIXTIME(timestamp) create_at,
MINUTE(FROM_UNIXTIME(timestamp)) create_minute,
MINUTE(FROM_UNIXTIME(timestamp))%30 create_minute_has_past_group,
date_format(FROM_UNIXTIME(timestamp) - interval minute(FROM_UNIXTIME(timestamp))%30 minute, '%H:%i') as period_start,
date_format(FROM_UNIXTIME(timestamp) + interval 30-minute(FROM_UNIXTIME(timestamp))%30 minute, '%H:%i') as period_end
FROM uri_history
After that group by period of start and COUNT DISTINCT user
SELECT date_format(FROM_UNIXTIME(timestamp) - interval minute(FROM_UNIXTIME(timestamp))%30 minute, '%H:%i') as period_start,
date_format(FROM_UNIXTIME(timestamp) + interval 30-minute(FROM_UNIXTIME(timestamp))%30 minute, '%H:%i') as period_end,
COUNT(DISTINCT(user)) count
FROM uri_history
GROUP BY period_start
ORDER BY period_start ASC;
I got these from these answer

MYSQL Finding accounts that have not had log entries updated in n days

I have an SQL query that I need help with...
Basically I have two tables I need to work with. One contains customer accounts and the other contains a log of customer service reps interactions with customers. I want this query give me the id of any account that has not had a log entry (interaction) in the last 14 days. I also want to filter out a few rep accounts that are irrelevant (using the assignedto field as you will see). Also, the date format in the log table is funky non-standard and I cannot change it, as software I have not written also utilizes this database.
The two tables are cm.dbs (customer accounts) and cm.log (interaction log).
This is the query I came up with but it takes FOREVER to run. The subquery works perfectly and takes a fraction of a second, but when the main query runs with the subquery it is just impossibly slow. I'm guessing this is because the subquery is being run for every row in the main query (and it doesn't need to be) but I am kind of clueless as to how to fix this, as I am not an expert in SQL, I know enough to create basic to intermediate queries and this is not something I have done before.
Here is the query I created so far:
SELECT id FROM cm.dbs WHERE id NOT IN (SELECT filenumber FROM cm.log
WHERE STR_TO_DATE(logdate, '%m/%d/%Y')
BETWEEN DATE_SUB(NOW(), INTERVAL 14 DAY)
AND NOW()
GROUP BY filenumber)
AND assignedto != 'OLD_ACCTS'
AND assignedto != 'HOUSE_ACCOUNTS'
AND assignedto != 'PAID_ACCOUNTS';
The subquery finds all the accounts that have entries in the log table within the last two weeks. It does this job perfectly. The trick is then to get the main query to find all the accounts that do not have entries.
Note also, that the filenumber field in cm.log corresponds to id in the cm.dbs table.
I may have approached this in a completely silly way and I am not above admitting that. Any input on making this work correctly and efficiently is appreciated. I'd also love the fixes/changes anyone recommends explained. I am not simply wanting a query built for me, I want to learn what I did wrong and how to do it better so next time I can figure this out for myself. I rarely ever ask questions like this, I usually figure things out on my own but this has me stumped.
EDIT: Here is a partial schema for the relevant fields in the tables:
cm.dbs:
id int(10) UN PK AI
title varchar(45)
firstname varchar(200)
middlename varchar(200)
lastname varchar(200)
fullname varchar(200)
address varchar(200)
address2 varchar(200)
city varchar(200)
state varchar(200)
zip varchar(50)
assignedto varchar(200)
...
cm.log:
id int(10) UN PK AI
filenumber varchar(200)
agentname varchar(200)
logtime varchar(200)
logdateandtime varchar(200)
logdate varchar(200)
logmessage mediumtext
Your query looks correct to me except the change below ( since you have multiple assignedto values to be checked for, use a IN operator instead making them in separate OR exclusively.)
SELECT id FROM cm.dbs WHERE id NOT IN (SELECT filenumber FROM cm.log
WHERE STR_TO_DATE(logdate, '%m/%d/%Y')
BETWEEN DATE_SUB(NOW(), INTERVAL 14 DAY)
AND NOW()
GROUP BY filenumber)
AND assignedto NOT IN ('OLD_ACCTS','HOUSE_ACCOUNTS','PAID_ACCOUNTS');
This is the best I can do without a database schema, but should hopefully be pretty close to what you were looking for (or at least point you in the right direction):
SELECT DISTINCT dbs.id
FROM cm.dbs, cm.log
WHERE dbs.id = log.filenumber
AND STR_TO_DATE(log.logdate, '%m/%d/%Y') NOT BETWEEN DATE_SUB(NOW(), INTERVAL 14 DAY) AND NOW()
AND dbs.assignedto NOT IN ('OLD_ACCTS','HOUSE_ACCOUNTS','PAID_ACCOUNTS');
If you get a chance run EXPLAIN on your query and add the output to your question, so we can profile it better (and include the database schema).
I think you are attacking this in the wrong way. Lets break down what you're looking for.
First thing is the filenumber and max logdate:
SELECT filenumber, MAX(logdate)
FROM cm.log
GROUP BY filenumber
So now we just need to join it to the other table:
SELECT filenumber, MAX(logdate), assignedto
FROM cm.log as log
INNER JOIN cm.dbs as dbs ON log.filenumber = dbs.id
GROUP BY filenumber
Now we want to apply some conditions on what we just selected (older than 2 weeks, not in those 3 groups) :
SELECT * FROM (
SELECT log.filenumber, MAX(logdate) as logdate, assignedto
FROM cm.log as log
INNER JOIN cm.dbs as dbs ON log.filenumber = dbs.id
GROUP BY filenumber) t
WHERE logdate < DATE_SUB(NOW(), INTERVAL 14 DAY)
AND assignedto NOT IN ('OLD_ACCTS','HOUSE_ACCOUNTS','PAID_ACCOUNTS')

Selecting records timestamped in a certain time range of each other

How would you go about selecting records timestamped within a certain amount of time of each other?
Application and sought solution:
I have a table with records of clicks, I am wanting to go through and find the clicks from the same IP that occurred within a certain time period.
e.g.: SELECT ALL ip_address WHERE 5 or more of the same ip_address, occurred/are grouped within/timestamped, within 10 minutes of each other
You can select record like that
$recorddate = date("Y-m-d");
SELECT * FROM table WHERE date > UNIX_TIMESTAMP('$recorddate');
UNIX_TIMESTAMP function converts date to timestamp. And you can easily use it in your queries.
If you want to grab the record in 10 minutes interval you can do something like that
$starttime = "2012-08-30 19:00:00";
$endtime = "2012-08-30 19:10:00";
SELECT * FROM table WHERE date >= UNIX_TIMESTAMP('$starttime') AND date <= UNIX_TIMESTAMP('$endtime') ;
Decided not to try for a single query on the raw data.
After discussion with a friend, and then reading about options mentioning the memory engine, and PHP memcatche; I decided to go with a regular table to record click counts that use a time to live timestamp. After that timestamp is passed, a new ttl is assigned and the count is re-set.
One thing is for my application I can't be exactly sure how long the parameter configuration settings will be - if they are larger and the memory gets cleared, then things start over.
It isn't a perfect solution if it is run on user link click, but it should be pretty good about catching click fraud storms, and do the job.
Some managing PHP/MySQL code ("Drupalized queries"):
$timeLimit = $clickQualityConfigs['edit-submitted-within-x-num-of-same-ip-clicks']." ".$clickQualityConfigs['edit-submitted-time-period-same-ip-ban']; // => 1 days // e.g.
$filterEndTime = strtotime("+".$timeLimit);
$timeLimitUpdate_results = db_query('UPDATE {ip_address_count}
SET ttl_time_stamp = :filterendtime, click_count = :clickcountfirst WHERE ttl_time_stamp < :timenow', array(':filterendtime' => $filterEndTime, ':clickcountfirst' => '0', ':timenow' => time()));
$clickCountUpdate_results = db_query('INSERT INTO {ip_address_count} (ip_address,ttl_time_stamp,click_count)
VALUES (:ipaddress,:timestamp,:clickcountfirst)
ON DUPLICATE KEY UPDATE click_count = click_count + 1', array(':ipaddress' => $ip_address,':timestamp' => $filterEndTime,':clickcountfirst' => '1'));
DB info:
CREATE TABLE `ip_address_count` (
`ip_address` varchar(24) NOT NULL DEFAULT '',
`ttl_time_stamp` int(11) DEFAULT NULL,
`click_count` int(11) DEFAULT NULL,
PRIMARY KEY (`ip_address`)
)

MySQL Search Between Date and Time

I'm trying to setup an automatic event countdown. What I want to do is use MySQL to fill the countdown with the next upcoming event.
If the event is in the future, the code will show 'upcoming event - countdown' and if the event is currently taking place then show 'event happening'.
I have a table structure like this:
CREATE TABLE jos_eventlist_events (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
catsid INT UNSIGNED,
dates DATE,
times TIME,
enddates DATE,
endtimes TIME,
published TINYINT(1),
title VARCHAR(255)
) Engine=InnoDB;
INSERT INTO jos_eventlist_events
VALUES
(1, 6, '2012-01-15', '21:00', '2012-01-15', '22:00', 1, 'Partying'),
(2, 6, '2012-01-15', '23:00', '2012-01-16', '01:00', 1, 'More partying')
;
So essentially, the query should do something akin to this: If there are no events currently taking place, retrieve the next upcoming event. If there is an event taking place then display it.
The following query is a stepping stone as to what I'm trying to achieve.
SELECT catsid,id,dates,times,endtimes,published,title
FROM jos_eventlist_events
WHERE catsid = 6 AND published = 1
AND dates && times >= CURDATE() && CURTIME()
AND dates && endtimes <= CURDATE() && CURTIME()
LIMIT 1
Keep in mind that my server is 5 hours behind my local time.
I know I'm missing something really silly with this, but any help you can provide will be greatly appreciated.
try (I did not test it):
SELECT catsid,id,dates,times,endtimes,published,title
FROM jos_eventlist_events
WHERE catsid = 6 AND published = 1
AND (CONCAT(dates, ' ', times)>= NOW()
OR ( CONCAT(dates, ' ', times) < NOW()
AND CONCAT(enddates, ' ', endtimes) >= NOW() ) )
ORDER BY CONCAT(dates, ' ', times)
LIMIT 1