MySQL query date with offset given - mysql

In MySQL I have a table node_weather:
mysql> desc node_weather;
+--------------------+--------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+-------------------+-----------------------------+
| W_id | mediumint(9) | NO | PRI | NULL | auto_increment |
| temperature | int(10) | YES | | NULL | |
| humidity | int(10) | YES | | NULL | |
| time | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
Now what I need to do is the following: for every two hours of the current day (00:00:00, 02:00:00, ..., 24:00:00) I want to get a temperature. Normally the query could be like that:
mysql> SELECT temperature
-> FROM node_weather
-> WHERE date(time) = DATE(NOW())
-> AND TIME(time) IN ('00:00:00','02:00:00','04:00:00','06:00:00','08:00:00','10:00:00','12:00:00','14:00:00','16:00:00','18:00:00','20:00:00','22:00:00','24:00:00');
In the ideal case, I should get a result as 12 rows selected and everything would be fine. But there are two problems with it:
The table does not include the data for thw whole day, so for example the temperature for the time '24:00:00' is missing. In this case, I would like to return NULL.
The table sometimes record the data with the timestamp like '10:00:02' or '09:59:58', but not '10:00:00'. To resolve this case, I would like to add the offset to all the values in IN expression (something like that ('10:00:00' - offset, '10:00:00' + offset)) and it would select always just ONE value (no matter which one) from this range.
I know it is kind of awkard, but that is how my boss wants it. Thanks for help!

Okay, a bit more precise than what I wrote in comments:
EDIT: Had a bug. Hopefully this doesn't.
SELECT
time,
deviation,
hour,
temperature
FROM (
SELECT
time,
ROUND(HOUR(time) / 2) * 2 AS hour,
IF(HOUR(time) % 2,
3600 - MINUTE(time) * 60 - SECOND(time),
MINUTE(time) * 60 + SECOND(time)
) AS deviation,
temperature
FROM node_weather
WHERE DATE(time) = DATE(NOW())
ORDER BY deviation ASC
) t
GROUP BY hour
ORDER BY
hour ASC
Basically, group on intervals like 09:00:00 - 10:59:59 (by rounding hour/2), then sort ascending by those intervals, and within the interval by the distance to the center of the interval (so we choose 10:00:00 over 09:00:00 or 10:59:59).

Related

Group by day using the timestamp field

My table schema looks like this:
+--------------------+--------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+-------------------+-----------------------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(50 | NO | | 0 | |
| modified | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| created | timestamp | NO | | CURRENT_TIMESTAMP | |
+--------------------+--------------+------+-----+-------------------+-----------------------------+
I want to get a count of the names and group by number of names modified by day at the moment I can only group by the full date including the timestamp eg:
SELECT name, count(*) FROM mytable GROUP BY modified
Thanks in advance.
Use MySQL DATE() function to extract the date from the timestamp:
SELECT name, count(*) FROM mytable GROUP BY DATE(mytable.modified);
The DATE() function extracts the date value from a date or datetime expression.
Dependant upon what you mean by day, you have a few options;
Here's your cheat sheet.
DAYNAME(date) for the day of the week (Mon-Sun)
DAYOFMONTH(date) for the day of the month (1-31)
DAYOFWEEK(date) for the day of the week (1-7)
DAYOFYEAR(date) for the day of the year (1-365)
Edit:
If you want to group by the entire date (as opposed to a particular day), ignoring the time you can use
DATE_FORMAT(date, format)
The full list of format specifiers can be found at the above link, but what you'll probably need is:
Date_FORMAT(date, '%Y-%m-%d')
This will format the date as 'YYYY-MM-DD' and you can group by that.
You can use this query or a version of it
SELECT DATE_FORMAT(modified,"%Y-%m-%d") as date_string, count(1) FROM mytable group by date_string;

How to add random interval to timestamp in MySQL?

We got the following table mytable:
+----+------------+------------+
| id | created | expired |
+----+------------+------------+
| 1 | 1496476314 | NULL |
| 6 | 1496477511 | NULL |
| 7 | 1496477518 | NULL |
| 12 | 1496477534 | NULL |
| 13 | 1496477536 | NULL |
| 15 | 1496477541 | NULL |
| 21 | 1496477548 | NULL |
| 22 | 1496477550 | NULL |
| 26 | 1496477565 | NULL |
| 28 | 1496477566 | NULL |
| 29 | 1496477583 | NULL |
+----+------------+------------+
We'd like to do the following:
set expired = created + random(15 - 30 minutes) as unix_timestamp where expired is null;
I currently have no idea to done it.
If u just can give me some ideas it would save my day.
I tried to convert the created timestamp to date_time and add to that date_time the wanted 15 - 30 minutes and finally convert the new_date_time back to unix_timestamp, but there should be an easier way.
If you want to add a random number of minutes between, say, 14 and 33, you can do it like this:
SET expired = DATE_ADD(created, INTERVAL 14 + RAND()*(33-14) MINUTE);
If you want to have seconds granularity, you need to add SECOND-typed intervals:
SET expired = DATE_ADD(created, INTERVAL 14*60 + RAND()*(33-14)*60 SECOND);
This would saves one datetime conversion if you had a DATETIME for the expired column, which makes it slightly easier to expire records (WHERE expired < NOW()). If you have an integer holding a Unix timestamp, then Darshan's answer is definitely the way to go, and you'd do well to calculate the Unix timestamp in your app and then plug it in the query:
WHERE expired <= 123456789
Having an index on that column would make expirations go blazingly fast. I think it might be even faster than the datetime method, but it's just a sensation, I haven't actually checked.
unix_timestamp is number of seconds elapsed since 1st January 1970. Now, if you want to add 15 to 30 minutes then the equivalent seconds would be 900 to 1800. Here's what you can do:
set expired = created + ROUND((RAND() * (900))+900) where expired is null;
This is how it works:
RAND() will generate a random number between 0 and 1
By using RAND() * (maximum - minimum)) + minimum we make sure we generate a number between 900 and 1800.
ROUND then rounds that number to nearest int.

