Ask Ben: Averaging Date/Time Stamps In SQL

Posted July 28, 2006 at 8:15 AM by Ben Nadel

Tags: SQL, Ask Ben

How can I average dates in a SQL call?

There are several types of date/time averaging in SQL. You can either average the date/time stamp as a whole, the date part of the date/time stamp, or the time portion of the date/time stamp. While I don't necessarily feel like all of those are worth discussing (due to usefulness), since I am not sure about what part you are asking, I will cover all three.

The secret to date/time averaging is that date/time stamps can be represented as a floating point number. I have covered this a number of times on this blog so I won't go into too much detail, but the idea is that as a floating point number, the integer part represents the number of days since the beginning of time (as the SQL server defines it) and the decimal part represents the time or rather, the fraction of days. SQL does not make this conversion for you; you have to CAST the date/time stamp as a FLOAT value:

  • CAST( date_time_value AS FLOAT )

Once you do this, it's easy to see how averaging can take place. You are probably used to running aggregates on numeric values. The FLOAT produced by a date/time cast is no different.

For the examples below, let's assume you have a web statistics database and you have a table that has user sessions. The field "date_created" is the date/time that a user has started a session on your web site. For this first example, let's just average the date/time session stamps as a whole:

  • <cfquery name="qFullAverage" datasource="..." username="..." password="...">
  • SELECT
  • CAST(
  • AVG(
  • CAST( date_created AS FLOAT )
  • )
  • AS DATETIME
  • )
  • FROM
  • web_stats_session
  • </cfquery>

As you can see, first we cast the date_created field to a FLOAT. Then, we run an average on the float fields. Keep in mind, this converts every date_created value to a FLOAT in the entire table and then runs the average on it. Once we have the average, we have to cast the FLOAT value back to DATETIME so that we can see it as a user-friendly date.

Averaging all date/time objects, to me, has little practical value. Averaging the date portion of a date/time stamp has a little more value, but not much more. However, if you want to average just the date part, it is basically the same operation. The only difference is that we have to strip out the time part, which as you will recall, is the decimal part of the floating point number. Now, here's a mathematical question: do you need to strip out the decimal part prior to averaging? My gut instinct tells me that it doesn't matter, but I am not a math whiz. Regardless, we definitely have to do it after the AVG:

  • <cfquery name="qDateOnlyAverage" datasource="..." username="..." password="...">
  • SELECT
  • CAST(
  • FLOOR(
  • AVG(
  • -- This inner FLOOR() call is optional (I think).
  • -- The averaging of the integer parts should
  • -- not be affected. Put it in if you want.
  • -- FLOOR(
  • CAST( date_created AS FLOAT )
  • --)
  • )
  • )
  • AS DATETIME
  • )
  • FROM
  • web_stats_session
  • </cfquery>

As you can see, we do pretty much the same thing as in the first example. The difference here is that once we are done averaging the FLOAT values, we FLOOR() the number to remove the time portion before we CAST it back to a DATETIME value.

Averaging times to me seems like the most practical of all the possible scenarios. Averaging time gives you more insight into trending in an informative manner. When averaging the times there is no doubt like above: we have to remove the integer part of the date/time FLOAT value BEFORE performing the average. To do this, we have to subtract the integer part from the full floating point value. After that, it's pretty much the same:

  • <cfquery name="qTimeOnlyAverage" datasource="..." username="..." password="...">
  • SELECT
  • CAST(
  • AVG(
  • -- Get full floating point value.
  • CAST( date_created AS FLOAT ) -
  • -- Subtract the integer part.
  • FLOOR(
  • CAST( date_created AS FLOAT )
  • )
  • )
  • AS DATETIME
  • )
  • FROM
  • web_stats_session
  • </cfquery>

Notice that in this example, we did not FLOOR() the resultant average. We want to keep the time part.

The trick to handling date/time stamps in SQL fast and effectively is remembering that you can represent them as FLOAT values. Numbers are faster to work with and make people more comfortable. After all, you're all used to dealing with numbers in SQL. Just don't forget to CAST things back to date/time.



Reader Comments

Aug 11, 2006 at 6:35 PM // reply »
1 Comments

I really appreciated your info on this because it was through using your CAST to FLOAT (or DECIMAL) and back out again as TIMESTAMP that I realized CF has this built in to the AVG function. Just AVG a list of dates and use DATEFORMAT on the output side and you'll get the same thing. :)


Aug 16, 2006 at 7:52 AM // reply »
74 Comments

Frank,

Word up. The more I mess around with Dates and Times, the more I realize how powerful they are and how manipulation of them can be really awesome. Good stuff all around.


