I have a database table full of time records and I need to calculate the quantity of hours that exist between them...
A time record has the following fields: 'created' (i.e. 2017:08:30 11:15:00) and 'direction' (i.e. 1 represents "clock in" and "0" represents "clock out"). So I need to set a start and end date, then select all time records within that time frame and calculate the quantity of hours worked (the quantity of hours that exist between the records where direction=0 and direction=1).
Any idea how to create the logic for this? The result must be a measurement of "hours" in decimal format (1 decimal place, i.e. '26.7' hours).
I started by establishing variables:
$query_start_date = "2017-08-29 00:00:00";
$query_end_date = "2017-08-30 23:59:59";
Let's assume these are the time records that exist in that time frame:
time record 1: 'created'="2017-08-29 08:00:00", 'direction'=1;
time record 2: 'created'="2017-08-29 16:30:00", 'direction'=0;
time record 3: 'created'="2017-08-30 08:00:00", 'direction'=1;
time record 4: 'created'="2017-08-30 16:00:00", 'direction'=0;
But I don't know how to begin the calculation. Do I select the records first and assign each record to a variable as an array...? Any help is appreciated!
Make start and end time in two columns:
SELECT
created as start,
(select created from test t2 where t2.created > t.created
and direction = 1 order by created limit 1) as end
FROM `test` t where direction = 0
conunt time difference:
select TIME_TO_SEC(TIMEDIFF(end, start))/60/60 as diff, start, end from
(SELECT
created as start,
(select created from test t2 where
t2.created > t.created
and direction = 1 order by created limit 1) as end
FROM `test` t where direction = 0) intervals
and finally count sum:
select sum(TIME_TO_SEC(TIMEDIFF(end, start))/60/60) as total from
(SELECT
created as start,
(select created from test t2 where
t2.created > t.created
and direction = 1 order by created limit 1) as end
FROM `test` t where direction = 0) intervals
Related
I have no idea how to solve the following problem: I have several rows in my database with one timestamp per row. Now I would like to filter all rows for entries until the date interval for any two dates is bigger than 30 days. I have no defined date interval for specific dates, like between 12/01/2017 and 11/01/2017, that would be easy, even for me. All I know is that the timestamp interval from one row to the next row (query must be sorted by timestamp desc) must not be bigger than 30 days.
Please see my db at http://sqlfiddle.com/#!9/55a521/2
In this case the last entry shown should be the one with id 65404844. I would appreciate if you might give me a small hint for this.
Thank you very much!
You can use this query to build a filter.
SELECT
t.id,
from_unixtime(timestamp)
, IF(#pt < timestamp - 30*24*60*60, 1, 0) AS filter
, #pt := timestamp
FROM
t
, (SELECT #pt := MIN(timestamp) FROM t) v
ORDER BY timestamp
see it working live in an sqlfiddle
Important here is to order by timestamp. Then you initialize the #pt variable with the lowest value. Another important thing is to have the select clause in the right order.
First you compare the current record with the variable in the IF() function. Then you assign the current record to the variable. This way when the next row is evaluated, the variable still holds the value of the previous row in the IF() function.
To get the rows you want, use above query in a subquery to filter.
SELECT id, ts FROM (
SELECT
t.id,
from_unixtime(timestamp) as ts
, IF(#pt < timestamp - 30*24*60*60, 1, 0) AS filter
, #pt := timestamp
FROM
t
, (SELECT #pt := MIN(timestamp) FROM t) v
ORDER BY timestamp
) sq
WHERE sq.filter = 1
This filters out the rows that have a more than 30 days difference from the previous rows. (1st solution) - only works if the id column has consecutive values
SELECT t.id, t.timestamp, DATEDIFF(FROM_UNIXTIME(t1.timestamp), FROM_UNIXTIME(t.timestamp)) AS days_diff
FROM tbl t
LEFT JOIN tbl t1
ON t.id = t1.id + 1
HAVING days_diff <= 30
ORDER BY t.timestamp DESC;
This filters all the results that are within 30 days of each of the other entries.
SELECT *
FROM tbl t
WHERE EXISTS (
SELECT id
FROM tbl t1
WHERE DATEDIFF(FROM_UNIXTIME(t1.timestamp), FROM_UNIXTIME(t.timestamp)) < 30
AND t1.id <> t.id
)
ORDER BY t.timestamp desc;
I have a table that stores shift records for employees.
Simply, there's the following data:
id = Shift ID
employeenum = Employee Number
start = unix timestamp of shift start time
end = unix timestamp of shift end time
date = YYYY-mm-dd description of date the shift starts on
status = shift status (numeric status identifier)
I am currently determining conflicts through a looping php script but it's far too slow. I've searched other questions and can't quite find the answer I'm looking for.
I am trying to come up with a query that will basically give me a list of employeenums that have conflicting shifts within a given time period.
i.e. for the period 2016-07-03 to 2016-07-10, which employees have overlapping start and end timestamps for shifts with a status value of 1 or 7.
Any help would be appreciated.
Thank you!
EDIT
This is essentially the table structure.
id is a primary auto increment key. The table is full of numeric data.
ID is an autoincremented number, employeenum is a 6 digit number, start and end are unix timetamps, date is YYYY-mm-dd date format, overridden is 1 or 0, status is 1,2,3,4,5,6, or 7.
Current loop works by querying:
SELECT * FROM schedule WHERE overridden =0 AND date >=$startdate AND date <= $enddate AND (status = 1 OR status = 7) AND employeenum != 0 ORDER BY date ASC
It then loops through all of those returned shifts to test whether or not another one conflicts with them by executing this query over and over (using the returned start and end values from the results of the above query):
SELECT `employeenum` FROM `schedule` WHERE `overridden` =0 AND `date` >= '$startdate' AND `date` <= '$enddate' AND (`status` = '1' OR `status` = '7') AND ((('$start' > `start`) AND ('$start' < `end`)) OR ((`end` > '$start') AND (`end` < '$end'))) AND `employeenum` = '$employee';"
If there is a result, it pushes the employee number to an array of employees with conflicts. This then prevents the loop from checking for that employee again.
At any given time there could be 10,000 records, so it's executing 10,000+ queries. These records represent only 100-200 employees, so I am looking for a way to query one time to see if there are any overlapping (start and end overlap with another start or end) records between two date values for one employeenum without having to query the database 10,000 times.
This Query will give you the shift id, the date, the employee number, the conflicting employee numbers and a count of conflicting shifts. You have a ton of shifts that conflict in your dataset!!!
SELECT `schedule_test`.`id`, `schedule_test`.`date`, `schedule_test`.`employeenum`, GROUP_CONCAT(DISTINCT`join_tbl`.`employeenum`), COUNT(`join_tbl`.`employeenum`)
FROM `schedule_test`
INNER JOIN `schedule_test` AS `join_tbl` ON
`schedule_test`.`date` = `join_tbl`.`date`
AND (`join_tbl`.`status` = 1 OR `join_tbl`.`status` = 7)
AND (`join_tbl`.`start` BETWEEN `schedule_test`.`start` AND `schedule_test`.`end`
OR `join_tbl`.`end` BETWEEN `schedule_test`.`start` AND `schedule_test`.`end`)
AND `schedule_test`.`id` != `join_tbl`.`id`
WHERE (`schedule_test`.`status` = 1 OR `schedule_test`.`status` = 7)
GROUP BY `schedule_test`.`id`
ORDER BY `schedule_test`.`date`
Adapted from #cmorrissey 's answer. THANK YOU!!
SELECT `schedule_test`.`id`, `schedule_test`.`date`,
`schedule_test`.`employeenum`,
GROUP_CONCAT(DISTINCT`join_tbl`.`employeenum`),
COUNT(`join_tbl`.`employeenum`)
FROM `schedule_test`
INNER JOIN `schedule_test` AS `join_tbl` ON
`schedule_test`.`date` = `join_tbl`.`date`
AND (`join_tbl`.`status` = 1 OR `join_tbl`.`status` = 7)
AND (`join_tbl`.`employeenum` = `schedule_test`.`employeenum`)
AND (`join_tbl`.`start` BETWEEN `schedule_test`.`start` AND `schedule_test`.`end`
OR `join_tbl`.`end` BETWEEN `schedule_test`.`start` AND `schedule_test`.`end`)
AND `schedule_test`.`id` != `join_tbl`.`id`
WHERE (`schedule_test`.`status` = 1 OR `schedule_test`.`status` = 7)
GROUP BY `schedule_test`.`id`
ORDER BY `schedule_test`.`date`
I currently have a system to track inventory items.
The sql table is set up as follows:
Unique ID | Order number | Location | TimeStamp
Every time an order moves, a new entry is created with the same order number with the new location and timestamp.
Now I need to find the average time required for order to move from one location to another, say from Location Warehouse to Pickup Depot.
I am trying to work on the query and I have this so far.
SELECT
IFNULL(TIMESTAMPDIFF(SECOND,
MIN(TimeStamp),
MAX(TimeStamp)) / NULLIF(COUNT(*) - 1, 0), 0)
FROM TableName
WHERE Status = 'Delivered'
AND TimeStamp > DATE_SUB(NOW(), INTERVAL 6 HOUR)
The works really well if the table only had one order number, the moment we add more table numbers the average goes off.
I need it to only look at the timestamp difference for each order number, while currently I think its looking at the whole table.
Any help would be much appreciated.
Apologize for posting this question twice, the previous post did not contain enough information.
Thanks again.
SELECT
IFNULL(TIMESTAMPDIFF(SECOND,
MIN(TimeStamp),
MAX(TimeStamp)) / NULLIF(COUNT(*) - 1, 0), 0)
FROM TableName
WHERE Status = 'Delivered'
AND TimeStamp > DATE_SUB(NOW(), INTERVAL 6 HOUR)
GROUP BY OrderNumber
The above query returns the timedifference in different rows in sql (with the following error " Current selection does not contain a unique column. Grid edit, checkbox, Edit, Copy and Delete features are not available.". The table has one column named "IFNULL(TIMESTAMPDIFF(SECOND, MIN(TimeStamp), MAX(TimeStamp)) / NULLIF(COUNT(*) - 1, 0), 0)" with the time difference for various orders arrange in rows. Now I am trying to get their average with the output code.
Am outputting the results with the following code:
$row_cnt = $result2->num_rows;
while ($row2 = mysqli_fetch_assoc($result2)) {
$processingseconds = $row2['IFNULL(TIMESTAMPDIFF(SECOND, MIN(TimeStamp), MAX(TimeStamp)) / NULLIF(COUNT(*) - 1, 0), 0)'] + $processingseconds;
}
print "Current Processing Time: ";
$processingseconds = $processingseconds/$row_cnt;
$processingminutes = $processingseconds/60;
echo $processingminutes;
Try adding a Group by condition to your query.
GROUP BY Order_Number_column
Something like this:
SELECT
IFNULL(TIMESTAMPDIFF(SECOND,
MIN(TimeStamp),
MAX(TimeStamp)) / NULLIF(COUNT(*) - 1, 0), 0)
FROM TableName
WHERE Status = 'Delivered'
AND TimeStamp > DATE_SUB(NOW(), INTERVAL 6 HOUR)
GROUP BY Your_Order_Number_column
I have a MYSQL table for tasks where each task has a date, start time,end time and user_id. I want to calculate total number of hours on specific date.
Table Structure
CREATE TABLE tasks
(`id` int,`user_id` int, `title` varchar(30), `task_date` datetime, `start` time, `end` time)
;
INSERT INTO tasks
(`id`,`user_id`, `title`,`task_date`, `start`, `end`)
VALUES
(1,10, 'Task one','2013-04-02', '02:00:00', '04:00:00'),
(2,10, 'Task two','2013-04-02', '03:00:00', '06:00:00'),
(3,10, 'Task three.','2013-04-02','06:00:00', '07:00:00');
MYSQL Query
select TIME_FORMAT(SEC_TO_TIME(sum(TIME_TO_SEC(TIMEDIFF( end, start)))), "%h:%i") AS diff
FROM tasks
where task_date="2013-04-02"
The result am getting is "06:00" Hours which is fine, but I want to exclude the overlap hours. In the example I gave result should be "05:00" Hours when the hour between 3-4 in the 2nd record is excluded because this hour is already exist in the 1st record between 2-4.
1st record 2=>4 = 2 Hours
2nd record 3=>6 = 3 hours 3-1 hour=2 (The 1 hour is the overlap hour between 1st and 2nd record )
3rd 6=>7=1
Total is 5 Hours
I hope I made my question clear. Example http://sqlfiddle.com/#!2/05dd8/2
Here is an idea that uses variables (in other databases, CTEs and window functions would make this much easier). The idea is to first list all the times -- starts and ends. Then, keep track of the cumulative number of starts and stops.
When the cumulative number is greater than 0, then include the difference from the previous time. If equal to 0, then it is the beginning of a new time period, so nothing is added.
Here is an example of the query, which is simplified a bit for your data by not keeping track of changes in user_id:
select user_id, TIME_FORMAT(SEC_TO_TIME(sum(secs)), '%h:%i')
from (select t.*,
#time := if(#sum = 0, 0, TIME_TO_SEC(TIMEDIFF(start, #prevtime))) as secs,
#prevtime := start,
#sum := #sum + isstart
from ((select user_id, start, 1 as isstart
from tasks t
) union all
(select user_id, end, -1
from tasks t
)
) t cross join
(select #sum := 0, #time := 0, #prevtime := 0) vars
order by 1, 2
) t
group by user_id;
Here is a SQL Fiddle showing it working.
I am trying to combine two MYSQL Queries into one. What I want to do is select the first and last row added for each day and subtract the last column for that day from the first column of that day and output that. What this would do is give me a net gain of XP in this game for that day.
Below are my two queries, their only difference is ordering the date by DESC vs ASC. the column in the database that i want to subtract from each other is "xp"
$query = mysql_query("
SELECT * FROM (SELECT * FROM skills WHERE
userID='$checkID' AND
skill = '$skill' AND
date >= ".$date."
ORDER BY date DESC) as temp
GROUP BY from_unixtime(date, '%Y%m%d')
");
$query2 = mysql_query("
SELECT * FROM (SELECT * FROM skills WHERE
userID='$checkID' AND
skill = '$skill' AND
date >= ".$date."
ORDER BY date DESC) as temp
GROUP BY from_unixtime(date, '%Y%m%d')
");
SELECT FROM_UNIXTIME(date, '%Y%m%d') AS YYYYMMDD, MAX(xp)-MIN(xp) AS xp_gain
FROM skills
WHERE userID = '$checkID'
AND skill = '$skill'
AND date >= $date
GROUP BY YYYYMMDD
This assumes that XP always increases, so it doesn't need to use the times to find the beginning and ending values.
If that's not a correct assumption, what you want is something like this:
SELECT first.YYYYMMDD, last.xp - first.xp
FROM (subquery1) AS first
JOIN (subquery2) AS last
ON first.YYYYMMDD = last.YYYYMMDD
Replace subquery1 with a query that returns the first row of each day, and subquery2 with a query that returns the last row of each day. The queries you posted in your question don't do this, but there are many SO questions you can find that explain how to get the highest or lowest row per group.