I find that it can be advantageous to use a Dates table, especially when doing a lot of date calculations. Learn what a Dates table is and how to create one, and then try it out for yourself.

What is a Dates table?

A Dates table stores a range of dates. Dates tables are very common in a DateWarehouse as a dimension table. You can also use Dates tables in OLTP databases for lookups. When programmers use Dates tables, they don't have to worry about using or designing functions for handling or formatting dates in the database. It is a precompilation of a wide range of date values and their associated month, quarter, year, etc.

Creating a Dates table

It's simple to create a Dates table -- it only takes a little TSQL programming. The script below creates the DateLookup table, which I will use throughout the rest of the example.

CREATE TABLE DateLookup
(
    DateKey INT PRIMARY KEY,
    DateFull DATETIME,
    CharacterDate VARCHAR(10),
    FullYear CHAR(4),
    QuarterNumber TINYINT,
    WeekNumber TINYINT,
    WeekDayName VARCHAR(10),
    MonthDay TINYINT,
    MonthName VARCHAR(12),
    YearDay SMALLINT,
    DateDefinition VARCHAR(30),
    WeekDay TINYINT,
    MonthNumber TINYINT
)

As you can see from the field names, the table contains detailed information regarding parts of a date, such as the name of the month, name of the day of the weekend, the quarter number, etc. It's very useful to have this information stored in a table for date searches based on certain months, quarters, and similar information.

The script below populates my DateLookup table with date information from the year 1900 through the end of 2100. I enter this large range of dates because I am not sure what type of dates I am going to handle in my tables, so I like to have a wide range available. This range likely won't cover erroneous dates in my tables, but it should do a pretty good job covering a large percentage of them.

DECLARE @Date DATETIME
SET @Date = \'1/1/1900\' 

WHILE @Date < \'1/1/2100\'
BEGIN
    INSERT INTO DateLookup
    (
        DateKey, DateFull, FullYear,
        QuarterNumber, WeekNumber, WeekDayName,
        MonthDay, MonthName, YearDay,
        DateDefinition,
               CharacterDate,
               WeekDay,
               MonthNumber
    )
    SELECT
        CONVERT(VARCHAR(8), @Date, 112), @Date, YEAR(@Date),
        DATEPART(qq, @Date), DATEPART(ww, @Date), DATENAME(dw, @Date),
        DATEPART(dd, @Date), DATENAME(mm, @Date), DATEPART(dy,@Date),
              DATENAME(mm, @Date) + \' \' + CAST(DATEPART(dd, @Date) AS CHAR(2)) + \',   
          \' + CAST(DATEPART(yy, @Date) AS CHAR(4)),
          CONVERT(VARCHAR(10), @Date, 101),
          DATEPART(dw, @Date),
          DATEPART(mm, @Date)
   
    SET @Date = DATEADD(dd, 1, @Date)
END

Using the DateLookup table

Once I load data into my DateLookup table, I can run queries against it. For example, the following query lists the number of Wednesdays in the year 2003:

SELECT WeekDayName, DayCount = COUNT(*) FROM DateLookup WHERE FullYear = 2003 AND WeekDayName = \'Wednesday\' GROUP BY WeekDayName

There were 52 Wednesdays in 2003.

