Get records between Two Dates with overlapping time intervals - mysql

I have the following database
CREATE TABLE `table` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`time` bigint(20) DEFAULT NULL,
`name` varchar(20) DEFAULT NULL,
`messages` varchar(2000) NOT NULL,
PRIMARY KEY (`id`)
)
INSERT INTO `table` VALUES (1,1467311473,"Jim", "Jim wants a book"),
(2,1467226792,"Tyler", "Tyler wants a book"),
(3,1467336672,"Phil", "Phil wants a book");
I need to get the records between date 29 Jun 2016 and 1 July 2016 for time intervals 18:59:52 to 01:31:12.
I wrote a query but it doesn't return the desired output
SELECT l.*
FROM table l
WHERE ((time >=1467226792) AND (CAST(FROM_UNIXTIME(time/1000) as time) >= '18:59:52') AND (CAST(FROM_UNIXTIME(time/1000) as time) <= '01:31:12') AND (time <=1467336672))
Any suggestions??

As I understand it, you're simply interested in all periods greater than '2016-06-29 18:59:52' and less than '2016-07-01 01:31:12' where the time element is NOT between '01:31:12' and '18:59:52'
I think you can turn that logic into sql without further assistance
Ah, well, here's a fiddle - left out all the from_unixtime() stuff because it adds unnecessary complication to an understanding of the problem - but adapting this solution to your needs is literally just a case of preceding each instance of the column time with that function:
http://rextester.com/OOGWB23993

If i got it right
SELECT l.*
FROM `table` l
WHERE time >=1467226792
AND time <=1467336672
AND CAST(FROM_UNIXTIME(time/1000) as time) >= '18:59:52'
AND FROM_UNIXTIME(time/1000) <= DATE_ADD(DATE_ADD(DATE_ADD(CAST(FROM_UNIXTIME(time/1000) as date), INTERVAL 25 HOUR), INTERVAL 31 MINUTE), INTERVAL 12 SECOND)

Related

Mysql select current time between two columns

I have a schedule table in which I have two columns time_from and time_to with values in the 24 hour format like if the from and to are 9 Pm to 1am than the time_from and time_to would be 21 and 1 respectively.
Now I need to check if the current time falls between these two columns
If the time is 11 Pm now I should get the column whose in which column value in between 11 Pm that is 23 and 1
CREATE TABLE `ace_rp_inventory_locations_night` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`time_from` varchar(255) DEFAULT NULL,
`time_to` varchar(255) DEFAULT NULL
);
INSERT INTO ace_rp_inventory_locations_night (id, name, time_from, time_to)
VALUES (1, 'test', '23','1');
please check sql fiddle
this query works
SELECT *
FROM `ace_rp_inventory_locations_night`
WHERE time_from_1 <= '23'
AND time_to_1 >= '23'
if the time is in 24 Hours range that is 20 and 24 but the value for 1am is 1 and the above query does not work because 23 and 1 does not full fill the condition I am checking it with the current time so if the time is 2am I have to pass 2 in the query.
How can I use the current time and use it to query between two columns?
You can use:
SELECT *
FROM `ace_rp_inventory_locations_night`
WHERE (time_from_1 <= time_to_1 AND 23 between time_from_1 and time_to_1) OR
(time_to_1 > time_to_1 AND 23 NOT BETWEEN time_from_1 and time_to_1);
Note: This uses BETWEEN which affect how the end points of the ranges are handled. Your question doesn't specify what to do. You should probably be explicit about the inequalities.

What's wrong with this simple MySQL syntax? Summing the dates

Among the rest, I've got three columns in my table:
start- timestamp, the default value is CURRENT_TIMESTAMP
duration- datetime, usually 0000-00-07 00:00:00 (one week)
end - timestamp, the default value is 0000-00-00 00:00:00
Here's what I do:
UPDATE `banners` SET `end` = `start` + `duration` WHERE `id` = 93
No errors appear, the id is exact - but the operation doesn't execute, the end field just remains at zeros.
What's wrong? Any quotes, brackets needed? I also tried making the middle field the timestamp type as well with no result.
Very possible, just a little ugly in terms of code...
UPDATE `banners`
SET `end` = FROM_UNIXTIME(UNIX_TIMESTAMP(`start`) + (UNIX_TIMESTAMP(`duration`) - UNIX_TIMESTAMP('1970-01-01 00:00:00')),'%Y-%d-%m %h:%i')
WHERE `id` = 93
...you just need to convert everything to seconds, add the duration from teh second one and then convert back to a datetime string for setting :)
You cannot add DATETIME values the same way you add numbers. What's the meaning of April 25, 2016 added to January 5, 2016?
You should store your durations using the smallest time unit that can be used to represent them as integer numbers and use the MySQL DATE_ADD() function instead of the addition.
For example, if duration is 1 WEEK then you can use any of:
UPDATE `banners` SET `end` = DATE_ADD(`start`, INTERVAL 1 WEEK) WHERE `id` = 93
UPDATE `banners` SET `end` = DATE_ADD(`start`, INTERVAL 7 DAY) WHERE `id` = 93
UPDATE `banners` SET `end` = DATE_ADD(`start`, INTERVAL 168 HOUR) WHERE `id` = 93
If duration is usually 1 week, you can use DATE_ADD() function of MySql
DATE_ADD(start,INTERVAL 7 DAY)
Hope that helps

