Introduction
Those of you that have worked extensively with dates in SQL Server (or any other relational database management system (RDBMS)) will know how finicky and complicated it can be to use DATE functions, DATEPART, DATENAME, DATEADD, GETDATE(), CURRENT_TIMESTAMP etc. Personally, I find it very useful to have a calendar table that stocks all of the necessary, pre-calculated fields in one place. Whether you’re doing BI or web, it can be very helpful to have a fixed ID for a date in order to really optimize your data analysis and processing. There are two points that you need to grasp in order to understand the following script:- Computed column values
- Recursive CTE (Common Table Expressions) queries.
Computed columns
In SQL Server it is possible to create columns in a table that are merely calculations based on another column. These are called computed columns and they can come in very handy. Take this query for example:
1 2 3 4 5 |
CREATE TABLE #idTest (ID INT NOT NULL IDENTITY(1,1), ID0 AS (ID -1), VALUE VARCHAR(32)) INSERT INTO #idTest(VALUE) VALUES('This'),('is'),('a'),('test') |
Recursive queries in SQL Server
This one is a bit more difficult to get your head around and don’t worry if you can’t understand it completely, the importance is that you understand the utility of it. I will try to explain it in a simple way. Basically, we create a temporary table and execute a select on a value in that temporary table. This creates a loop that we take advantage of. But don’t worry, SQL Server has a recursive limit of 100 by default. This means you won’t knock out the instance by executing an infinite loop unless you really want to. It is often useful to use recursive CTE to calculate hierarchies in a single query. You can check out more details on CTEs and recursive queries here. Once you have come to grips with these two concepts you will have the knowledge necessary to dynamically build your own calendar table. I have provided a basic example below which will create a date table with dates, years, semesters, trimesters, months, weeks and days. If you decide to implement such a table you can optimize storage by simply referencing a date ID in any table instead of stocking a full date. It will also allow you to select parts of dates instead of calculating them on the fly if you join to the calendar table.Table and data creation
In this DDL script you see the creation of one ID column, one date column and the rest being calculations based on the date “DATE” using computed column syntax. The computed columns are then stored on the disk using the PERSISTED key word.
1 2 3 4 5 6 7 8 9 10 11 |
CREATE TABLE [dbo].CALENDAR( [ID] INT IDENTITY(1,1) NOT NULL PRIMARY KEY, [DATE] DATE NOT NULL, [YEAR] AS (DATEPART(YEAR,[DATE])) PERSISTED, [SEMESTER] AS (CASE WHEN DATEPART(MONTH,[DATE]) < (7) THEN '1' ELSE '2' END) PERSISTED, [TRIMESTER] AS (CASE WHEN DATEPART(MONTH,[DATE]) < (4) THEN '1' ELSE CASE WHEN DATEPART(MONTH,[DATE]) < (7) THEN '2' ELSE CASE WHEN DATEPART(MONTH,[DATE]) < (10) THEN '3' ELSE '4' END END END) PERSISTED, [MONTH] AS (CASE WHEN LEN(CONVERT(VARCHAR(2),DATEPART(MONTH,[DATE])))=(1) THEN '0'+ CONVERT(VARCHAR(2),DATEPART(MONTH,[DATE])) ELSE CONVERT(VARCHAR(2),DATEPART(MONTH,[DATE])) END) PERSISTED, [WEEK] AS (CASE WHEN LEN(CONVERT(VARCHAR(2),DATEPART(WEEK,[DATE])))=(1) THEN '0'+ CONVERT(VARCHAR(2),DATEPART(WEEK,[DATE])) ELSE CONVERT(VARCHAR(2),DATEPART(WEEK,[DATE])) END), [DAY] AS (CASE WHEN LEN(CONVERT(VARCHAR(2),DATEPART(DAY,[DATE])))=(1) THEN '0'+ CONVERT(VARCHAR(2),DATEPART(DAY,[DATE])) ELSE CONVERT(VARCHAR(2),DATEPART(DAY,[DATE])) END) PERSISTED, ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
DECLARE @START_DATE DATETIME DECLARE @ENDDATE DATETIME SET @START_DATE = '20100101' SET @ENDDATE = '20151231' ; WITH CTE_DATES AS ( SELECT @START_DATE DateValue UNION ALL SELECT DateValue + 1 FROM CTE_DATES WHERE DateValue + 1 < @ENDDATE) INSERT INTO CALENDAR ([DATE]) SELECT CAST(DateValue AS date) FROM CTE_DATES OPTION (MAXRECURSION 0) |
See more
For BI documentation, consider ApexSQL Doc, a tool that documents SQL Server instances, databases, objects, SSIS packages, SSAS cubes, SSRS reports, Tableau server sites and SharePoint Server farms.Useful Resources
WITH common_table_expression (Transact-SQL) Data definition language Specify Computed Columns in a TableLatest posts by Evan Barke (see all)
- SQL Server Commands – Dynamic SQL - July 4, 2014
- SQL Server cursor performance problems - June 18, 2014
- SQL Server cursor tutorial - June 4, 2014