MySQL check if two date range overlap with input - mysql

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.

Related

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.

UNIX Timestamp in future mysql

I have a table that holds a few UNIX timestamps that are in the future, I need to find the ones that are older than NOW() or are within 10 minutes of being in the past.
Here's the query I have tried using to no avail
SELECT
TIME_TO_SEC(TIMEDIFF(NOW(), FROM_UNIXTIME(`expires`))) AS `time_diff`
FROM
`video_table`
WHERE
time_diff < NOW() OR time_diff > '-600'
Can anyone point me in the direction as to why this will not work?
The question is not too clear, but I'm trying to answer with a few scenarios.
This query will return all records already expired:
SELECT * FROM video_table WHERE expires < UNIX_TIMESTAMP()
And this will return all records that are about to expire (they will expire within 10 minutes):
SELECT * FROM video_table
WHERE expires>=UNIX_TIMESTAMP() AND expires<UNIX_TIMESTAMP() + 600
This will return all records in the future (they will expire later than 10 minutes from now):
SELECT * FROM video_table WHERE expires>UNIX_TIMESTAMP() + 600
time_diff will give you the difference between NOW() and your expires column. What you want is to actually check if NOW()<expires (time_diff < 0) or if it's (NOW()+600s < expires) (time_diff < 600). So it's enough to check the latter:
SELECT
TIME_TO_SEC(TIMEDIFF(NOW(), FROM_UNIXTIME(`expires`))) AS `time_diff`
FROM
`video_table`
HAVING
`time_diff` < 600
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,expires INT NOT NULL);
INSERT INTO my_table(expires) VALUES
(1442075399),
(1442075499),
(1442075599),
(1442075699),
(1442076106),
(1442076206),
(1442076306),
(1442076406);
SELECT UNIX_TIMESTAMP();
+------------------+
| UNIX_TIMESTAMP() |
+------------------+
| 1442075727 |
+------------------+
SELECT * FROM my_table WHERE expires - 600 < UNIX_TIMESTAMP();
+----+------------+
| id | expires |
+----+------------+
| 1 | 1442075399 |
| 2 | 1442075499 |
| 3 | 1442075599 |
| 4 | 1442075699 |
| 5 | 1442076106 |
| 6 | 1442076206 |
| 7 | 1442076306 |
+----+------------+
You can use DATE_ADD to get a time in the future:
SELECT UNIX_TIMESTAMP() - `expires` AS `time_diff`
FROM `video_table`
WHERE expires < UNIX_TIMESTAMP(DATE_ADD(NOW(), INTERVAL 10 MINUTE))
I am not quite sure I understand what you are trying to do, if you create a variable that will give the present time and than run your query as a less than or equal to clause it should work.
Here is an example
$t=time();
SELECT * FROM video_table
WHERE column_holding_unix_time<=$t;
I hope this helps

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.

Issue with Group by Mysql query

