Mysql query logic (course before, after and between) - php

I have a course with many columns, but only three of them are required for this question:
----------------------------------------
| start_date | start_time | end_time |
----------------------------------------
| 2018-09-12 | 09:30 | 11:30 |
----------------------------------------
I need a query that includes courses with this criteria:
Courses that began 30 minutes ago or will begin in the next 30 minutes.
Courses that end in 30 minutes or ended in the next 30 minutes.
Courses currently in progress
The first two ones I managed to write the query for, but the third criteria was now requested. So far I have this query (pseudo mysql query)
SELECT *
FROM courses
WHERE start_date = today AND
((start_time >= now-30min AND start_time <= now+30min) OR
(end_time >= now-30min AND end_time <= now+30min))
Question is, how to write a query to satisfy all three requirements... I am blowing my mind for 1 hour and could not make it work, something is not working in my head.
Thanks.

Assuming courses do not go over the midnight boundary:
select c.*
from courses c
where start_date = curdate() and
(start_time between curtime() - interval 30 minute and curtime() - interval 30 minute or
end_time between curtime() - interval 30 minute and curtime() - interval 30 minute or
( start_time < curtime() and end_time > curtime() )
)
The last condition is simply that the course started in the past and will end in the future.

Related

Daily month sales separate AM and PM

I have my php mysql currently that get the entire months sales and groups it by days. I am now trying to take that further and separate am vs pm sales. The AM shift is 10am-7pm and PM shift is 7pm-2am. I Know I can group by day then by hour and iterate through and get the am that way but I am sure their is a better way directly in sql.
Thanks for any insight.
SELECT DATE(a_tabs.strDate - INTERVAL 16 HOUR) as day ,
DATE_FORMAT(a_tabs.strDate, '%h') AS hour ,
sum(a_invoices.Total) as total
FROM a_tabs
Right JOIN a_invoices on a_tabs.TabId = a_invoices.TabId
WHERE a_tabs.strDate BETWEEN '2022-03-01 09:00:00' and '2022-03-31 18:00:00'
AND a_invoices.status='c'
and a_tabs.status<>'v'
GROUP BY day , hour
result from this query
So in a given day you have 3 periods:
02:00 to 10:00 [8h, no shift]
10:00 to 19:00 [9h, AM shift]
19:00 to 02:00 [7h, PM shift]
But the trouble with a naieve solution is that the PM shift crosses over the date boundary.
Assuming a simplified table like:
CREATE TABLE sales (
id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
rep_id INTEGER UNSIGNED,
dt DATETIME,
amount INTEGER
);
We can correctly align the shifts with the date boundary with dt - INTERVAL 2 HOUR and then something like:
SELECT
DATE(dt - INTERVAL 2 HOUR) 'day',
IF( HOUR(dt - INTERVAL 2 HOUR) BETWEEN 0 AND 7, 'UN',
IF( HOUR(dt - INTERVAL 2 HOUR) BETWEEN 8 AND 16, 'AM', 'PM' )
) AS 'shift',
SUM(amount) AS 'sales'
FROM sales
GROUP BY day, shift;
Sample data omitted for brevity, see on sqlfiddle: http://sqlfiddle.com/#!9/d4fbcb/22/0, but for a sale every 15 minutes on the dot, beginning at 2022-01-01 00:00:00:
| day | shift | sales |
|------------|-------|-------|
| 2021-12-31 | PM | 8 |
| 2022-01-01 | AM | 36 |
| 2022-01-01 | PM | 28 |
| 2022-01-01 | UN | 32 |
| 2022-01-02 | AM | 36 |
| 2022-01-02 | PM | 28 |
| 2022-01-02 | UN | 32 |
You can see that it correctly assigns the sales between 00:00 and 02:00 to the previous day's PM shift, and sales outside the defined shifts as "UN" for undefined.
However, with regard to maintainability, extensibility, and performance: I would not really recommend this approach of calculating the shift duringreport generation at all.
Maintainability: At some point in the future the shift boundary changes, now this query is returning incorrect shift data going forward. The naieve fix is to just change the hours in the query, but now it returns incorrect results for past data.
Extensibilty: At some point in the future a "cover" shift is added for 4PM to 10PM to account for demand. It is not possible to compute using a query like this anymore.
Performance: All of those dt - INTERVAL 2 HOUR and IF() statements add overhead and make it difficult or impossible to use indexes depending on what the requirements off your query are.
What I would suggest is making the "shift" into metadata that is associated with the sale record and calculated at insert time. Depending on your particular requirements, it might just be a string in the sale record, eg: 20220101_AM, or a foreign key relation into more robust schema.
Given both your group by clauses are related to the time. Start by shifting the date so that AM is truly AM and PM is truly PM.
a_tabs.strDate - INTERVAL 7 HOUR
(7 chosen as 7pm end of AM shift).
Use UNIX_TIMESTAMP to get this down to a second value (hours would be better but there isn't a function for that). And then div by a 12 hr interval.
So
SELECT ...
GROUP BY UNIX_TIMESTAMP(a_tabs.strDate - INTERVAL 7 HOUR) DIV (60*60*12)

Check if user has uploaded more or exactly 50 files in less than two minutes MySQL

So I have an image uploader, and I want to create a minimal bot protection.
My table structure looks like this:
| ID | uploader_ip | image_name | image_url_id | date
------------------------------------------------------
| 1 | 127.0.0.1 | bla.jpg | fsdJGf | UNIX Timestamp (UPDATE ON: Create)
I want to check if there are more or exactly 50 images/rows, that were created in less or exactly two minutes.
How can I do that?
You'll want to construct a query that incorporates count and group, and puts a range on the date column. If you were to do this for only the most recent two minutes, your query would look something like this:
select count(*) from your_table where uploader_ip="<some ip>" and date > (now() - interval 2 minute) group by uploader_ip;
Here is the query
Select count(*) from table
where
table.date > now() - interval 2 minute
-- or the function date_sub(now(), interval 2 minute)
and id = 1
If you want all user id that have uploads within a 2 minute window:
select id,
(select count(*)
from table
where id = t.id
and date > t.date - interval 1 minute
and date < t.date + interval 1 minute
) as total
from table t
where total >= 50

How to select a records with timestamp ranges?

I mean that if I have table like this:
id | time | name
1 | 1354382314 | test1
2 | 1374769114 | test2
3 | 1322759914 | test3
How to select a records, for example, that was created a week ago, month ago or year ago? Is it possible only with mysql functions or how can I do it in php?
I think it's also possible with mysql functions
Like,
select * from table where time > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 1 WEEK))
select * from table where time > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 1 YEAR))
select * from table where time > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 1 MONTH))
Since timestamp is a number that always grows, you can simply calculate the start and end stamp of your requested range, and use
WHERE `time` >= 'startstamp' AND `time` <= 'endstamp'
To get a stamp 1 week ago, you can use php functoon strtotime("-1 week"). Similar with month etc.
If you need current stamp for anything, use time().
select id,time,name from your_table where (current_timestamp-time)>7*24*60*60;
7*24*60*60 stands for a week

