MySQL Search Between Date and Time - mysql

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

Related

Select one piece of data from every day at a specific hour MySQL

My database has data imputed every 1 minute and is stored in the format 2020-04-05 16:20:04 under a column called timestamp.
I need a MySQL query to select data from every day at a specific hour (the second does not matter), for for example I want to get the data from 16:00 of every day from the past 30 days.
It currently, just grabs the data from the past 30 days and then the PHP application sorts it, however, this is causing very slow loading time, hence wanting to only select the wanted data from the database.
Example of data
Please try the following sql:
select
d.timestamp, hour(d.timestamp)
from
demo1 d
where
DATEDIFF(NOW(), d.timestamp) < 30 and hour(d.timestamp) = 16;
The create sql is as following:
CREATE TABLE `demo1` (
`id` int(11) not null auto_increment primary key,
`serverid` int(11) not null,
`timestamp` datetime not null,
KEY `idx_timestamp` (`timestamp`)
) engine = InnoDB;
insert into `demo1` (serverid, timestamp)
VALUES (1, "2020-07-05 16:20:04"),
(2, "2020-07-06 17:20:04"),
(3, "2020-07-07 16:40:04"),
(4, "2020-07-08 08:20:04"),
(5, "2020-07-05 15:20:04"),
(5, "2020-07-05 16:59:04"),
(5, "2020-06-04 16:59:04");
Zhiyong's response will work, but wont perform well. You need to figure out a way to get the query to use indexes.
You can add a simple index on timestamp and run the query this way:
SELECT
d.timestamp, d.*
FROM demo1 d
WHERE 1
AND d.timestamp > CURDATE() - INTERVAL 30 DAY
AND hour(d.timestamp) = 16;
In MySQL 5.7 and up, you can created a generated column (also called calculated column) top store the hour of the timestamp in a separate column. You can then index this column, perhaps as a composite index of hour + timestamp, so that the query above will perform really quickly.
ALTER TABLE demo1
ADD COLUMN hour1 tinyint GENERATED ALWAYS AS (HOUR(timestamp)) STORED,
ADD KEY (hour1, timestamp);
The result query would be:
SELECT
d.timestamp, d.*
FROM demo1 d
WHERE 1
AND d.timestamp > CURDATE() - INTERVAL 30 DAY
AND hour1 = 16;
More info on that here:
https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html
https://dev.mysql.com/doc/refman/5.7/en/generated-column-index-optimizations.html

Building a RFID log-in system in 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.

MySQL JSON_EXTRACT performance

We have a logging table which is growing as new events happening. At the moment we have around 120.000 rows of log events stored.
The events table looks like this:
'CREATE TABLE `EVENTS` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`EVENT` varchar(255) NOT NULL,
`ORIGIN` varchar(255) NOT NULL,
`TIME_STAMP` TIMESTAMP NOT NULL,
`ADDITIONAL_REMARKS` json DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=137007 DEFAULT CHARSET=utf8'
Additional_Remarks is a JSON field because different applications log into this table and can add more information to the event which happened. I did not want to put any data structure here, because this information can be different. For example one project management application can log:
ID, "new task created", "app", NOW(), {"project": {"id": 1}, "creator": {"id": 1}}
While other applications do not have projects or creator, but maybe cats and owners they want to store in the Additional_Remarks field.
Queries can use the Additional_Remarks field to filter information for one specific application like:
SELECT
DISTINCT(ADDITIONAL_REMARKS->"$.project.id") as 'project',
COUNT(CASE WHEN EVENT = 'new task created' THEN 1 END) AS 'new_task'
FROM EVENTS
WHERE DATE(TIMESTAMP) >= DATE(NOW()) - INTERVAL 30 DAY
AND ORIGIN = "app"
GROUP BY project
ORDER BY new_task DESC
LIMIT 10;
Output EXPLAIN query:
'1', 'SIMPLE', 'EVENTS', NULL, 'ALL', NULL, NULL, NULL, NULL, '136459', '100.00', 'Using where; Using temporary; Using filesort'
With this query I get the top 10 projects with the most created tasks for the last 30 days. Works fine, but this queries get slower and slower as our table grows. With 120.000 rows this query needs over 30 seconds.
Do you know any way to improve the speed? The newest information in the table with the highest id is more important then older entries. Often I look only for entries which happened in the last X days. It would be useful to stop the query after the first entry is older as X days from the where clause, as all further entries are even older.
if TIME_STAMP is indexed, the DATE function will not allow the index to be used because it is non-deterministic.
WHERE DATE(TIMESTAMP) >= DATE(NOW()) - INTERVAL 30 DAY
Can be rewritten as.
WHERE TIMESTAMP >= UNIX_TIMESTAMP(DATE(NOW()) - INTERVAL 30 DAY)
Do you know any way to improve the speed?
The only way i can see to speed up the query is to multicolumn index TIMESTAMP and ORIGIN like so ALTER TABLE EVENTS ADD KEY timestamp_origin (TIME_STAMP, ORIGIN); and mine query adjustment above
EDIT
And a delivered table may improve query speed because it will use the new index.
SELECT
ADDITIONAL_REMARKS->"$.project.id" AS 'project',
COUNT(CASE WHEN EVENT = 'new task created' THEN 1 END) AS 'new_task'
FROM (
SELECT
*
FROM EVENTS
WHERE
TIME_STAMP >= UNIX_TIMESTAMP(DATE(NOW()) - INTERVAL 30 DAY)
AND
ORIGIN = "app"
)
AS events_within_30_days
GROUP BY project
ORDER BY new_task DESC
LIMIT 10;
A inner select where I already reduce the amount of rows could reduce the query time from 30 sec to 0.05 sec.
It looks like:
SELECT
ADDITIONAL_REMARKS->"$.project.id" AS 'project',
COUNT(CASE WHEN EVENT = 'new task created' THEN 1 END) AS 'new_task'
FROM (
SELECT *
FROM EVENTS WHERE
EVENT = 'new task created'
AND TIME_STAMP >= UNIX_TIMESTAMP(DATE(NOW()) - INTERVAL 30 DAY)
AND ORIGIN = "app" ) AS events_within_30_days
GROUP BY project
ORDER BY new_task DESC
LIMIT 10;

