As I mentioned in Automating a Date Dimension Source, an intra-day time dimension can also be sourced from an automated common table expression (CTE). Before I explain how I sourced a time dimension from a CTE, I should explain my time dimension and why I needed it.

My time dimension supported a business need down to the second for every minute in every hour of each day. Think point of sales transaction. When you are busy, your cash registers can ring up multiple sales within a minute. Your business needs to know where your sales peaks occur throughout the day. Early morning? Late afternoon? Overnight?

Just as with the date dimension, database functions like DATEPART() might help segment data over some time continuum, but leveraging such functions on reports impacts query performance, code complexity and data interpretation.

Why not handle all of those seconds, minutes and hours in a centralized time dimension where query performance is optimized and everyone uses standardized data hierarchy rules? Consider a DimTime table with time rollups.

You might wonder why I have a date dimension for days, weeks, months, etc. separate from a time dimension for seconds, minutes and hours. The short answer is practicality. Each day has 86,400 seconds (i.e., 60 seconds per minute, 60 minutes per hour, 24 hours per day). That means one 365-day year has 31,536,000 seconds. A dimension with 31+ million rows per year is just not going to perform well.

So I created a date dimension that enabled analysts to easily traverse a day-month-year hierarchy and a separate time dimension that enabled analysts to easily traverse a second-minute-hour hierarchy.

Of course, I just intended to explain how I sourced my time dimension from a CTE.

Beginning with SQL Server 2005, Common Table Expression (CTE) offered that better solution. A CTE is a query which returns a temporary result set that can reference itself. The CTE is then used as a source for a subsequent query. Rather than delve into the details of CTE syntaxes, I want to explain how a recursive temporary result set enables me to simplify my time dimension maintenance.

The following annotated query uses a recursive CTE to generate the value of each second within a 24-hour day.

-- Build a CTE for every second in a 24-hour day
WITH cteTime AS (
 SELECT CAST('00:00:00' AS TIME(0)) ClockTime
 UNION ALL
 SELECT DATEADD(SECOND,1,ClockTime)
 FROM cteTime
 WHERE ClockTime < cast('23:59:59'="" as="" time(0)))="" ="" --="" using="" the="" cte="" of="" seconds,="" derive="" descriptions,="" bands,="" etc.="" select="" --="" common="" attributes="" ((datepart(hour,clocktime)="" *="" 10000)="" +="" (datepart(minute,clocktime)="" *="" 100)="" +="" datepart(second,clocktime))="" as="" timekey,="" clocktime,="" convert(varchar(10),clocktime,109)="" as="" time12hourdescription,="" datepart(hour,clocktime)="" as="" hourid,="" datepart(minute,clocktime)="" as="" minuteid,="" case="" when="" datepart(minute,clocktime)="">< 15="" then="" 1="" when="" datepart(minute,clocktime)="">< 30="" then="" 2="" when="" datepart(minute,clocktime)="">< 45="" then="" 3="" when="" datepart(minute,clocktime)="">< 60="" then="" 4="" end="" as="" quarterhourid,="" --="" custom="" attributes="" case="" when="" clocktime="" between="" '00:00:00'="" and="" '05:59:59'="" then="" 'over="" night'="" when="" clocktime="" between="" '06:00:00'="" and="" '11:59:59'="" then="" 'morning'="" when="" clocktime="" between="" '12:00:00'="" and="" '17:59:59'="" then="" 'afternoon'="" when="" clocktime="" between="" '18:00:00'="" and="" '23:59:59'="" then="" 'evening'="" end="" as="" periodname="" from="" ctetime="" option="" (maxrecursion="">

The top 10 rows returned from the query above appear below.

TimeKey

ClockTime

Time12HourDescription

HourID

MinuteID

QuarterHourID

PeriodName

0

0:00:00

12:00:00AM

0

0

1

Over Night

1

0:00:01

12:00:01AM

0

0

1

Over Night

2

0:00:02

12:00:02AM

0

0

1

Over Night

3

0:00:03

12:00:03AM

0

0

1

Over Night

4

0:00:04

12:00:04AM

0

0

1

Over Night

5

0:00:05

12:00:05AM

0

0

1

Over Night

6

0:00:06

12:00:06AM

0

0

1

Over Night

7

0:00:07

12:00:07AM

0

0

1

Over Night

8

0:00:08

12:00:08AM

0

0

1

Over Night

9

0:00:09

12:00:09AM

0

0

1

Over Night

The top 10 rows returned from the query above appear below.

The query above should be executable in any SQL Server 2005 or later version. Use that query as a PowerPivot time source and you have an instant ad hoc time dimension. Use that query as a data source in SSIS and you have a package that can maintain your enterprise data warehouse indefinitely.

Need to update your attributes with new business rules? Simply alter the query logic and derive the attributes using the new logic. Because the logic is coded, you can easily manage changes using source controllers such as Team Foundation Server (TFS).

If you are interested in how this logic can create a date dimension, just see my earlier explanation on Automating a Date Dimension Source.