Oct 16, 2006 at 9:29 AM // reply »
1 Comments

Hey Ben, Just wanna say thanks for this article, it helped me a lot today, in fact, i am bookmarking this site right now hehe.

Frank, just a note, I ate the dust completedly when I read your comment like 3 times, and I still didn't get a word. Like, what does CF mean? Also, DATEPART() can be used as output too?


Oct 16, 2006 at 9:34 AM // reply »
10,743 Comments

Rafael,

Glad to help. Hope you enjoy the site and thanks for bookmarking :)

Frank was refering to ColdFusion (CF). It's a server-side scripting language and is what most of my blog enteries are about. ColdFusion can average columns in a query once they come back from the database. If you are just using SQL, don't worry about what he said.


Feb 8, 2008 at 8:02 AM // reply »
1 Comments

From Argentina, Thank you very much for your tips, very useful


Mar 14, 2009 at 9:58 AM // reply »
1 Comments

This is a nice article. Got what i was looking for in an instant. Thks!


Oct 5, 2009 at 1:23 PM // reply »
1 Comments

Thanks Ben!


Dec 15, 2009 at 3:00 PM // reply »
1 Comments

Hi,

Thanks for th tip. Saved my day worth of work.

Vijay.
California.


Jul 29, 2010 at 11:27 AM // reply »
1 Comments

Excellent article - crystal clear

Thanks

David


Sep 30, 2010 at 9:58 AM // reply »
1 Comments

Hi,

I spent whole day to find out average of date time and failed... and at last, I got answer from your site...

Very Useful.. Thanks a Ton


Oct 3, 2010 at 9:50 PM // reply »
10,743 Comments

Cool guys, glad you liked this approach :)


May 17, 2011 at 7:27 AM // reply »
1 Comments

Ben, Thanks for this, I've been trying to get a quick and easy way to avg my times and this was easy to understand and very concise


Sep 13, 2011 at 11:28 PM // reply »
1 Comments

This was the number 1 result on google when searching for "sql average date"... and rightly so! Question answered, well done Ben!


Feb 1, 2012 at 1:22 PM // reply »
1 Comments

I had issues with the code running against data that overlapped midnight. I adjusted the code which seemed to give more accurate numbers.
For instance, if the data was 11PM,1AM,2AM,2AM, you would expect an average around midnight or 1AM.
Your math returns 28/4=7 or 7AM. My code breaks when avg across noon, so use accordingly.

  • convert(varchar,cast(avg(cast(backup_start as FLOAT)-.5 -FLOOR(CAST(backup_start as FLOAT)-.5))+.5 AS datetime),108) [avg_startTime2]



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 16, 2012 at 8:18 PM
Best Of ColdFusion 10 Contest Entry - HTML Email Utility
Just found this, looks good! I'm trying to run it on local, it's the 64bit version and I'm experiencing horrible lag. On average the generate.cfm processes the content change in 60-90 seconds. I've ... read »
May 16, 2012 at 6:40 PM
Maintaining Sessions Across Multiple ColdFusion CFHttp Requests
I am trying to integrate this CFHTTPsession into an application that will log into zeekrewards.com to post ads and I am not having any luck. The code works perfectly for logging into other websites, ... read »
May 16, 2012 at 2:44 PM
Creating A Sometimes-Fixed-Position Element With jQuery
Thank you, very useful technique! Worked like a charm. ... read »
May 16, 2012 at 1:58 PM
Movies As A Religious Experience
Acting can, in a way, ruin the movie-goer's experience. I used to be able to get so caught up in movies and their plots, and totally engaged. But lately, I haven't been able to as much with a lot o ... read »
May 16, 2012 at 1:52 PM
The Science Of Optimal Post-Exercise Nutrition
children of this age eat very less vegetables so u can opt for salads they will like it also carrot ,cucumber,onion and as far as pulses are concerned u can boil them ,give him along with mashed rice ... read »
May 16, 2012 at 1:34 PM
Strange ColdFusion JRUN Stack Overflow Error
Hey, Recently I updated my jrun4 using the latest updater 7 and now i am having memory issues :(:(:( any help is appreciated ... read »
May 16, 2012 at 9:56 AM
ColdFusion 10 Beta, Apache Tomcat, And Symbolic Links On Mac OSX
Hi, Now that ColdFusion 10 is out I have stumbled over this as well and I cannot figure out the proper solution. We're running virtual hosts via Apache2; the ColdFusion-applications store their fil ... read »
May 15, 2012 at 6:03 PM
Movies As A Religious Experience
@Ben, I don't know whether you'd consider this a religious observation, but it seems to me, in a sense, movies multiply how many lives we get to have. Each movie is like a little extra life we get ... read »