The real power of using a Dates table comes when you use the table in conjunction with other tables. In the following example, I use my DateLookup table and join it to my SalesHistory table (from my article about generating dynamic SQL statements in SQL Server on the SaleDate. Performing this join makes it easier for me to analyse my sales information based on the date the sale occurred.

The following query ranks the month with the highest sales per product line:

SELECT *
FROM
(
        SELECT                
               dd.MonthName,
               Product,
               RecordCount = COUNT(*),
               Ranking = DENSE_RANK() OVER ( PARTITION BY Product ORDER BY COUNT(*) DESC, NEWID() DESC )
        FROM   
               DateLookup dd
               JOIN SalesHistory s ON dd.DateKey = CONVERT(VARCHAR(8), SaleDate, 112)
        GROUP BY
               dd.MonthName,
               Product
) a
WHERE Ranking = 1

This query ranks product sales per quarter per year:

SELECT FullYear, QuarterNumber, Product, SaleCount = COUNT(*) FROM DateLookup dd JOIN SalesHistory s ON dd.DateKey = CONVERT(VARCHAR(8), SaleDate, 112) GROUP BY FullYear, QuarterNumber, Product ORDER BY FullYear, QuarterNumber

Without the DateLookup table, this query would have to be written like this, which I think is more difficult:

SELECT DATEPART(yy, SaleDate), DATEPART(qq, SaleDate), Product, SaleCount = COUNT(*) FROM SalesHistory s GROUP BY DATEPART(yy, SaleDate), DATEPART(qq, SaleDate), Product ORDER BY DATEPART(yy, SaleDate), DATEPART(qq, SaleDate)

Drawback

The main drawback to using the Dates table is with the way in which I have to join it back to my OLTP tables. My Dates table does not contain time data, and my OLTP data almost certainly will; this means that joining the two tables requires some conversion on my part to do in the join. While this conversion in the join is not necessarily a huge deal, it may lead to poor performance under certain conditions.

Advertisement

Database Jumping This was published in Database Jumping, check every Thursday for more stories

Related links

Comments

1

Jack Corbett - 06/05/08

You could fix your conversion issue by storing the date start 1/1/1900 00:00:00 and a date end 1/2/1900 00:00:00 and then you could use >= and < in your join.

Also with the new date and time data types in SQL 2008 you could include start time and end time columns

» Report offensive content

2

Kent Butler - 06/05/08

A great advantage of this however, which is not explicitly stated, is that this can be applied to existing tables for the endless reporting scenarios. Great idea.

I wonder how the performance of this technique compares to the re-calculation of DATEPARTs over and over, which I would guess a lot of us are doing. Have you measured this?

» Report offensive content

3

Wayne - 07/05/08

Interesting. I had to do something recently where I needed the number of days elapsed between two dates but I had to exclude weekend days and holidays. It made it a bit of a challenge, ended up using a UDF with a date table. I also had to include the time component in the comparison, which was an additional level of difficulty.

I'll have to load this up and play with it, I might be able to simplify my process. I can see the benefit in a DW, but we're strictly OLTP right now. Still, it'll be interesting to experiment with.

» Report offensive content

4

Wayne - 07/05/08

You might also consider a "numbers" table, you can go from a 77,000 iteration loop doing one insert per pass to a single insert statement.

» Report offensive content

5

James Horsley - 07/05/08

Your "no time" drawback could be helped bay having a NextDateFull datetime column as well (being 1 day after your date - so if date is 1 May 2008 with time 00:00:00.000 NextDateFull is 2 May 2008 with time 00:00:00.000 you can then join on (OtherTableDateCol>=DateFull AND OtherTableDateCol<NextDateFull) and things will fall into correct row regardless of whether or not there are times on them

» Report offensive content

6

James Horsley - 07/05/08

Sorry - should have read other comments before posting mine - Jack has already suggested much the same thing

» Report offensive content

7

James Horsley - 07/05/08

Another idea - add a bit field IsHoliday that can be set true on all public holidays for working day counts and maybe a varchar HolidayName field too

» Report offensive content

8

Mark - 07/05/08

The Public Holiday issue is a complex one. Because not only do different countries have different holidays, different states also have different holidays. You really need a separate table for holidays, because you can't even rely on timezones to sort that mess out.

» Report offensive content

9

Wayne - 08/05/08

Public holidays is definitely a complex one. For my elapsed days function I have an insert statement for the fixed-date American holidays: 12/25, 1/1, 11/11, 7/4; and if they fall on a weekend, well that record is already there and they're not inserted. The woman who uses the DB knows that after city council approves next year's holidays, she needs to make sure that they are added to the table.

If you had to take into account other countries, I'd go for a table consisting of date, holiday name, and ISO country code. Have you ever added the Japanese or Hebrew holidays to Outlook? It's a heck of a list. You'd definitely need knowledge of the nation to know which are actual holidays and which are observances/non-holidays.

» Report offensive content

10

ABIRLAL SEN - 08/05/08

HOW TO WRITE TRIGGER IN SQL SERVER 2005?

» Report offensive content

11

Ulrich Boeck - 09/05/08

yes... but:

isn't it a big deal to mimic dwh behaviour on an oltp database with something like a fact table?.

In order to get your grouping that way you have to
- populate the dates into the dates table ("who")
- establish a join with your "fact" table with a convert(varchar.. and implicitely a cast to int

i have the feeling that this does not perform well on huge data amounts

» Report offensive content

12

Payur - 24/05/08

I am getting the following error when I execute the second script that is DECLARE @Date DATETIME
SET @Date = \'1/1/1900\' :

This is on SQL 2005.


Msg 102, Level 15, State 1, Line 2
Incorrect syntax near '1/1/1900\'.

Please advice

DECLARE @Date DATETIME
SET @Date = \'1/1/1900\'


ERROR:
Msg 102, Level 15, State 1, Line 2
Incorrect syntax near '1/1/1900\'.

» Report offensive content

13

HanShi - 23/06/08

@Payur

Remove both backslashes (\) and it'll work

» Report offensive content

Leave a comment

You must read and type the 6 chars within 0..9 and A..F

* indicates mandatory fields.

13

HanShi - 23/06/08

@Payur Remove both backslashes (\) and it'll work ... more

12

Payur - 24/05/08

I am getting the following error when I execute the second script that is DECLARE @Date DATETIME SET @Date = \'1/1/1900\' : This ... more

11

Ulrich Boeck - 05/09/08

yes... but: isn't it a big deal to mimic dwh behaviour on an oltp database with something like a fact table?. In ... more

Log in


Sign up | Forgot your password?

  • Lana Kovacevic What's new in GWT 1.5?

    I recently wrote an introduction to the Google Web Toolkit based on Lars Rasmussen's session at the Google Developer Day 2008 in Sydney. Following the introductory session Lars gave us a deeper insight into GWT, particularly what's new in version 1.5. Read more »

    -- posted by Lana Kovacevic

  • Lana Kovacevic The Portal of the Future

    At this year's Gartner Application Development, Integration and Web Services Summit, I attended Gene Phifer talk: "Portal of the Future: What's Beyond Web 2.0?". Read more »

    -- posted by Lana Kovacevic

  • Staff Google's new foray into image search

    Google is developing visual crawling software that can be used for facial recognition and scene analysis. In addition images can be matched with display ads and utilise geotagging information for various applications. Read more »

    -- posted by Staff

What's on?

  • Club Builder: Sports, Gates and Gears

    This week on Club Builder: Steve Ballmer gives a teary goodbye to Bill Gates, Mark Taylor moves into IT endorsements and we ask some Google Gears questions.