MySql: select data based on timestamp and dynamic interval - php

In my MySql DB I have these fields:
id | email_id | interval | start_at | last_sent_at
--- | -------- | ---------- | ------------------- | -------------------
1 | 8293 | +6 months | 2017-06-14 16:59:54 | 2017-06-14 16:59:54
--- | -------- | ---------- | ------------------- | -------------------
2 | 8904 | (NULL) | 2017-05-14 12:32:45 | (NULL)
I am trying to create a dynamic way for users to set a schedule for an email job in laravel. The idea is to use laravel's commands scheduler to run a command that checks all scheduled emails and then runs them if they haven't been sent (whereNull(last_sent_at)) or if the last time they were sent was more than the current time minus 6 months or 12 months or what ever they select for that field.
I have tried several different queries to get this to work to no avail. In laravelized code I have tried the following...
$schedules = Schedule::selectRaw('schedules.schedulable_id, schedules.schedulable_type, UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(schedules.interval) as NowPlusInterval')
->whereNull('last_sent_at')
->orWhere('schedules.last_sent_at', '>=', 'NowPlusInterval')
->get();
and
$schedules = Schedule::whereNull('last_sent_at')
->orWhereRaw('schedules.last_sent_at >= NOW()-'.Carbon::parse('schedules.interval')->toDateTimeString())
->get();
and many many other variations. The second one is the simplest to me but because I am passing the db field to a php function it doesn't recognize that I am trying to get the value from the db field (not parse a string called 'schedules.interval'). Is there anyway write this field into a variable that Carbon can parse or is there a raw mysql query that I can run that will give me all the fields where last_sent_at is null and where now minus the interval listed is more than the last_sent_at.
I've racked my brain on this for hours. Any help is appreciated.

Try using mysql's DATE_SUB() and INTERVAL functions. Something to the effect of:
->whereRaw('last_sent_at >= SUB_DATE(NOW(), INTERVAL 6 MONTH)')
or perhaps this:
->whereNull('last_sent_at')
->orWhere(DB::raw('DATE(`schedules.last_sent_at`) >= DATE_SUB(NOW(), INTERVAL `schedules.interval`')))
->get();

Related

how to update every row with time/date diff MySQL with different format?

I wanna make some app with feeds/post feature. and my problem is i wanna make it with php to update my timeDiff column in mysql with '2 years ago' or '5 seconds ago' or '1 hour ago' depends on how old the post it is. currently i just have timeDiff using timestampdiff with seconds format.
current table example:
| name | timePost | timeDiff |
|:---- |:------| :-----|
| Bobby | 2020-6-7 10:40:26| 74059449|
| Glenn | 2021-10-11 21:32:11| 39597944|
| Yerch | 2022-10-12 12:45:57| 7118|
query in php:
UPDATE story SET timeDiff = TIMESTAMPDIFF(SECOND, timePost, NOW())
Expected table example:
| name | timePost | timeDiff |
|:---- |:------|:-----|
| Bobby | 2020-6-7 10:40:26| 2 years ago|
| Glenn | 2021-12-11 21:32:11| 11 months ago|
| Yerch | 2022-10-12 12:45:57| 2 hours ago|
any idea how to make it? Any help will be very valuable. Thank you.
You don't want to do that in database. Instead, store created field and use something like this function in your PHP script.

Group By alternative in Laravel 5.4

I have a table Schedule with 3 columns (id, ref_number, pay_date). Each ref_number has a pay_date in every month. So the table looks something like this:
id | ref_number | pay_date
-----------------------------
1 | A001 | 2018-06-29
1 | A001 | 2018-07-29
1 | A002 | 2018-06-30
1 | A002 | 2018-07-30
1 | A002 | 2018-08-30
1 | A003 | 2018-06-29
I want to fetch only the earliest record for every ref_number that have pay_date between today and a date (30 or 31 days from today). The below query works fine in Mysql (I would pass the dates dynamically later).
SELECT id,ref_number,MIN(pay_date) FROM schedule
WHERE (pay_date BETWEEN '2018-06-30' AND '2018-07-30')
GROUP BY ref_number
I know we can turn the mysql "strict" to false in Database config file and the Group By would behave as expected, but without having to change that is there any other way around this problem?
What would be the eloquent equivalent for this query? With or without groupby.
After searching for a while, I came across an answer using nested select statement and gave it a try in Laravel. It worked exactly as I wanted. Here's the sweet piece of code:
Schedule::select(DB::raw(id, ref_number, MIN(pay_date) as pay_date))
->from(DB::raw("(SELECT *
FROM schedule
WHERE (pay_date > CURDATE()))
temp")
)
->groupBy('temp.ref_number')
->get();

query for time in mysql between hours