I have been stumped by this MySQL query for the past few day,and seeking some expert help before i throw my PC out of the window, lol.
I have a table that consists of invoices; invoice number column name is Trans_ref$$ (primary key).
each invoice is linked to a job , column name is OPSREF$$_ORIGINAL (foreign key).
Both columns are in the same table.
a job can have multiple invoices.
Now the issue is i'm trying to create a query that will identify the FIRST invoice in sequence (Trans_ref$$) in a job group and if that FIRST invoice is with a date range then output the job (OPSREF$$_ORIGINAL).
The invoice number is sequential and a unique number.
I have tried using a case / min group by statement to identify the first invoice in a job group within a data range, however this query is not isolating the first invoice within a date range, instead it is just outputting 1st or 2nd or 3rd positioned invoice in job group that matches the date range….I hope this makes sense .
I have included a screen shot of the table and the desired output, and would appreciate if someone
Hello
I have been stumped by this query for the past day,and seeking some expert help before i throw my PC out of the window, lol.
I have a table that consists of invoices; invoice number column name is Trans_ref$$ (primary key).
each invoice is linked to a job , column name is OPSREF$$_ORIGINAL (foreign key).
Both columns are in the same table.
a job can have multiple invoice.
Now the issue is i'm trying to create a query that will identify the FIRST invoice (Trans_ref$$) in a job group and if that FIRST invoice is with a date range then output the job (OPSREF$$_ORIGINAL).
The invoice number in a sequential / unique number.
I have tried using a case / min group by statement to identify the first invoice in a job group within a data range, however this query is not isolating the first invoice within a date range, instead it is just outputting 1st or 2nd or 3rd positioned invoice in job group that matches the date range….I hope this makes sense .
Here the code I have used
SET #START_DATE := "2014-08-27";
SET #END_DATE:= "2014-09-02";
select
count(TRANS_REF$$),
Case
when min(TRANS_REF$$) and INVOICE_DATE_D8>=#START_DATE and INVOICE_DATE_D8<=#END_DATE
then OPSREF$$_ORIGINAL
end as OPSREF$$_ORIGINAL
from INVOICE_HEADER_UNS
where
TRANSACTION_STATUS = 9
and Tmode$$="06"
group by OPSREF$$_ORIGINAL
So to breakdown the process in stages.....
1. grab each OPSREF$$_ORIGINAL group within the INVOICE_HEADER_UNS table
Date Range - Start 27/08/2014 - End 02/09/2014
TABLE - INVOICE_HEADER_UNS
TRANS_REF$$ INVOICE_DATE_D8 OPSREF$$_ORIGINAL
---------------------------------------------
| 1 | 26/08/2014 | ABC |
| 2 | 02/08/2014 | ABC |
---------------------------------------------
| 3 | 28/08/2014 | DDD |
| 4 | 09/09/2014 | DDD |
---------------------------------------------
| 5 | 01/01/2013 | JJJ |
| 6 | 21/12/2013 | JJJ |
---------------------------------------------
| 7 | 01/09/2014 | LLL |
---------------------------------------------
2. within each OPSREF$$_ORIGINAL group, look at TRANS_REF$$ sequence and find the first TRANS_REF$$ in that OPSREF$$_ORIGINAL group.
Date Range - Start 27/08/2014 - End 02/09/2014
TABLE - INVOICE_HEADER_UNS
TRANS_REF$$ INVOICE_DATE_D8 OPSREF$$_ORIGINAL Is First inv?
--------------------------------------------------------
| 1 | 26/08/2014 | ABC | yes
| 2 | 02/08/2014 | ABC | no
--------------------------------------------------------
| 3 | 28/08/2014 | DDD | yes
| 4 | 09/09/2014 | DDD | no
---------------------------------------------------------
| 5 | 01/01/2013 | JJJ | yes
| 6 | 21/12/2013 | JJJ | no
---------------------------------------------------------
| 7 | 01/09/2014 | LLL | yes
---------------------------------------------------------
3. Look at the the FIRST INVOICE in the ordered sequence for the group and check the INVOICE_DATE_D8 for that row and see if it is within date range ( 27/08/2014 - 02/09/2014 )
Date Range - Start 27/08/2014 - End 02/09/2014
TABLE - INVOICE_HEADER_UNS
TRANS_REF$$ INVOICE_DATE_D8 OPSREF$$_ORIGINAL Is First inv? In date range?
--------------------------------------------------------
| 1 | 26/08/2014 | ABC | yes no
--------------------------------------------------------
| 3 | 28/08/2014 | DDD | yes yes
---------------------------------------------------------
| 5 | 01/01/2013 | JJJ | yes no
---------------------------------------------------------
| 7 | 01/09/2014 | LLL | yes yes
4. Does each row meet the criteria (i.e. first invoice and in date - YES + YES) ? if correct then output the distinct OPSREF$$_ORIGINAL for that row
Here is the desired output from the example above
Date Range - Start 27/08/2014 - End 02/09/2014
TABLE - INVOICE_HEADER_UNS
OPSREF$$_ORIGINAL Is First inv? In date range?
------------------------
| DDD | yes yes
------------------------
| LLL | yes yes
------------------------
Based on this, can you suggest how I can do this? thanks
Here is the Table definition;
tablename - INVOICE_HEADER_UNS
**TRANS_REF$$** char(7) - Primary key
INVOICE_DATE_D8 datatime
OPSREF$$_ORIGINAL char(11)
16/09/2014 - Here is the query that appears to be returning the correct results using DRapp syntax, however the query needs to be optimised somehow as when I bolt this onto the bigger query as a sub query it causes my MYSQL workbench to crash. After doing an explain on the query , it appears to be indexing through 28,309 rows. How can i optimise this query without impacting the results?
I have tried adding the below to the where clause however it adds more records to the overall query output, but indexes 979 rows instead/
set #START_DATE := '2014-08-27';
set #END_DATE := '2014-09-02';
SELECT
if (min(ihu.INVOICE_DATE_D8)>= #START_DATE and min(ihu.INVOICE_DATE_D8)<= #END_DATE ihu.opsref$$_original, null) as firstinvoiceindaterange
from
INVOICE_HEADER_UNS ihu
join CONSIGNMENT_ALL_HEADER_UNS on ihu.OUR_REF = CONSIGNMENT_ALL_HEADER_UNS.OPSREF$$
where ihu.TRANSACTION_STATUS = 9 and ihu.Tmode$$="06"
AND CONSIGNMENT_ALL_HEADER_UNS.CONS_TYPE$$ <>'7'
AND CONSIGNMENT_ALL_HEADER_UNS.CONS_TYPE$$ in ('3' , '5', '9')
#and ihu.INVOICE_DATE_D8>= #START_DATE and ihu.INVOICE_DATE_D8<= #END_DATE
group by
ihu.opsref$$_original
I think this should help you out, and you can tweak it as needed.
SELECT
ihu.opsref$$_original,
MIN( ihu.TRANS_REF$$ ) MinRefForEntireJob,
MIN( ihu.INVOICE_DATE_D8 ) MinDateForEntireJob,
MIN( IF( ihu.INVOICE_DATE_D8 >= #START_DATE
AND ihu.INVOICE_DATE_D8 <= #END_DATE, ihu.TRANS_REF$$, null )) as MinRefWithinDateRange,
MIN( IF( ihu.INVOICE_DATE_D8 >= #START_DATE
AND ihu.INVOICE_DATE_D8 <= #END_DATE, ihu.INVOICE_DATE_D8, null )) as MinDateWithinDateRange
from
INVOICE_HEADER_UNS ihu,
( select #START_DATE := '2014-08-27',
#END_DATE := '2014-09-02' ) sqlvars
group by
ihu.opsref$$_original
Once you see these results, then you SHOULD be able to just add a HAVING clause something like
HAVING
MinDateWithinDateRange IS NOT NULL

MySQL query date with offset given

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).