MySQL doesn't use indexes in a SELECT clause subquery

I have an "events" table
table events
id (pk, auto inc, unsigned int)
field1,
field2,
...
date DATETIME (indexed)
I am trying to analyse holes in the trafic (the moments where there is 0 event in a day)
I try this kind of request
SELECT
e1.date AS date1,
(
SELECT date
FROM events AS e2
WHERE e2.date > e1.date
LIMIT 1
) AS date2
FROM events AS e1
WHERE e1.date > NOW() -INTERVAL 10 DAY
It takes a very huge amount of time
Here is the explain
+----+--------------------+-------+-------+---------------------+---------------------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-------+---------------------+---------------------+---------+------+----------+-------------+
| 1 | PRIMARY | t1 | range | DATE | DATE | 6 | NULL | 1 | Using where |
| 2 | DEPENDENT SUBQUERY | t2 | ALL | DATE | NULL | NULL | NULL | 58678524 | Using where |
+----+--------------------+-------+-------+---------------------+---------------------+---------+------+----------+-------------+
2 rows in set (0.00 sec)
Tested on MySQL 5.5
Why can't mysql use the DATE indexe? is it because of a subquery?
Your query suffers from the problem shown here which also presents a quick solution with temp tables. That is a mysql forum page, all of which I unearthed thru finding this Stackoverflow question.
You may find that the creation and populating such a new table on the fly yields bearable performance and is easy to implement with the range of datetimes now() less 10 days.
If you need assistance in crafting anything, let me know. I will see if I can help.
You are looking for dates with no events?
First build a table Days with all possible dates (dy). This will give you the uneventful days:
SELECT dy
FROM Days
WHERE NOT EXISTS ( SELECT * FROM events
WHERE date >= days.day
AND date < days.day + INTERVAL 1 DAY )
AND dy > NOW() -INTERVAL 10 DAY
Please note that 5.6 has some optimizations in this general area.

Where clause containing date in MySQL statement not working