MySQL date interval on select

i've made this SQL code :
CREATE TABLE `logs` (
`id_log` INT(11) NOT NULL AUTO_INCREMENT,
`data_log` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id_log`),
)
i made it to Insert a record when my server goes down,but i would like to make some check if it wasn't Inserted the same record 10 minutes before.
So i was looking for some SELECT that shows only records from NOW() to 10 minutes before.
You're looking for INTERVAL # [UNIT], there's various ways to use it -- http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html:
SELECT count(*)
FROM logs
WHERE data_log > NOW() - INTERVAL 10 MINUTE;
This will return the count of records written to the log in the last ten minutes:
SELECT Count(*) as count_in_last_10 FROM logs WHERE data_log BETWEEN DATE(NOW()-INTERVAL 10 MINUTE) AND NOW()

Calculating working hours for each weekday between date range

I have a table timeandattandance with following fields
TAId int(11) NOT NULL AUTO_INCREMENT,
PostId int(11) NOT NULL,
PositionId int(11) NOT NULL,
CreatedBy int(11) NOT NULL,
ModifiedBy int(11) NOT NULL,
CreatedDate datetime NOT NULL,
ModifiedDate datetime NOT NULL,
TimeIn datetime NOT NULL,
TimeOut datetime DEFAULT NULL,
TimeBilled tinyint(1) NOT NULL DEFAULT '0',
DeviceId varchar(50) DEFAULT NULL,
UserId int(11) DEFAULT NULL,
oldid varchar(45) DEFAULT NULL,
FromCallIn tinyint(1) DEFAULT '0'
I need to get working hours for each user for each day grouped by week ending date between a date range. that is I have been given a date range. Firstly I need to find out that timein will be between this date range then I need to get all week ending dates and then for every week I need to calculate working hours for each day.
Also while calculating the working hours I need to check if difference between timein and timeout is more than one day then these working hours will be separated for two days instead of one.
I know it's bit complex and if require more explanation please let me know.
Here's an example/demonstration of one possible approach:
SELECT r.dt + INTERVAL 7-DAYOFWEEK(r.dt) DAY AS week_ending
, t.userid
, r.dt
, SUM(TIMESTAMPDIFF(SECOND
,GREATEST(r.dt,t.timein)
,LEAST(r.dt+INTERVAL 1 DAY,t.timeout)
)
)/3600 AS hours_worked
FROM ( SELECT '2014-09-28' AS rb, '2014-10-11' AS re) dr
JOIN ( SELECT DATE(i.timein) AS dt FROM mytable i
UNION
SELECT DATE(o.timeout) FROM mytable o
) r
ON r.dt BETWEEN DATE(dr.rb) AND DATE(dr.re)
JOIN mytable t
ON t.timein < r.dt+INTERVAL 1 DAY
AND t.timeout > r.dt
GROUP BY t.userid, r.dt
ORDER BY week_ending, t.userid, r.dt
NOTE: The week_ending returns the date of the Saturday following (or of) the work date.
The date range is specified in the inline view dr (date range), range begin date is column rb, the range end date is column re. This example shows a two week range, starting on Sunday 2014-09-28 for two full weeks, including time worked on Saturday 2014-10-11. (The value for re could be derived as an integer number of days from rb, to get 14 full days, `rb + INTERVAL 13 DAY)
Only hours worked on these dates, or on days between these dates, are reported. A given userid that did not have any work "time" on a given date will not have a row returned for that date. (The query could be easily tweaked to return rows for all employees for all dates in the range, and returning zeros.)
Absent a "cal" calendar table that contains all date values, we can get a distinct list of date values in the range from the table itself; this could be relatively expensive operation for a large number or rows in the table. This won't return date values that don't appear in the timein or timeout columns. (If there's a work period of over 24 hours, e.g. starting on Monday and ending on Wednesday. and there are no other rows that have a timein or timeout on Wednesday, the 24 hours worked that day will be omitted... that's likely an extreme corner case. Having a distinct list of all possible date value available in a calendar table avoids that problem.)
FOLLOWUP
From your comment it sounds like you need a cross product between a distinct list of days, and a distinct list of users, then an outer join to the working hours table.
JOIN ( distinct_list_of_dates_r ) r
ON ( r_in_specified_week )
CROSS
JOIN ( distinct_list_of_userid_u ) u
LEFT
JOIN mytable t
ON ( t_userid_matches_u )
AND ( t_times_matches_r )
And then GROUP BY the userid from u, rather than from t. You'll likely want to replace a NULL value returned from hours_worked with a 0. The MySQL IFNULL function is convenient for that, IFNULL(expr,0) is shorthand for CASE WHEN expr IS NULL THEN 0 ELSE expr END. If you want total hours for the entire week, rather than by individual days, then do the GROUP BY on the week_ending expression, rather than on the individual date.