MySql Inserting From ISNULL Clause

After trawling through the web and still not finding any simple answers I'm hoping someone can advise on this.
All I want to do is insert a new row into a table if a value from that table is not null. In this case, I have a table called "Events".
Events Table
Event_ID | Event_Name | Event_Interval | Event_Date | Event_Interval | Event_Repeat
-----------------------------------------------------------------------------------
1 My Event 14 2017-04-04 14 Completion
There are two conditions on which I'm working towards; best described using a bit of pseudocode:
IF EVENT_INTERVAL != NULL THEN
IF EVENT_REPEAT = "DUE" THEN
DATE_ADD(EVENT_DATE, INTERVAL EVENT_INTERVAL DAY)
ELSE
DATE_ADD(NOW(), INTERVAL EVENT_INTERVAL DAY)
END IF
END IF
So: when someone completes an event, if the interval value is not null, it inserts a new row to the table where it adds the interval value to either the event date or the current date, depending on the value in "Event_Repeat".
So far I've got to :
SELECT
(IF(ISNULL((SELECT e.Event_Interval from events e where e.Event_ID = 1)) = 1, "NULL", "NOT NULL"))
This works absolutely fine and can distinguish whether the interval value is NULL or not. I've tried to incorporate/substitute an insert statement into here but as of yet no luck.
If anyone can help to structure this properly (or better!) please feel free as I'm still finding my way around queries etc.
Thanks in advance.
you can create SP as
CREATE PROCEDURE update_event
#event_id int
AS
BEGIN
DECLARE #event_repeat_temp nvarchar(200)
SELECT #event_repeat_temp = event_repeat FROM event_table WHERE event_id = #event_id
PRINT #event_repeat_temp
IF #event_repeat_temp = 'Due'
BEGIN
INSERT INTO event_table (event_id , event_name, event_interval, event_date, interval,event_repeat) VALUES (2,'My Event',14,'completion')
END
ELSE
BEGIN
//whatever you want
END
END
and call this SP as below
EXEC update_event #event_id =1

Mysql time spread

Sorry, I have difficulty explaining my question and search for a previous answer. This is my problem -- I have a MySQL table with events
CREATE TABLE events {
id INT,
event INT,
date DATETIME
}
Data is being added a few times a week or month. I would like to see the statistical spread of time between two adjacent events. Something like:
Time difference between two events
1 day appart - 4 occurances
2 days apart - 2 occurances
n days apart - x occurances
It should be something like this, I guess, but calculating the time difference between events.
SELECT COUNT('id') AS 'no', ??? AS 'delta' GROUP BY FLOOR( 'delta' )
This piece of SQL code did it:
SET #old = NOW();
SELECT COUNT(`id`) AS `no`, query1.`delta` FROM
( SELECT `id`, `date`, DATEDIFF( #old, `date` ) AS `delta`, #old := `date` AS `old`
FROM `life`
ORDER BY `date`DESC ) query1
GROUP BY `delta`
ORDER BY `delta`