Getting records between two dates for a certain amount of time

Say I have a table which looks like the following:
id | name | date
1 | test1 | 2013-05-12 00:00:01
2 | test2 | 2013-05-13 00:00:01
3 | test3 | 2013-05-14 00:00:01
4 | test4 | 2013-05-15 00:00:01
5 | test5 | 2013-05-15 00:00:02
An example of what I am looking to do would be to go back through the records for the past 3 days and then I want to count how many records there are on each individual say.
So, for the 15th it would return 2, 14th 1, etc.
I know I can do the following to get a count between 2 dates (this would be for the 15th):
SELECT COUNT(id) as recordCount FROM exampletable WHERE date >= STR_TO_DATE('130515', '%y%m%d') - INTERVAL 1 DAY AND date < STR_TO_DATE('130515', '%y%m%d')
However I am unsure how I would do it so I could get an array for the past 3 days.
I have an idea of how I could do it in PHP, having a for loop and then changing the first argument in STR_TO_DATE each time, but I am curious, is there a way I could do this using a SQL query only?
UPDATED
SELECT COUNT(*) recordCount
FROM exampletable
WHERE DATE(`date`) BETWEEN CURDATE() - INTERVAL 3 DAY AND CURDATE()
GROUP BY DATE(`date`)
NOTE This query wont use any index on date
SELECT COUNT(*) AS 'recordCount' FROM dbo.Table_1
WHERE dt > DATEADD(dd,-3,GETDATE())
GROUP BY DATEPART(dd,dt)

PHP MYSQL - Impossible Query? - Find "currently showing"

I have a table with a kind of weird date format in a column as varchar - this is the format that the company has provided me with - the T in the middle seems to mess things up.
EVENTID | EVENT_DATE | EVENT_DURATION
1 | 2012-10-14T06:00 | 15
2 | 2012-10-14T06:15 | 11
3 | 2012-10-14T06:26 | 14
4 | 2012-10-14T06:40 | 10
ect...ect
I have php code to return the current time in the exact same format (with the weird 'T' in the middle'
$thisin = DateTime::createFromFormat('Y-m-d\TH:i', date('Y-m-d\TH:i'));
$thisin->setTimeZone(new DateTimeZone('UTC'));
$thisout= $thisin->format('Y-m-d\TH:i');
Assuming that today is the 14th and the current time is 06:21, how do i query the current row based on duration that matches "$thisout from php" and the next five rows (in the future).
Because the current time and date are returned from php as "2012-10-14T06:21" The query should output
2 | 2012-10-14T06:15 | 11 (Now SHowing)
3 | 2012-10-14T06:26 | 14
4 | 2012-10-14T06:40 | 10
ect ect
I have been scratching my head for hours, DATE_FORMAT() Doesn't seem to work, and I think it may be the T in the middle. I am aslo have to figure out how to use duration to determine if the current time applies to a specific row.
This does not work
SELECT DISTINCT EVENTID, EVENT DATE, EVENT_DURATION
FROM epg_event
WHERE DATE_FORMAT(EVENT_DATE, '%Y-%m-%dT%H:%i') >= DATE_FORMAT(NOW(), '%Y-%m-%dT%H:%i')
ORDER BY EVENT_DATE ASC LIMIT 5
Any Ideas?
You should load the date information into a Date column rather than a varchar column in the database. As previously noted this is the ISO format for dates.
Need to add in the interval.
where event_date + INTERVAL duration MINUTE >= NOW()
I'm not currently able to test it, but something like this might work:
SELECT DISTINCT EVENTID, EVENT_DATE, EVENT_DURATION
FROM epg_event
WHERE (event_date <= NOW()) AND (ADDTIME(event_date, INTERVAL event_duration MINUTE) >= NOW())
ORDER BY event_date ASC LIMIT 5
DATE_FORMAT() is meant for formatting a DATETIME field, not the other way around. And, as mentioned earlier, you event_date should be a DATETIME field and you should convert the time when you import the data to your database.

Categories