Table Name: DemoTable.
Total Fields: 2
Fields:
id (int, auto increment, primary key)
month_and_year (varchar(10))
month_and_year contains date as '2015-03', '2015-01', '2014-12' and so on...
I am trying to get values from the table between '2014-10' and '2015-03'.
SELECT * FROM DemoTable where month_and_year>='2014-10' AND month_and_year<='2015-03' ORDER BY month_and_year DESC
Query does not give desired output as month_and_year field has varchar data type. Changing varchar to date data type isn't possible as date data type does not accept date in 'yyyy-mm' format.
How can the result be obtained?
PS:Is UNIX_TIMESTAMP() a safe bet in this case?
You should never store date value as varchar and choose mysql native date related data types like date,datetime or timestamp
However in your case you need to do some date related calculations before doing the select query. Consider the following table
mysql> select * from test ;
+------+----------------+
| id | month_and_year |
+------+----------------+
| 1 | 2014-10 |
| 2 | 2014-10 |
| 3 | 2014-09 |
| 4 | 2014-11 |
| 5 | 2015-01 |
| 6 | 2014-08 |
+------+----------------+
Now the approach would as
First convert the varchar to real date
Then for the lower limit always start the comparison from first day of the year month value
The upper limit will be till the end of the month.
So the query becomes
select * from test
where
date_format(
str_to_date(
month_and_year,'%Y-%m'
),'%Y-%m-01'
)
>=
date_format(
str_to_date('2014-10','%Y-%m'
),'%Y-%m-01'
)
and
last_day(
date_format(
str_to_date(month_and_year,'%Y-%m'
),'%Y-%m-01'
)
)
<=
last_day(
date_format(
str_to_date('2015-03','%Y-%m'
),'%Y-%m-01'
)
);
The output will be as
+------+----------------+
| id | month_and_year |
+------+----------------+
| 1 | 2014-10 |
| 2 | 2014-10 |
| 4 | 2014-11 |
| 5 | 2015-01 |
+------+----------------+
Use the function STR_TO_DATE(string,format);
http://www.mysqltutorial.org/mysql-str_to_date/
You should use either mysql date time functions or use int field in mysql and store UNIXTIMESTAMP and compare like you are already doing. I think it is overkill to store unixtimestamp because you only need month and year and you won't benefit a lot from unixtimestamp advantages.

MySQL check if two date range overlap with input

I need to check if two dates over lap with another two dates in my database.
My database looks like this
+----+--------------+------------+------------+
| id | code | StartDate | EndDate |
+----+--------------+------------+------------+
| 1 | KUVX-21-40 | 2013-10-23 | 2013-11-22 |
| 2 | UEXA286-1273 | 2013-10-30 | 2013-11-29 |
| 3 | UAJFAU-2817 | 2013-10-21 | 2013-11-20 |
| 4 | KUVX-21-40 | 2013-10-30 | 2013-11-29 |
+----+--------------+------------+------------+
In my query i specify the scope: A start date and an enddate
Lets asign them as follows:
ScopeStartDate = "2013-10-1"
ScopeEndDate = "2013-11-26"
Above should return me all of the records, since the all overlapse the timespan.
However I cannot get a query working :/
I've tried the following query with no luck:
WHERE
(
(StartDate < ScopeStartDate AND StartDate > ScopeStartDate)
OR
(StartDate > ScopeStartDate AND EndDate < ScopeEndDate )
)
This returns me two results:
1 and 3
what am I doing wrong?
I believe the following condition matches every possible overlapping case.
WHERE
(
(ScopeStartDate <= EndDate AND ScopeEndDate >= StartDate)
)
except if you declare illogic timespans (for example, those which end before starting)
This is an old thread, but use BETWEEN. This is an excerpt from my timeclock, pls modify to your needs...
$qs = "SELECT COUNT(*) AS `count` FROM `timeclock` WHERE `userid` = :userid
AND (
(`timein` BETWEEN :timein AND :timeout OR `timeout` BETWEEN :timein AND :timeout )
OR
(:timein BETWEEN `timein` AND `timeout` OR :timeout BETWEEN `timein` AND `timeout`)
);";
This is just the where clause. Given InputStartDate and InputEndDate are given by user input, and DataStartDate and DataEndDate are the datetime values in the table:
where ((DataEndDate > InputStartDate) and (DataStartDate < InputEndDate))
You can cover all date overlapping cases even also when toDate in database can possibly be null as follows:
SELECT * FROM `tableName` t
WHERE t.`startDate` <= $toDate
AND (t.`endDate` IS NULL OR t.`endDate` >= $startDate);
This will return all records that overlaps with the new start/end dates in anyway.