SELECT AVG (closeTime - createTime)
FROM Deals
WHERE dealid = 123
The 'closeTime' and 'addTime' have a DATETIME ( YYYY-MM-DD HH:MI:SS ) format. The result is:
105030215.0000
Which function should I use to convert this value into a DATETIME?
addTime: 12/04/2016 13:06
closeTime: 12/05/2017 16:08
Result that I am looking for (which I calculated in Excel):
29:03:02:15 (DD:HH:MM:SS)
Subtracting DATETIMEs in MySQL does not appear to give the difference between them in seconds. With your two example dates, 2016-04-12 13:06 and 2016-05-12 16:08 (assuming the 2017 in your question is a typo), subtracting them returns 100030200 whereas the correct answer is 2602920. This is out by a factor of 38.4 and a bit.
The MySQL function to return the difference in seconds is TIMESTAMPDIFF and, indeed, SELECT TIMESTAMPDIFF(SECOND, '2016-04-12 13:06', '2016-05-12 16:08') returns the correct number of seconds.
Now all we need to do is convert it into your format. MySQL has a built-in function SEC_TO_TIME which sort of does this, but unfortunately it doesn't work for periods of more than 839 hours and it doesn't itemise the days separately. We can work around this by calculating the days separately from the rest of the calculation and using SEC_TO_TIME on the leftovers once the days have been subtracted from the difference.
SELECT CONCAT(
TIMESTAMPDIFF(DAY, '2016-04-12 13:06', '2016-05-12 16:08'), ':',
SEC_TO_TIME(TIMESTAMPDIFF(SECOND, '2016-04-12 13:06', '2016-05-12 16:08') % 86400));
-- 30:03:02:00 (DD:HH:MM:SS)
This appears to be the correct answer. Although your desired output was 29:03:02:15, there were 30 days in April, and no seconds in the inputs at all.
As you're using an aggregate function to determine the number of seconds, we won't be able to select the value in days separately from the value in seconds (otherwise we'd get the average number of days followed by part of the average number of seconds), so you'll have to rewrite this as
SELECT CONCAT(
FLOOR(AVG(TIMESTAMPDIFF(SECOND, createTime, closeTime)) / 86400), ':',
SEC_TO_TIME(AVG(TIMESTAMPDIFF(SECOND, createTime, closeTime)) % 86400))
FROM deals WHERE dealid = 123;
-- 30:03:02:00.0000
This now includes fractions of a second because the average difference might not be a round number of seconds. To exclude them, use FLOOR or ROUND.
SELECT CONCAT(
FLOOR(AVG(TIMESTAMPDIFF(SECOND, createTime, closeTime)) / 86400), ':',
SEC_TO_TIME(ROUND(AVG(TIMESTAMPDIFF(SECOND, createTime, closeTime))) % 86400))
FROM deals WHERE dealid = 123;
Related
I need to calculate the average number of years from my MySQL database, and I try to use TIMESTAMPDIFF
Which one gives a valid result:
AVG( TIMESTAMPDIFF(MONTH, tanggal_masuk, tanggal_yudisium )/12 )
or
AVG( TIMESTAMPDIFF(YEAR, tanggal_masuk, tanggal_yudisium ) )
tanggal_masuk and tanggal_yudisium columns are DATE type.
It depends how accurate you want your final result to be. Using TIMESTAMPDIFF with MONTH and dividing by 12 will give floating point results (e.g. 0.5 years) where using it with YEAR will only give integers (e.g. TIMESTAMPDIFF(YEAR, '2018-09-01', '2018-01-10') is 0) where TIMESTAMPDIFF(MONTH, '2018-09-01', '2018-01-10') / 12 gives -0.5833. Overall, using MONTH will give more accurate results.
These statements both yields same result. It is up to you.
select ABS(TIMESTAMPDIFF(MONTH, '2018-01-01', '2010-01-01') / 12); --Result 8
select ABS(TIMESTAMPDIFF(YEAR, '2018-01-01', '2010-01-01') ) --Result 8
I am looking to pull scheduled hours in a given time period. Our start and end schedule times are datetimes so I converted them to timestamps. When I dont sum them everything looks correct, but when I sum them over a time period, the output isnt in a timestamp format and the numbers are incorrect.
The query I am using:
select sal.public_name, sum(timediff(timestamp(end_time), timestamp(start_time)))
from bi.support_agents_list sal
join bi.support_sp_shifts_scheduled ss
on ss.agent_sp_id = sal.sp_id
join bi.support_sp_shifts s
on s.pk_id = ss.pk_id
where date(start_time) between '2014-01-29' and '2014-01-31'
group by sal.public_name
A few examples of results I am getting:
Agent 1: 53000 - when it should be 5.5 hours or 5:30
agent 2: 196000 - when it should be 20 hours
Any thoughts on this? I would prefer my output to be in an hour count so 5 hours and 30 min is formatted as 5.5 rather than 5:30.
try this instead of the sum
date_format(timediff(timestamp(end_time), timestamp(start_time)),
'%k hours, %i minutes, %s seconds') as thesum
like that
select sal.public_name,
date_format(timediff(timestamp(end_time), timestamp(start_time)), '%k hours, %i minutes, %s seconds') as thesum
from bi.support_agents_list sal
When doing aggregate calculations with datetime sum(datetime), the result is not what you expect (=cannot sum datetimes). You will be better off converting the datetime to seconds before the aggregate function and then convert it back to time.
Your aggregate function call would then look something like:
select sec_to_time(sum(unix_timestamp(end_time)-unix_timestamp(start_time)))
Be aware that you may reach maximum value that time datatype can contain and that unix_timestamp starts from 1970.
I have date field in mysql in 1988-04-04 format.I need to calculate age in 25/04 months format.I tried this:
SELECT CONCAT(
FLOOR((curdate() - dob) / 31536000),
'y ',
FLOOR(MOD((curdate() - dob) / 31536000 * 12, 12)),
'm'
) `age` from age
It is giving me 0/0 months. I'll be grateful for any help.
When you select curdate() in a numeric context, you get the yyyymmdd value, such as 20130812 (for today, August 12, 2013). That's not really that useful for date calculations.
By way of example, my birthdate (old fart that I am) would be 19650202. When you work out the numeric difference between that and today, you get 480610.
Now, if you divide that by 31536000 (not sure where you got that from), you definitiely get zero, despite the fact I'm a 48-year-old geezer :-)
You would be far better off using datediff() to work out the number of days difference between two dates and then applying the correct divide and modulo operations to get full-years and months from that, something like (untested but should be a good start):
select
floor (100 * datediff (curdate(), dob) / 36525) as years,
floor (mod (100 * datediff (curdate(), dob), 36525) / 100 / 30) as months
from age
That won't be perfect since the location of leap-years will affect it a little, the actual days per year is 365.2425 over the long term, and we're assuming exactly 30 days per month but it should be accurate to within a couple of days.
If you want a more accurate measure, you need to find or implement some more exact equations for working out the values.
That's probably going to entail using year() and month() to extract the relevant fields from both dob and the current day and subtracting those, adjusting if if the current date comes before the birthday in the current year.
Using DATEDIFF as suggested by paxdiablo:
SELECT
FLOOR(DATEDIFF(CURDATE(), dob) / 365.25) AS years,
FLOOR(MOD(DATEDIFF(CURDATE(), dob), 365.25) / (365.25 / 12)) AS months
FROM age
I created a field called 'hours_spent' in MySQL using the decimal datatype to store time. The values are stored like this 1.30, 2.30 etc... (for 1hr30min, 2hr30min).
I want to calculate the sum of various time values.
The sum of time is not what I expected: 1.30 + 2.30 = 3.60, whereas I expected 4.00.
I used the SUM function in MySQL to count the hours_spent field. If the values are 0.30 + 1.50 = 1.80, whereas I expected 2.20.
My first mistake was to use the decimal type instead of the time datatype, but I cannot change datatype.
So, is there any way to sum the time values and get result as I expect?
Thanks
I prepared you a demo at sqlfiddle, you can try it there if you want:
http://www.sqlfiddle.com/#!2/c9afc/2
Here are the query samples:
select #indexer:=instr(dateasdecimal, '.')
, left(dateasdecimal, #indexer-1) * 60 + substr(dateasdecimal, #indexer+1) as totalMinutes
from testtable;
select #indexer:=instr(dateasdecimal, '.')
, sum(left(dateasdecimal, #indexer-1) * 60 + substr(dateasdecimal, #indexer+1)) as totalMinutes
from testtable;
Note: Please don't forget to accept answers to your questions:
https://meta.stackexchange.com/a/65088/200585
To convert a decimal into seconds, you could use this:
truncate(hours_spent,0)*60+(hours_spent-truncate(hours_spent,0))*100
and then you can do the sums easily. Then you can convert back seconds to the decimal format with this:
truncate(seconds/60,0)+truncate(mod(seconds, 60)/100,2)
You could always turn the decimals into a string, cast as time, then sum that time using time_to_sec and produce a formatted time with sec_to_time. Of course, it would be much better to be storing those times a different way, even if it involves converting the entire dataset.
SELECT sec_to_time(sum(time_to_sec(goodTime))) FROM (
SELECT CAST(badTime AS TIME) AS goodTime FROM (
SELECT REPLACE(badTime, '.', ':') AS badTime FROM (
SELECT CAST(badTime AS dec(4,2)) AS badTime FROM (
SELECT 1.3 AS badTime
UNION select 2.3
) z
) y
) x
) w
Lets say I have a table that contains the following - id and date (just to keep things simple).
It contains numerous rows.
What would my select query look like to get the average TIME for those rows?
Thanks,
Disclaimer: There may be a much better way to do this.
Notes:
You can't use the AVG() function against a DATETIME/TIME
I am casting DATETIME to DECIMAL( 18, 6 ) which appears to yield a reasonably (+- few milliseconds) precise result.
#1 - Average Date
SELECT
CAST( AVG( CAST( TimeOfInterest AS DECIMAL( 18, 6 ) ) ) AS DATETIME )
FROM dbo.MyTable;
#2 - Average Time - Remove Date Portion, Cast, and then Average
SELECT
CAST( AVG( CAST( TimeOfInterest - CAST( TimeOfInterest AS DATE ) AS DECIMAL( 18, 6 ) ) ) AS DATETIME )
FROM dbo.MyTable;
The second example subtracts the date portion of the DATETIME from itself, leaving only the time portion, which is then cast to a decimal for averaging, and back to a DATETIME for formatting. You would need to strip out the date portion (it's meaningless) and the time portion should represent the average time in the set.
SELECT CAST(AVG(CAST(ReadingDate AS real) - FLOOR(CAST(ReadingDate as real))) AS datetime)
FROM Rbh
I know that, in at least some of the SQL standards, the value expression (the argument to the AVG() function) isn't allowed to be a datetime value or a string value. I haven't read all the SQL standards, but I'd be surprised if that restriction had loosened over the years.
In part, that's because "average" (or arithmetic mean) of 'n' values is defined to be the sum of the values divided by the 'n'. And the expression '01-Jan-2012 08:00' + '03-Mar-2012 07:53' doesn't make any sense. Neither does '01-Jan-2012 08:00' / 3.
Microsoft products have a history of playing fast and loose with SQL by exposing the internal representation of their date and time data types. Dennis Ritchie would have called this "an unwarranted chumminess with the implementation."
In earlier versions of Microsoft Access (and maybe in current versions, too), you could multiply the date '01-Jan-2012' by the date '03-Mar-2012' and get an actual return value, presumably in units of square dates.
If your dbms supports the "interval" data type, then taking the average is straightforward, and does what you'd expect. (SQL Server doesn't support interval data types.)
create table test (
n interval hour to minute
);
insert into test values
('1:00'),
('1:30'),
('2:00');
select avg(n)
from test;
avg (interval)
--
01:30:00