id title start end
1 Doing Coding for this project. 2013-04-02 02:00:00 2013-04-02 04:00:00
2 Doing Coding for this project. 2013-04-02 04:00:00 2013-04-02 06:00:00
3 Doing Coding for this project. 2013-04-02 06:00:00 2013-04-02 06:30:00
I have above MySQL database table record. Now i want to get the total number of hours.
I am developing TimeSheet Management Application and we need to display total working hours with minutes and second of employee. (i.e 04:30:00 according to data i share)
what i have tried?
SELECT HOUR(TIMEDIFF(end,start)) AS 'totalHour' but works only for each row not on all records.
I have also tried TIMESTAMPDIFF.
Is this possible?
EDIT
From the answer i have received from people i have tried every single of them but everytime i just get 4 or 4.5000 but it should return 06:30:00.
The range of HOUR() function is 0 to 23 so it's not correct to use it for total hours in diff.
For single value you could use TIMESTAMPDIFF() like:
SELECT TIMESTAMPDIFF(HOUR, start, end) AS `totalHour` FROM ...
If you want to calculate it for whole project, you have to sum up all time differences and them print it formatted probably with funciton like TIME_FORMAT() which prints hour larger than 24:
If the time value contains an hour part that is greater than 23, the %H and %k hour format specifiers produce a value larger than the usual range of 0..23. The other hour format specifiers produce the hour value modulo 12.
So you can use:
SELECT TIME_FORMAT( SEC_TO_TIME( SUM( TIME_TO_SEC(end) - TIME_TO_SEC(start))), "%H")
AS `totalHour`
FROM ...
GROUP BY sort_of_project_id
If you need seconds/minutes too (as suggested in comment), use either:
time_to_sec( <left side of select>)/3600 which will return value like 4.84 hours
TIME_FORMAT( ..., "%H:%m:%s") which will display 4:38:24
Try this query
SELECT
id,
title,
TIME_FORMAT(SEC_TO_TIME(sum(TIME_TO_SEC(TIMEDIFF( end, start)))), "%h:%i") AS diff
FROM
tbl1
GROUP BY
title
According to the data that you have given answer should be 4:30. Pl cross check in you records.
FIDDLE
Try like this, it will give you the total no of hours:
For Example:
SELECT sum(time_to_sec(timediff(end, start ))/ 3600) AS 'totalHour' from test;
If you run this query for above table you given, it shows the output 4.5 hours.
Hope it will help you.
I already answered in other thread https://stackoverflow.com/questions/44560345/query-is-not-working/44567322#44567322
I just created a temporary table called dataimport
[Table Format][1]
and wrote a query as,
SELECT `EnNo`, work_dt,
SEC_TO_TIME(sum(TIMESTAMPDIFF(SECOND,login,logout))) as time_worked
from (
SELECT `EnNo`, date(`DateTime`) as work_dt, `DateTime` as login
, coalesce(
(SELECT MIN(`DateTime`)
FROM `dataimport` as b
WHERE a.EnNo = b.EnNo
and date(a.`DateTime`) = date(b.`DateTime`)
and b.`DateTime` >= a.`DateTime`
and b.`INOUT` = 'E'
), now()) AS logout
FROM `dataimport` AS a
WHERE a.`INOUT` = 'S'
) as t
GROUP BY `EnNo`, work_dt
Finally got the output as,
[Output][2]
Hope this is what you are lookin on.
[1]: https://i.stack.imgur.com/OEEMe.png
[2]: https://i.stack.imgur.com/g6ivm.png
Change your
SELECT HOUR(TIMEDIFF(end,start)) AS 'totalHour'
to
SELECT sum(TIMESTAMPDIFF(HOUR, end, start)) AS 'totalHour'
SELECT IF(DATE(datetime_end) = DATE(datetime_start), TIMEDIFF(datetime_end,datetime_start), IF(DATEDIFF(datetime_start,datetime_end) > 1, ADDTIME( TIME_FORMAT(CONCAT((DATEDIFF(datetime_start,datetime_end) - 1) * 8,':00:00'), "%H:%i:%s"), ADDTIME( TIMEDIFF(datetime_end,CONCAT(DATE(datetime_end),' 08:00:00')), TIMEDIFF(CONCAT(DATE(datetime_start),' 17:00:00'),datetime_start) ) ) , ADDTIME( TIMEDIFF(datetime_end,CONCAT(DATE(datetime_end),' 08:00:00')), TIMEDIFF(CONCAT(DATE(datetime_start),' 17:00:00'),datetime_start) ) ) ) AS total_working_hrs FROM table
Related
I have a field that is a a DATETIME field.
How do I select rows that fall between between 12:00 am and 8:30 am?
when I type in this query:
$query = "select * from table where TIME(changetime) between '12:00:00 and '08:30:00'";
it gives me zero hits.
The changetime field has the date like this: 2015-07-14 10:57:57
12:00:00 is 12 PM (noon), not 12 AM (midnight). You need to use
WHERE TIME(changetime) BETWEEN '00:00:00' AND '08:30:00'
1) you don't need TIME(changetime), it could probably worsen your search-time because mysql will need to apply a function instead of using index.
use just changetime.
2) where changetime between '2015-07-14 10:57:57' and '2015-07-14 12:57:57'
http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_between
3) you need to use right range - in your example END_DATE is BEFORE START_DATE. So, between will return 0 (12:00:00 > 08:30:00)
4) look at #Barmar's comment, maybe you really want entries between some time in any date, not between the datetimes
My goal is it to create a SQL-Query that counts all items in a certain time frame (e.g. 5min)
That's my code so far:
SELECT FROM_UNIXTIME(FLOOR(timestamp_stop/5*60)*(5*60), '%h:%i') AS timekey, timestamp_stop, count(item) AS performance
FROM task
WHERE done = 1
GROUP BY timekey
ORDER BY timestamp_stop ASC
That works great, but doesn't include time frames in which there aren't any records in the database.
I would like to also get these 0-count-ones, up to the current time.
Currently I have no simple/elegant solution in my mind. Any ideas?
Some little post processing in php would also be possible.
As Gordon mentioned, you probably want a secondary table as a basis for ALL 5-minute intervals. I have done similar with a query to self-build using MySQL variables.
select
YourTable.WhateverFields
from
( select
#startTime RangeStart,
#startTime := date_add( #startTime, interval 5 MINUTE ) RangeEnd
from
( select #startTime := '2014-10-20' ) sqlvars,
AnyTableThatHasAsManyDaysYouExpectToReport
limit
12 * numberOfHoursYouNeed * numberOfDaysYouNeed ) DynamicTimeRange
LEFT JOIN YourTable
on YourTable.DateTimeField >= DynamicTimeRange.RangeStart
AND YourTable.DateTimeField < DynamicTimeRange.RangeEnd
So, in this example, the innermost declars a variable "startTime" to Oct 20, 2014 which defaults to 12:00:00. Then the one out from that creates a result set of two columns for a RangeStart and RangeEnd and might look something like...
RangeStart RangeEnd
2014-10-20 00:00 2014-10-20 00:05
2014-10-20 05:00 2014-10-20 00:10
2014-10-20 10:00 2014-10-20 00:15
2014-10-20 05:00 2014-10-20 00:20
2014-10-20 20:00 2014-10-20 00:25
The table reference "AnyTableThatHasAsManyDaysYouExpectToReport" is just that... any table in your database that has at least as many records as you would need to generate your 5-minute intervals for however many hours and days. If you need 1 day worth = 12 records * 5 minutes = 1 hour * 24 hrs = 24*12 = 288 records needed. If you wanted a week, then so be it... multiply that by 7 so my sample just has place-holders to help clarify the intent...
But with the LEFT JOIN, you get all the intervals...
If there are such time frame, but the where clause filters out the records, you can do:
SELECT FROM_UNIXTIME(FLOOR(timestamp_stop/5*60)*(5*60), '%h:%i') AS timekey,
timestamp_stop,
sum(item is not null and done = 1) AS performance
FROM task
GROUP BY timekey
ORDER BY timestamp_stop ASC;
If you still have gaps, then you need to generate a table (or subquery) containing the list of the time frames that you want and use left join.
EDIT:
A subquery is not pleasant. You have to list all the time values. Something like:
SELECT q.timekey, t.timestamp_stop, coalesce(t.performance, 0) as performance
FROM (SELECT '00:00' as timekey UNION ALL
SELECT '00:05' UNION ALL
. . .
) q LEFT JOIN
(SELECT FROM_UNIXTIME(FLOOR(timestamp_stop/5*60)*(5*60), '%h:%i') AS timekey,
timestamp_stop,
COUNT(item) AS performance
FROM task
WHERE done = 1
GROUP BY timekey
) t
ON t.timekey = q.timekey
ORDER BY timestamp_stop ASC;
I am working with an equipment reservation system and need to calculate days that the equipment is being rented for so I can multiply it by the total cost per day of each reservation, then total the cost for all reservations in the query. The equipment pickup date and return date are stored in separate fields as yyyy/mm/dd 00:00:00.000 (no entries contain a time other than 00:00:00.000), there is also fields for pickup AM/PM and return AM/PM. If the pickup date is in the PM then that day does not count as a day, if the return date is in the AM then it does not count as a day. Ideally I would like to use the query below to get to my end goal, however it calculates the number of days as too many.
SELECT
SUM(TOTAL*DATEDIFF(day,PICKUPDT,RETURNDT))
FROM
RENTAL
WHERE
AGENTCODE = '$AGENT' AND
PICKUPDT >= '06/01/2013' AND
RETURNDT <= '06/01/2014' AND
PAIDOUT = '1' )
The above is nested within the whole query that I am running. The TOTAL field is the cost per day of rental. I need a way to accurately count the number of days the consumer will be charged for. I appreciate any help that can be given on the topic. This is my first question here and apologize in for any mistakes I made.
The finished screen I am working towards can be seen here:
http://jeffreyalanscott.com/stack_overflow1.jpg
I would suggest using TIMESTAMPDIFF instead of DATEDIFF as DATEDIFF only considers the date components and not the time.
SELECT SUM(total * TIMESTAMPDIFF(DAY, pickupdt, returndt) + 1)
...
For example:
SELECT TIMESTAMPDIFF(DAY, '2014-01-01 08:00:00', '2014-01-02 12:00:00') + 1;
would yield an integer value of 2 indicating that the item was check out for longer than 24 hours.
The + 1 part is because the function rounds down partial days.
You can use a case statement to increment the day count based on the hour-of-day of the return time. You might implement more granular rules based on the Days:Hours:
declare #rental table (AgentCode varchar(100), PickUpDt datetime, ReturnDt datetime, PickupAmPm char(2), ReturnAmPm char(2));
insert into #rental
select '123', '2014-02-01', '2014-02-02', 'AM', 'PM' union all
select '456', '2014-02-01', '2014-02-02', 'AM', 'AM'
select AgentCode,
[total] = datediff(dd, PickupDt, ReturnDt) + case ReturnAmPm when 'AM' then 0 else 1 end
from #rental
Returns:
AgentCode total
----------- -----------
123 2
456 1
I have a fun one for you. I have a database with the date columns free_from and free_until. What I need to find is the amount of days between now and 1 month today which are free. For example, if the current date was 2013/01/15 and the columns were as follows:
free_from | free_until
2013/01/12| 2013/01/17
2013/01/22| 2013/01/26
2013/01/29| 2013/02/04
2013/02/09| 2013/02/11
2013/02/14| 2013/02/17
2013/02/19| 2013/02/30
The answer would be 16
as 2 + 4 + 6 + 2 + 2 + 0 = 16
The first row only starts counting at the 15th rather than the 12th
since the 15th is the current date.
The last row is discounted because none of the dates are within a
month of the current date.
The dates must be counted as it the free_from date is inclusive and
the free_until date is exclusive.
I'm assuming DATEDIFF() will be used somewhere along the line, but I can't, for the life of me, work this one out.
Thanks for your time!
Edit: This is going into PHP mysql_query so that might restrict you a little concerning what you can do with MYSQL.
SET #today = "2013-01-15";
SET #nextm = DATE_ADD(#today, INTERVAL 1 month);
SET #lastd = DATE_ADD(#nextm, INTERVAL 1 day);
SELECT
DATEDIFF(
IF(#lastd> free_until, free_until, #lastd),
IF(#today > free_from, #today, free_from)
)
FROM `test`
WHERE free_until >= #today AND free_from < #nextm
That should work. At least for your test data. But what day is 2013/02/30? :-)
Dont forget to change #today = CURDATE();
The best I can think of is something like:
WHERE free_until > CURDATE()
AND free_from < CURDATE() + INTERVAL '1' MONTH
That will get rid of any unnecessary rows. Then on the first row do in PHP:
date_diff(date(), free_until)
On the last row, do:
date_diff(free_from, strtotime(date("Y-m-d", strtotime($todayDate)) . "+1 month"))
Then on intermediate dates do:
date_diff(free_from, free_until)
Something to that effect, but this seems extremely clunky and convoluted...
From the top of my mind... first do a:
SELECT a.free_from AS a_from, a.free_until AS a_until, b.free_from AS b_from
FROM availability a
INNER JOIN availability b ON b.free_from > a.free_until
ORDER BY a_from, b_from
This probably will return a set of rows where for each row interval you have next i.e. greater intervals. The results are ordered strategically. You can then wrap the results in a partial group by:
SELECT * FROM (
SELECT a.free_from AS a_from, a.free_until AS a_until, b.free_from AS b_from
FROM availability a
INNER JOIN availability b ON b.free_from > a.free_until
ORDER BY a_from, b_from
) AS NextInterval
GROUP BY a_from, b_until
In the above query, add a DATE_DIFF clause (wrap it in SUM() if necessary):
DATE_DIFF(b_until, a_from)
I'm looking for a best practice advice how to speed up queries and at the same time to minimize the overhead needed to invoke date/mktime functions. To trivialize the problem I'm dealing with the following table layout:
CREATE TABLE my_table(
id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,
important_data INTEGER,
date INTEGER);
The user can choose to show 1) all entries between two dates:
SELECT * FROM my_table
WHERE date >= ? AND date <= ?
ORDER BY date DESC;
Output:
10-21-2009 12:12:12, 10002
10-21-2009 14:12:12, 15002
10-22-2009 14:05:01, 20030
10-23-2009 15:23:35, 300
....
I don't think there is much to improve in this case.
2) Summarize/group the output by day, week, month, year:
SELECT COUNT(*) AS count, SUM(important_data) AS important_data
FROM my_table
WHERE date >= ? AND date <= ?
ORDER BY date DESC;
Example output by month:
10-2009, 100002
11-2009, 200030
12-2009, 3000
01-2010, 0 /* <- very important to show empty dates, with no entries in the table! */
....
To accomplish option 2) I'm currently running a very costly for-loop with mktime/date like the following:
for(...){ /* example for group by day */
$span_from = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i, date("Y", $time_min));
$span_to = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i+1, date("Y", $time_min));
$query = "..";
$output = date("m-d-y", ..);
}
What are my ideas so far? Add additional/ redundant columns (INTEGER) for day (20091212), month (200912), week (200942) and year (2009). This way I can get rid of all the unnecessary queries in the for loop. However I'm still facing the problem to very fastly calculate all dates that doesn't have any equivalent in database. One way to simply move the problem could be to let MySQL do the job and simply use one big query (calculate all the dates/use MySQL date functions) with a left join (the data). Would it be wise to let MySQL take the extra load? Anyway I'm reluctant to use all these mktime/date in the for loop. Since I have complete control over the table layout and code even suggestions with major changes are welcome!
Update
Thanks to Greg I came up with the following SQL query. However it still bugs me to use 50 lines of sql statements - build up with php - that maybe could be done faster and more elegantly otherwise:
SELECT * FROM (
SELECT DATE_ADD('2009-01-30', INTERVAL 0 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 1 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 2 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 3 DAY) AS day UNION ALL
......
SELECT DATE_ADD('2009-01-30', INTERVAL 50 DAY) AS day ) AS dates
LEFT JOIN (
SELECT DATE_FORMAT(date, '%Y-%m-%d') AS date, SUM(data) AS data
FROM test
GROUP BY date
) AS results
ON DATE_FORMAT(dates.day, '%Y-%m-%d') = results.date;
You definitely shouldn't be doing a query inside a loop.
You can group like this:
SELECT COUNT(*) AS count, SUM(important_data) AS important_data, DATE_FORMAT('%Y-%m', date) AS month
FROM my_table
WHERE date BETWEEN ? AND ? -- This should be the min and max of the whole range
GROUP BY DATE_FORMAT('%Y-%m', date)
ORDER BY date DESC;
Then pull these into an array keyed by date and loop over your data range as you are doing (that loop should be pretty light on CPU).
Another idea is not to use string inside the query. Transform the string parameter to datetime, on mysql.
STR_TO_DATE(str,format)
http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html