I have a table called reports in MySQL(MariaDB) . There is a one(out of 5) column named logdate which is is of type datetime .columns stores the the date and time (in 24hr format) .
for ex here is sample value from that column
2021-04-10 09:35:00
I have to find all reports between a given date and time .
I get 4 variables from form data in PHP
$fromdate= $_POST['fromdate'];
$todate= $_POST['todate'];
$fromtime= $_POST['fromtime'];
$totime= $_POST['totime'];
$fromtime and $totime are just integers with value from 0-23 for hours.
For example the condition may be like get all data between 4th April 2021
from 5 o'clock To 8 April 2021 18 o'clock
i.e. From 2021-04-04 03:00:00 to 2021-04-08 18:00:00. There will be never condition on minutes and seconds .
My question is how to construct a datetime in PHP compatible with MySQL types so I can have good(efficient, there are millions of records in table ) search speed?
for ex
$select = "select * from reports where logdate between ? and ? ";
P.S: I tried saving date and time as integer as unixtime stamp. But when i convert from and to date received using strttotime() I facing time format issue due to bug in my code which so can use datetime only.
If you have any suggestion to improve efficiency of DB please suggest.Thanks
Hi this link may be of help in optimizing date comparison
MySQL SELECT WHERE datetime matches day (and not necessarily time)
This one below, will help you in formatting your strtotime() by using strptime()
https://www.php.net/manual/en/function.strptime.php
Also check your spelling or typo; you wrote "strttotime()" instead of "strtotime()" yours has an extra 't' in str"tto"time, it should be str"to"time, though without the double qoutes
Though I can't say for sure this is the most effective way but you can use hour(logdate) to compare with $fromdate and $todate
$select = "select * from reports where hour(logdate) between ? and ? ";
But it will only compare hour part. Please mention how you are getting date part to compare?
It is not a good idea to make a calculation on a field in the WHERE CLAUSE. In this case MySQL / MariaDB must calculate the value from this field to comapare it to see
if this ROW has this condition. So MySQL must read the whole table FULL TABLE SCAN and CANT use any INDEX.
A better way to do this is to store the calculation on fix site. Then MySQL calculated it only one time and can use a Index ( if there one) .
you can easy use a query like this:
$select = "SELECT * FROM reports where logdate between date(?) + INTERVAL ? HOUR AND date(?) + INTERVAL ? HOUR ";
to test see:
SELECT date('2021-04-05') + INTERVAL 16 HOUR;
result:
2021-04-05 16:00:00
Here is what is working for me after using Bernds solution .
I constructing datetime string in php
$fromstr ="$fromdate"." "."$fromtime".":00:00";
$tostr="$todate"." "."$totime".":00:00";
here is my query looks like for date of 7th April to 10th April
$ select = "SELECT * FROM reports where logdate >= '$fromstr' and logdate <= '$tostr' order by logdate";
after echoing it
"SELECT * FROM reports where logdate >= '2021-04-07 3:00:00' and logdate <= '2021-04-10 5:00:00' order by logdate";```
However I am not sure if can use index for logdate column and utilize it with above query.
Related
how to search between two date , when date format in database like :
2015-10-10 02:23:41 am
i just want to search between two date with format :
2015-10-10
without
02:23:41 am
any ideas please ?
Your question isn't completely clear. But, I guess you hope to find all the rows in your table where Date occurs on or after midnight on 2015-08-05 and before midnight on 2015-09-11 (the day after the end of the range you gave in your question.
Those two search criteria will find all the rows with Date values in the range you specified. (I'm ignoring the 02 at the end of 2015-09-10 02 in your question because I can't guess what it means, if anything.)
Try this query:
SELECT *
FROM table
WHERE `Date` >= '2015-08-05'
AND `Date` < '2015-09-10' + INTERVAL 1 DAY
This has the benefit that it can exploit an index on the Date column if you have one, so it can be fast.
You could write
SELECT *
FROM table /* slow! */
WHERE DATE(`Date`) BETWEEN '2015-08-05' AND '2015-09-10'
That's slightly easier to read, but the WHERE condition isn't sargable, so the query will be slower.
Notice that the beginning of the range uses >= -- on or after midnight, and the end of the range uses < -- before midnight.
Pro tip: Avoid the use of reserved words like DATE for column names. If you make mistakes writing your queries, their presence can really confuse MySQL, not to mention you, and slow you down.
May I suggest:
select * from table where cast(date as date) between '2015-08-05' and '2015-09-10'
When your where clause is based on a timestamp, but you're using date as the parameters for your between, it excludes anything that happens on the second date unless it happened precisely at midnight.
When using the end date for the range, include the time of the end of the day:
SELECT *
FROM YourTable
WHERE date BETWEEN '2015-08-05' AND '2015-09-10 23:59:59'
I want to compare a datetime from sql and the todays date.
The datetime in sql is for example 2015/05/09 12:00:00 and the current date is 2015/05/09. I want to compare these things if they are equal, but for the whole day so the time is a problem. I have the following query.
$q1="SELECT reservations.reservation_id, reservations.room_id, room.room_rate, reservations.arrival_date, reservations.departure_date,
meals.meal_rate, room.room_rate, roomtype.max_persons,
CONCAT(clients.first_name,' ',clients.last_name)as name
FROM reservations, clients, meals, room, roomtype
WHERE reservations.room_id=room.room_id AND reservations.client_id=clients.client_id AND room.roomtype_id=roomtype.roomtype_id
AND reservations.meals=meals.meal_id AND reservations.arrival_date=CURDATE()
GROUP BY reservation_id
";
reservations.arrival_date=CURDATE() this is equal only at 12:00:00 o'clock. i want that to be equal for the whole day.Actually i want to compare if these two dates are equal without time but my database must be datetime with the time...
does anyone has an idea?
thanx in advance
You should learn to use standard join syntax. Simple rule: Never use commas in the from clause.
The answer to your question is either the date() function (or something similar):
where date(reservations.arrival_date) = CURDATE()
Or an inequality:
where (reservations.arrival_date >= CURDATE() and
reservations.arrival_date < date_add(CURDATE(), interval 1 day)
)
The second is actually preferred because it can make use of an index on reservations(arrival_date).
Hi there please help me if you can. Here is my senario:
I have a MySQL database with a column that holds a date in the form of a varchar. The format of the date is the following 29/05/2014 (i.e. d/m/Y).
I'm trying to compare the value of this column with todays date and return any rows where the date is earlier than todays date.
I'm using a php variable to store todays as follows:
$date = date("d/m/Y");
Here is my SQL query:
SELECT * FROM patients WHERE last_seen < '$date'
What gets returned
So what is returned is very unusual (to me). All records where the last_seen "day" is less than todays "day". It seems to be overlooking the month and year. So in other words if I last_seen = "30/05/2014" and todays date is "29/05/2014" this record is still returned.
Does anyone have any ideas what I might be doing wrong here?
Thanks
You really, really shouldn't store dates in a varchar field - use date or datetime or timestamp data type.
That said, sometimes you don't have control over the database and you have to deal with somebody else's bad design decision. In this case, to compare dates, convert the varchar strings to dates and compare them that way. So, in your case, you can have something like this:
$date = date("d/m/Y");
and then
SELECT * FROM patients WHERE str_to_date('last_seen', '%d/%m/%Y') < str_to_date('$date', '%d/%m/%Y')
or simpler
SELECT * FROM patients WHERE date(last_seen) < current_date
This way you are actually comparing dates and not strings containing dates. Naturally, this assumes that all dates are stored in the same format.
EDIT: I just tested the last option - and, apparently, date('30/05/2014') returns NULL on my system (mysql 5.5 on linux), hence I suggest the best way is
SELECT * FROM patients WHERE str_to_date('last_seen', '%d/%m/%Y') < current_date
You need to store your date as DATE or DATETIME in your database.
Then you can use:
SELECT * FROM patients WHERE DATE(last_seen) < CURRENT_DATE
I'm setting up a script to run daily and check for members who meet a certain age - automated emails can be set up in a CMS and assigned to be sent at any age, either in months or years. To handle this via PHP and MySQL, the number of months is passed as a parameter to a method, which I deal with as below. However, I'm not sure I'm going about this in the easiest way! Partly because of the formatting of the UK date format, I'm converting from string to datetime to unix timestamp to make the comparison.
Can anyone find a better way of going about this? Thanks
// If num of months provided is a year, make calculation based on exact year
if ($age_in_months % 12 == 0)
{
// Using 365 days here (60 * 60 * 24 * 365 = 3153600)
$clause = 'WHERE ROUND((UNIX_TIMESTAMP() - UNIX_TIMESTAMP(STR_TO_DATE(dob, "%d/%m/%Y"))) / 31536000) = ' . $age_in_months;
}
else
{
// Using 30 days as avg month length (60 * 60 * 24 = 86400) - convert months to days
$clause = 'WHERE ROUND((UNIX_TIMESTAMP() - UNIX_TIMESTAMP(STR_TO_DATE(dob, "%d/%m/%Y"))) / 86400) = ' . $age_in_months * 30;
}
change that column to type date or datetime
don't use UK date format, use ISO-8601 format
and index on that column
I'd just use the MySQL date logic. You can be sneaky and use the fact that the DOB column is stored as text this lie this:
SELECT whatever FROM users WHERE dob LIKE DATE_FORMAT(CURDATE(), '%d/%m/%%');
This will take the current date, format it like a UK date (the %% turns into a single %). So for today (as I post), that would be '20/12/%'. It uses that for a LIKE on dob, giving you everyone with a birthday of '20/12/(something)'.
It's a little weird, but it actually takes advantage of having the DOB stored in a text format. I'm assuming an index on DOB, although you could get away without it if you don't have too many people.
For the second query, it looks like you're trying to do a 'People who were born 6 months ago' type thing. Is that right? You could so something like this:
SELECT whatever FROM users WHERE DATE_ADD(CURDATE(), INTERVAL -6 MONTH) = STR_TO_DATE(dob, '%d/%m/%Y');
It's not a pretty, and if you want people who were born 6, 18, 30... months ago it won't work. In that case, I'd actually go with something like what you have. It's not ideal, but it would more-or-less work and the results may be close enough for you.
datediff(now(),dob) will give you the difference in days between two dates.
If you want to see whether somebody is at least 18, try if (date_sub(now(),18 years) > dob)
Everybody born in February? if (month(dob) = 2)
See http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html
Edit: and since you're stuck with (for unfathomable reasons) a database showing a date that's not in date format, replace dob with your STR_TO_DATE(dob... section.
I'm trying to request any records from a table that appear on or after today's date, using a single query.
All the dates for each record are stored in separate columns e.g.. - one for month, one for year, and one for day.
obviously i've been getting all records that occur after the year in today's date
$sql = "SELECT * FROM table WHERE year>=".$year_today."
ORDER BY year, month, day";
Then i've been trying to filter that down a bit, by using:
$sql = "SELECT * FROM table
WHERE year>=".$year_today." && month>=".$month_today."
&& day>=".$day_today."
ORDER BY year, month, day";
And in order to test it, i created a record in the database with yesterdays date, yet, this record still appears in the list of returned records. What am i doing wrong? :(
This can be achieved using time functions in a nice way, while it's better when using a DATE column.
SELECT * FROM TABLE WHERE TIMESTAMP(CONCAT(year,"-",month,"-",day)) >= CURDATE()
ok, that's evil as it doesn't use an index ... proper thing would be a DATE column, but doing all this by hand is annoying asyou also have to consider the case where year is bigger but months is smaller and stuff ....
I don't know if you can use "&&" instead of "AND" in your context - maybe try changing your SQL to use
" AND month>=".$month_today." AND day>=".$day_today."
EDIT :
as an extension to Nick's answer, to make the sql behave correctly you could code :
$sql = "SELECT * FROM table WHERE (
(year>".$year_today.")
OR (year=".$year_today." && month>".$month_today." )
OR (year=".$year_today." && month=".$month_today."&& day>=".$day_today.")
)
ORDER BY year, month, day";
...but this starts to get a whole lot messier than converting to dates and using date comparison
EDIT2:
but then again, if these columns are indexed, the indexes might be used. It's still a lot more effort to code SQL this way if performance using conversion to dates is totally acceptable.
To compare dates with dates:
SELECT * FROM table WHERE
STR_TO_DATE(
CONCAT(year
, '-'
, right(concat('00', month), 2)
, '-'
, right(concat('00', day), 2))
, '%Y-%m-%d')
>= STR_TO_DATE('2009-12-11', '%Y-%m-%d')
to check if a date is after another date this query will fail:
$sql = "SELECT * FROM table WHERE year>=".$year_today." && month>=".$month_today." && day>=".$day_today." ORDER BY year, month, day";
day 12 march of februari is smaller then day 15 in februari, but is still after it, because it's in a different month....