how to get a list of rows that is between 17:30 till tomarrow 8:30.
--------------------------------------
| id | user_id | action | time |
--------------------------------------
| 1 | 25 | enter | 1512459905
| 2 | 19 | exit | 1512125105
| 3 | 31 | enter | 1514581905 |
--------------------------------------
mysql table have a time column with unix timestamp and i want get a list every day that between 17:30 till 8:30
SELECT TIME_TO_SEC(TIMEDIFF(FROM_UNIXTIME('time 1'), FROM_UNIXTIME('time 2')) AS 'time_diff_in_sec' FROM 'your_table';
This can help:
Mysql Get Time Diff
$dateStart=strtotime(date("Y-m-d 17:30:00"));
$dateEnd=strtotime(date("Y-m-d 20:30:00"));
This is how you can convert your date into unix timestamp in php.
Then in your query:
SELECT * FROM my_table WHERE time BETWEEN '$dateStart' AND '$dateEnd'
If you don't want to execute your script manually every day you can set up a cronjob to do it for you.
You can use the TIME() function to extract the time part of the datatime,
So you can do something like this:
select * FROM table t
where TIME(f.time) between '17:30:00' AND '18:30:00'

MySQL: Get record where TIME('xx:yy:zz') between start and end time

This is the table:
+----+-----------+------------+----------+
| id | id_sensor | start_time | end_time |
+----+-----------+------------+----------+
| 1 | 12 | 21:15:00 | 02:45:00 |
| 2 | 7 | 00:00:00 | 23:15:00 |
| 3 | 5 | 04:30:00 | 16:30:00 |
+----+-----------+------------+----------+
I need to get record(s) where a specific time (e.g. 01:00:00) passed by PHP is between. start_time and end_time are TIME fields in UTC, I'm passing to the query hour via php, note, converted in php from user_timezone to UTC.
SELECT * FROM test WHERE TIME('01:00:00') BETWEEN start_time AND end_time;
Query returns only record id 2, not the 1. I need both, in this case (for id 1, end time ofcourse is next day).
Of course, if we looking for TIME('01:00:00'), we don't need the id 3.
Thank you.
I think this is the logic you want:
SELECT *
FROM test
WHERE (start_time < end_time AND TIME('01:00:00') BETWEEN start_time AND end_time) OR
(start_time > end_time AND TIME('01:00:00') NOT BETWEEN end_time AND start_time);
now() returns both, current time and date. You can also work with curtime(), which returns only the current time.
BTW, i think that working with SELECT * should be avoided (maybe you used it just for this example), it is IMHO always better to list the fields needed explicitly.

MySQL - selecting the correct data within the correct week using week beginnings

I am trying to select data, when inserting the data it has an auto insert of the date when submitting. So when data is inserted it inserts the current date.
However, in my table I have week beginnings, so I am trying to select the data inside of that week:
mysql> select * from week;
+---------+------+------------+
| week_id | week | date |
+---------+------+------------+
| 1 | 1 | 2014-12-29 |
| 2 | 2 | 2015-01-05 |
| 3 | 3 | 2015-01-12 |
| 4 | 4 | 2015-01-19 |
| 5 | 5 | 2015-01-26 |
| 6 | 6 | 2015-02-02 |
| 7 | 7 | 2015-02-09 |
| 8 | 8 | 2015-02-16 |
| 9 | 9 | 2015-02-23 |
| 10 | 10 | 2015-03-02 |
| 11 | 11 | 2015-03-09 |
| 12 | 12 | 2015-03-16 |
| 13 | 13 | 2015-03-23 |
| 14 | 14 | 2015-03-30 |
| 15 | 15 | 2015-04-06 |
| 16 | 16 | 2015-04-13 |
| 17 | 17 | 2015-04-20 |
e.g.
select * from table where date='2015-04-06';
However the data will not be selected and presented because the inserted date was 2015-04-10. The only way to retrieve that data is by doing this:
select * from table where date='2015-04-10'; < when the data was inserted
So my question is, is it possible to select that data from that week beginning?
So if I select data from 2015-04-06 it should show data from the range of 2015-04-06 to 2015-04-12, is that possible?
Hopefully I have explained correctly, been a bit tricky to explain let alone try to implement. I can add any more info if needed.
NOTE: I am trying to use this inside of PHP so where the date is I would just use a variable, just thought I would say.
As the week will always end 6 days from the beginning you can use the between operator and the date_add function like this:
(for your specific example):
select *
from table
where date between '2015-04-06' and date_add('2015-04-06', interval 6 day)
And using a php variable:
select *
from table
where date between '$name_of_dt_var' and date_add('$name_of_dt_var', interval 6 day)
You could also compare the week of the date the data was entered with the weeks in the week table using WEEK() function.
Assuming that week is the same value as week(), the:
select t.*
from table t
where week = week('2015-04-10');
Even if the numbers do not match, then presumably you have some base date (such as 2015-01-01 and simple arithmetic would accomplish something very similar).
I have found that the most robust way to do this sort of week processing is to truncate each date in the table (in your example 2015-04-10) to the preceding Monday at midnight. That way you can compute the week of each item by assigning it to the first day of that week.
This little formula returns the preceding Monday given any DATE or DATETIME value.
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7))
For example,
set #datestamp := '2015-04-10'
SELECT FROM_DAYS(TO_DAYS(#datestamp) -MOD(TO_DAYS(#datestamp) -2, 7))
yields the value 2015-04-06.
So, if you have a table called sale you can add up sales by week like this:
SELECT SUM(amount) weekly_amount,
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7)) week_beginning
FROM sale
GROUP BY FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7))
This is a very convenient way to handle things, because it's robust over end-of-year transitions. The WEEK() function doesn't work quite as well.
If your business rules say that your weeks begin on Sunday rather than Monday, use -1 rather than -2, as follows.
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -1, 7))

Categories