Calculate average number of years with TIMESTAMPDIFF - mysql

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

Related

Convert integer into datetime (dd:hh:mm:ss)

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;

how to select first and last value of rows and between 5 minutes interval

I hava a table with sample forex one minute bar quotes.
id,quote_name,quote_date,quote_time,open_rate,close_rate,high_rate,low_rate
"1417","EURUSD","2015-01-01","13:01:00","1.2096","1.2096","1.2097","1.2096"
"1418","EURUSD","2015-01-01","13:02:00","1.2097","1.2096","1.2097","1.2096"
"1419","EURUSD","2015-01-01","13:04:00","1.2096","1.2098","1.2101","1.2096"
"1420","EURUSD","2015-01-01","13:05:00","1.2099","1.2099","1.2099","1.2099"
Is it possible to create select statement which will be return 5 minute interval quotes. I mean that it should select 5 rows between each 5 minut interval and return open_rate from first row, close_rate from last quote, and min and max of high_rate and low_rate.
Is it possible at all? How to do that.
What I know is that how to select min and max value between two dates.
Getting the five-minute interval is a bit of a pain. One way is to convert to seconds and divide by 300. Then, getting the first and last is also tricky. In this case, I would suggest a trick using substring_index() and group_concat():
select quote_date, min(open_time) as open_time,
substring_index(group_concat(open_rate order by quote_time), ',', 1) as first_open,
substring_index(group_concat(close_rate order by quote_time desc), ',', 1) as last_close,
min(high_rate), max(high_rate),
min(low_rate), max(low_rate)
from quotes
group by quote_date, floor(to_seconds(quote_time) / 300);

AVG or SUM in SQL where the values are being calculated on the fly

I have an existing SQL query that gets call stats from a Zultys MX250 phone system: -
SELECT
CONCAT(LEFT(u.firstname,1),LEFT(u.lastname,1)) AS Name,
sec_to_time(SUM(
time_to_sec(s.disconnecttimestamp) - time_to_sec(s.connecttimestamp)
)) AS Duration,
COUNT(*) AS '#Calls'
FROM
session s
JOIN mxuser u ON
s.ExtensionID1 = u.ExtensionId
OR s.ExtensionID2 = u.ExtensionId
WHERE
s.ServiceExtension1 IS NULL
AND s.connecttimestamp >= CURRENT_DATE
AND BINARY u.userprofilename = BINARY 'DBAM'
GROUP BY
u.firstname,
u.lastname
ORDER BY
'#Calls' DESC,
Duration DESC;
Output is as follows: -
Name Duration #Calls
TH 01:19:10 30
AS 00:44:59 28
EW 00:51:13 22
SH 00:21:20 13
MG 00:12:04 8
TS 00:42:02 5
DS 00:00:12 1
I am trying to generate a 4th column that shows the average call time for each user, but am struggling to figure out how.
Mathematically it's just "'Duration' / '#Calls'" but after looking at some similar questions on StackOverflow, the example queries are too simple to help me relate to my one above.
Right now, I'm not even sure that it's going to be possible to divide the time column by the number of calls.
UPDATE: I was so close in my testing but got all confused & overcomplicated things. Here's the latest SQL (thanks to #McAdam331 & my buddy Jim from work): -
SELECT
CONCAT(LEFT(u.firstname,1),LEFT(u.lastname,1)) AS Name,
sec_to_time(SUM(
time_to_sec(s.disconnecttimestamp) - time_to_sec(s.connecttimestamp)
)) AS Duration,
COUNT(*) AS '#Calls',
sec_to_time(SUM(time_to_sec(s.disconnecttimestamp) - time_to_sec(s.connecttimestamp)) / COUNT(*)) AS Average
FROM
session s
JOIN mxuser u ON
s.ExtensionID1 = u.ExtensionId
OR s.ExtensionID2 = u.ExtensionId
WHERE
s.ServiceExtension1 IS NULL
AND s.connecttimestamp >= CURRENT_DATE
AND BINARY u.userprofilename = BINARY 'DBAM'
GROUP BY
u.firstname,
u.lastname
ORDER BY
Average DESC;
Output is as follows: -
Name Duration #Calls Average
DS 00:14:25 4 00:03:36
MG 00:17:23 11 00:01:34
TS 00:33:38 22 00:01:31
EW 01:04:31 43 00:01:30
AS 00:49:23 33 00:01:29
TH 00:43:57 35 00:01:15
SH 00:13:51 12 00:01:09
Well, you are able to get the number of total seconds, as you do before converting it to time. Why not take the number of total seconds, divide that by the number of calls, and then convert that back to time?
SELECT sec_to_time(
SUM(time_to_sec(s.disconnecttimestamp) - time_to_sec(s.connecttimestamp)) / COUNT(*))
AS averageDuration
If I understand correctly, you can just replace sum() with avg():
SELECT
CONCAT(LEFT(u.firstname,1),LEFT(u.lastname,1)) AS Name,
sec_to_time(SUM(
time_to_sec(s.disconnecttimestamp) - time_to_sec(s.connecttimestamp)
)) AS Duration,
COUNT(*) AS `#Calls`,
sec_to_time(AVG(
time_to_sec(s.disconnecttimestamp) - time_to_sec(s.connecttimestamp)
)) AS AvgDuration
Seems like all you need is another expression in the SELECT list. The SUM() aggregate (from the second expression) divided by COUNT aggregate (the third expr). Then wrap that in a sec_to_time function. (Unless I'm totally missing the question.)
Personally, I'd use the TIMESTAMPDIFF function to get a difference in times.
SEC_TO_TIME(
SUM(TIMESTAMPDIFF(SECOND,s.connecttimestamp,s.disconnecttimestamp))
/ COUNT(*)
) AS avg_duration
If what you are asking is there's a way to reference other expressions in the SELECT list by the alias... the answer is unfortunately, there's not.
With a performance penalty, you could use your existing query as an inline view, then in the outer query, the alias names assigned to the expressions are available...
SELECT t.Name
, SEC_TO_TIME(s.TotalDur) AS Duration
, s.`#Calls`
, SEC_TO_TIME(s.TotalDur/s.`#Calls`) AS avgDuration
FROM (
SELECT CONCAT(LEFT(u.firstname,1),LEFT(u.lastname,1)) AS Name
, SUM(TIMESTAMPDIFF(SECOND,s.connecttimestamp,s.disconnecttimestamp)) AS TotalDur
, COUNT(1) AS `#Calls`
FROM session s
-- the rest of your query
) t

Group by 2 mintues mysql

Mysql Table:
I want to take sum of acceleCount for 2 minutes intervals.
Query:
select time div 120000 as TwoMinutes,
sum(acceleCount) as Sum
from acceleTable
group by time div 120000
Result:
Here the twoMinutes column timestamp is meaning less. I want it to have a timestamp which is within the considering two minutes.
Any thoughts on how to change the sql query?
Bring the timestamps to a common denominator by dividing, rounding and multiplying:
SELECT
(ROUND(time / 120) * 120),
sum(acceleCount)
FROM acceleTable
GROUP BY (ROUND(time / 120) * 120)
A little optimized way to do it
SELECT (ROUND(time / 120000) * 120000) AS timekey, sum(acceleCount)
FROM acceleTable
GROUP BY timekey

mysql query to get age 25/04 months format where date format is 1988-04-04?

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