MySQL query runs very slow on large table

I am trying to run the following query on a very large table with over 90 million of rows increasing
SELECT COUNT(DISTINCT device_uid) AS cnt, DATE_FORMAT(time_start, '%Y-%m-%d') AS period
FROM game_session
WHERE account_id = -2 AND DATE_FORMAT(time_start '%Y-%m-%d') BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
GROUP BY period
ORDER BY period DESC
I have the following table structure:
CREATE TABLE `game_session` (
`session_id` bigint(20) NOT NULL,
`account_id` bigint(20) NOT NULL,
`authentification_type` char(2) NOT NULL,
`source_ip` char(40) NOT NULL,
`device` char(50) DEFAULT NULL COMMENT 'Added 0.9',
`device_uid` char(50) NOT NULL,
`os` char(50) DEFAULT NULL COMMENT 'Added 0.9',
`carrier` char(50) DEFAULT NULL COMMENT 'Added 0.9',
`protocol_version` char(20) DEFAULT NULL COMMENT 'Added 0.9',
`lang_key` char(2) NOT NULL DEFAULT 'en',
`instance_id` char(100) NOT NULL,
`time_start` datetime NOT NULL,
`time_end` datetime DEFAULT NULL,
PRIMARY KEY (`session_id`),
KEY `game_account_session_fk` (`account_id`),
KEY `lang_key_fk` (`lang_key`),
KEY `lookup_active_session_idx` (`account_id`,`time_start`),
KEY `lookup_finished_session_idx` (`account_id`,`time_end`),
KEY `start_time_idx` (`time_start`),
KEY `lookup_guest_session_idx` (`device_uid`,`time_start`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
How can I optimize this?
Thank for your answer
DATE_FORMAT(time_start '%Y-%m-%d') sounds expensive.
Every calculation on a column reduces the use of indexes. You probably run in to a full index scan + calculation of DATE_FORMAT for each value instead of a index lookup / range scan.
Try to store the computed value in the column (or create a computed index if mysql supports it). Or even better rewrite your conditions to compare directly to the value stored in the column.
Well, 90mlns is a lot, but I suspect it doesn't use the start_time_idx because of the manipulations, which you can avoid (you can manipulate the values you compare it with with, it also must be done only once per query if mysql is smart enough), have you checked EXPLAIN?
You may want to group and sort by time_start instead of the period value you create when the query is run. Sorting by period requires all of those values to be generated before any sorting can be done.
Try swapping out your WHERE clause with the following:
WHERE account_id = -2 AND time_start BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
MySQL will still catch the dates between, the only ones you'll need to worry about are the ones from today, which might get truncated due to technically being greater than midnight.
You can fix that by incrementing the second CURDATE( ) with CURDATE( ) + INTERVAL 1 DAY
I'd change
BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
to
> (CURDATE() - INTERVAL 90 DAY)
You don't have records from future, do you?
Change the query to:
SELECT COUNT(DISTINCT device_uid) AS cnt
, DATE_FORMAT(time_start, '%Y-%m-%d') AS period
FROM game_session
WHERE account_id = -2
AND time_start >= CURDATE() - INTERVAL 90 DAY
AND time_start < CURDATE() + INTERVAL 1 DAY
GROUP BY DATE(time_start) DESC
so the index of (account_id, time_start) can be used for the WHERE part of the query.
If it's still slow - the DATE(time_start) does not look very good for performance - add a date_start column and store the date part of time_start.
Then add an index on (account_id, date_start, device_uid) which will further improve performance as all necessary info - for the GROUP BY date_start and the COUNT(DISTINCT device_uid) parts - will be on the index:
SELECT COUNT(DISTINCT device_uid) AS cnt
, date_start AS period
FROM game_session
WHERE account_id = -2
AND date_start BETWEEN CURDATE() - INTERVAL 90 DAY
AND CURDATE()
GROUP BY date_start DESC