{"id":460,"date":"2012-05-20T09:09:09","date_gmt":"2012-05-20T09:09:09","guid":{"rendered":"http:\/\/bicortex.com\/?p=460"},"modified":"2013-10-08T21:52:25","modified_gmt":"2013-10-08T21:52:25","slug":"populate-date-dimension-table","status":"publish","type":"post","link":"http:\/\/bicortex.com\/bicortex\/populate-date-dimension-table\/","title":{"rendered":"How To Populate Date Dimension Table"},"content":{"rendered":"<p style=\"text-align: justify;\">I am confident that there is already a number (probably tens if not hundreds) of Internet posts which deal with the process of &#8216;DATE&#8217; dimension table population and I thought long and hard whether I should spend my time to reiterate this information; however, how we customize this routine is very individual and there is no &#8216;one size fits all\u2019 doctrine on how to structure the ETL or query to get the best results. Not trying to echo what has already been said, I thought it would be worthwhile to provide a quick rundown on what I have grown accustomed to when it comes to creating or updating the \u2018DATE\u2019 dimension, the linchpin of any dimensional data mart model. This process can be further customized to accommodate any changes or additions based on the business needs.<\/p>\n<p style=\"text-align: justify;\">Nearly any dimensional table can be created using an SSIS package but in this case DTS job just adds to maintenance overhead and complexity of the whole solution, especially, that once populated, Dim_Date does not require further updates or ongoing maintenance. Therefore, this post demonstrates only how to do it via SQL. Also, many people use Excel spreadsheet (a good sample can be downloaded from the Kimbal Group\u2019s website) and simply import this into a table via \u2018Import Data\u2019 database task or a job\/package. It is a great solution; however, in spite of Excel powerful formulae pool, more complex date derivatives cannot be easily generated. Besides, once most of the Dim_Date table structure is set in concrete, it is easy, quick and powerful method which can be reused (partially or fully) in other projects.<\/p>\n<p style=\"text-align: justify;\">Firstly, we need to create an empty table (Dim_Date) to populate with \u2018datetime\u2019 values and their derivatives. It will also be assigned a primary key (Date_Key) as a surrogate key which will become a foreign key in a fact table. We also create a unique index on a Date_Time column to enable faster lookups. The code for all these functions is as follows:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nIF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'&#x5B;dbo].&#x5B;Dim_Date]') AND type in (N'U'))\r\nDROP TABLE &#x5B;dbo].&#x5B;Dim_Date]\r\nGO\r\nCREATE TABLE &#x5B;dbo].&#x5B;Dim_Date](\r\n&#x5B;Date_Key]\t\t\t\t\t\t&#x5B;int]\t\t\tNOT NULL,\r\n&#x5B;Date_Time]\t\t\t\t\t\t&#x5B;datetime]\t\tNOT NULL,\r\n&#x5B;Calendar_Date]\t\t\t\t\t&#x5B;date]\t\t\tNOT NULL,\r\n&#x5B;Calendar_Year]\t\t\t\t\t&#x5B;int]\t\t\tNOT NULL,\r\n&#x5B;Calendar_Quarter]\t\t\t\t&#x5B;char](8)\t\tNOT NULL,\r\n&#x5B;Is_Leap_Year]\t\t\t\t\t&#x5B;int]\t\t\tNOT NULL,\r\n&#x5B;Calendar_Quarter_Number]\t\t&#x5B;int]\t\t\tNOT NULL,\r\n&#x5B;Calendar_Year_Quarter_Name]\t&#x5B;varchar](12)\tNOT NULL,\r\n&#x5B;Calendar_Month]\t\t\t\t&#x5B;int]\t\t\tNOT NULL,\r\n&#x5B;Calendar_Month_Number]\t\t\t&#x5B;int]\t\t\tNOT NULL,\r\n&#x5B;Calendar_Month_Name]\t\t\t&#x5B;varchar](10)\tNOT NULL,\r\n&#x5B;Calendar_Year_Month_Name]\t\t&#x5B;varchar](15)\tNOT NULL,\r\n&#x5B;Calendar_Month_Start]\t\t\t&#x5B;int]\t\t\tNOT NULL,\r\n&#x5B;Calendar_Month_End]\t\t\t&#x5B;int]\t\t\tNOT NULL,\r\n&#x5B;Calendar_Week]\t\t\t\t\t&#x5B;int]\t\t\tNOT NULL,\r\n&#x5B;Week_Day]\t\t\t\t\t\t&#x5B;int]\t\t\tNOT NULL,\r\n&#x5B;Week_Day_Name]\t\t\t\t\t&#x5B;varchar](10)\tNOT NULL,\r\n&#x5B;Week_Day_Type]\t\t\t\t\t&#x5B;varchar](8)\tNOT NULL,\r\n&#x5B;Days_In_Calendar_Year]\t\t\t&#x5B;smallint]\t\tNOT NULL,\r\n&#x5B;Days_In_Calendar_Month]\t\t&#x5B;smallint]\t\tNOT NULL,\r\n&#x5B;Days_In_Calendar_Week]\t\t\t&#x5B;smallint]\t\tNOT NULL,\r\n&#x5B;Weeks_In_Calendar_Month]\t\t&#x5B;smallint]\t\tNOT NULL,\r\n&#x5B;Weeks_In_Calendar_Year]\t\t&#x5B;smallint]\t\tNOT NULL,\r\n&#x5B;Public_Holiday_Flag]\t\t\t&#x5B;varchar] (8)\tNULL,       \t--Varies depending on the location e.g. country, region\r\n&#x5B;Financial_Year]\t\t\t\t&#x5B;int]\t\t\tNOT NULL,\t\t--Based on Australian standard financial year start and end dates\r\n&#x5B;Financial_Quarter]\t\t\t\t&#x5B;smallint]\t\tNOT NULL    \t--Based on Australian standard financial year start and end dates\r\n \r\nCONSTRAINT &#x5B;PK_DimDate] PRIMARY KEY CLUSTERED\r\n(\r\n&#x5B;Date_Key] ASC\r\n)\r\n) ON &#x5B;PRIMARY]\r\nGO\r\nCREATE UNIQUE INDEX Idx_DimDate ON Dim_Date(Date_Key)\r\nGO\r\nSET DATEFIRST 1    --If first day of week is a Monday. If first day of week = Saturday then set to 6\r\nGO\r\n<\/pre>\n<p style=\"text-align: justify;\">Next, once the table has been created, we can populate it with the data which will be defined using a number of functions and formulae encapsulated in a simple stored procedure based around two variables determining the start and end dates for the table content i.e. how far back in history or how far into the future in time you would like to go. The below code creates stored procedure which we will execute to populate our Dim_Date table. <\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nCREATE PROCEDURE dbo.usp_Populate_Date_Dimension\r\n(\r\n@Start_Day\t\tdate,\r\n@End_Day\t\tdate,\r\n@Final_Message  VARCHAR (150) OUTPUT\r\n)\r\nAS \r\n\r\nBEGIN\r\nBEGIN TRY\r\n\r\n--Create variable to handle record count for verification purposes\r\nDECLARE  @Record_Count\tint;\r\n\r\n--Loop through all dates in the specified date range and insert dates and their derivatives into 'Dim_Date' dimension table\r\nWITH DateCTE AS\r\n(\r\n  SELECT CAST(@Start_Day as datetime) AS Date_Value\r\n  UNION ALL\r\n  SELECT DATEADD(dd, 1, Date_Value)\r\n  FROM DateCTE\r\n  WHERE DATEADD(dd, 1, Date_Value) &lt;= @End_Day\r\n)\r\n \r\nINSERT INTO dbo.Dim_Date\r\n\tSELECT \r\n\t&#x5B;Date_Key]\t\t\t\t\t\t=\tCAST(CONVERT(CHAR(8),CAST(Date_Value as DATETIME),112) as INT),\r\n\t&#x5B;Date_Time]\t\t\t\t\t\t=\tDate_Value,\r\n\t&#x5B;Calendar_Date]\t\t\t\t\t=\tCAST(Date_Value as DATE),\r\n\t&#x5B;Calendar_Year]\t\t\t\t\t=\tYEAR(Date_Value),\r\n\t&#x5B;Calendar_Quarter]\t\t\t\t=\tCAST(YEAR(Date_Value) AS CHAR(4)) + CAST(DATEPART(Quarter ,Date_Value) AS CHAR(1)),\r\n\t&#x5B;Is_Leap_Year]\t\t\t\t\t=\tCASE WHEN DATEPART(day,DATEADD(day,-1,CAST(DATENAME(year,Date_Value) + '-03-01' AS date)))=29 THEN 1 ELSE 0 END,\r\n\t&#x5B;Calendar_Quarter_Number]\t\t=\tDATEPART(Quarter ,Date_Value),\r\n\t&#x5B;Calendar_Year_Quarter_Name]\t=\tDATENAME(YEAR,Date_Value) + ' Qtr' + DATENAME(QUARTER,Date_Value),\r\n\t&#x5B;Calendar_Month]\t\t\t\t=\tCAST(CONVERT(CHAR(6),CAST(Date_Value AS DATETIME),112) AS INT),\r\n\t&#x5B;Calendar_Month_Number]\t\t\t=\tDATEPART(m,Date_Value),\r\n\t&#x5B;Calendar_Month_Name]\t\t\t=\tDATENAME(MONTH,Date_Value),\r\n\t&#x5B;Calendar_Year_Month_Name]\t\t=\tCAST(YEAR(Date_Value) AS CHAR(4)) + ' ' + DATENAME(MONTH,Date_Value),\r\n\t&#x5B;Calendar_Month_Start]\t\t\t=\tCONVERT(CHAR(6),CAST(Date_Value AS DATETIME),112) + '01',\r\n\t&#x5B;Calendar_Month_End]\t\t\t=\tCAST(CONVERT(CHAR(8),CAST(CONVERT(VARCHAR(12), dateadd(day,-1 * DAY(DATEADD(MONTH,1,Date_Value)),\r\n\t\t\t\t\t\t\t\t\t\tDATEADD(month,1,Date_Value)),113) AS DATETIME),112) AS INT),\r\n\t&#x5B;Calendar_Week]\t\t\t\t\t=\tCAST(YEAR(Date_Value) AS CHAR(4)) + CAST(DATEPART(wk ,Date_Value) AS VARCHAR(2)),\r\n\t&#x5B;Week_Day]\t\t\t\t\t\t=\tDATEPART(dw, Date_Value),\r\n\t&#x5B;Week_Day_Name]\t\t\t\t\t=\tDATENAME(dw, Date_Value),\r\n\t&#x5B;Week_Day_Type]\t\t\t\t\t=\tCASE WHEN DATENAME(dw, Date_Value) IN ('Saturday','Sunday') \r\n\t\t\t\t\t\t\t\t\t\tTHEN 'Weekend' ELSE 'Weekday' END,  --Day_Type_Code function from previous post may also be used here\r\n\t&#x5B;Days_In_Calendar_Year]\t\t\t=\tCOUNT(DATEPART(wk ,Date_Value)) OVER (PARTITION BY YEAR(Date_Value)),\r\n\t&#x5B;Days_In_Calendar_Month]\t\t=\tCOUNT(*) OVER (PARTITION BY CAST(CONVERT(CHAR(6),CAST(Date_Value AS DATETIME),112) AS INT)),\r\n\t&#x5B;Days_In_Calendar_Week]\t\t\t=\tCOUNT(*) OVER (PARTITION BY CAST(YEAR(Date_Value) AS CHAR(4)) + CAST(DATEPART(wk ,Date_Value) AS VARCHAR(2)) ),\r\n\t&#x5B;Weeks_In_Calendar_Month]\t\t=\t(SELECT DISTINCT COUNT(DATEPART(wk,b.Date_Value)) OVER (PARTITION BY DATEPART(m,b.Date_Value))\r\n\t\t\t\t\t\t\t\t\t\tFROM DateCTE b  \r\n\t\t\t\t\t\t\t\t\t\tWHERE\r\n\t\t\t\t\t\t\t\t\t\tDATEPART(m,a.Date_Value) = DATEPART(m,b.Date_Value)\r\n\t\t\t\t\t\t\t\t\t\tAND YEAR(a.Date_Value) = YEAR(b.Date_Value)\r\n\t\t\t\t\t\t\t\t\t\tAND DATEPART(dw, Date_Value) = (1)),\t\t\t\t\t\t\t\t\t\r\n\t&#x5B;Weeks_In_Calendar_Year]\t\t=\t(SELECT DISTINCT COUNT(DATEPART(wk ,Date_Value)) OVER (PARTITION BY YEAR(Date_Value))\r\n\t\t\t\t\t\t\t\t\t\tFROM DateCTE b\r\n\t\t\t\t\t\t\t\t\t\tWHERE YEAR(a.Date_Value) = YEAR(b.Date_Value)\r\n\t\t\t\t\t\t\t\t\t\tAND DATEPART(dw, Date_Value) = (1)),\r\n\t&#x5B;Public_Holiday_Flag]\t\t\t=\tNULL,\r\n\t&#x5B;Financial_Year]\t\t\t\t=\tCASE WHEN datepart(mm, (select dateadd(d,datediff(d,0,dateadd(s,-1,dateadd(m,datediff(m,0,Date_Value)+1,0))),0))) &gt; 6\r\n\t\t\t\t\t\t\t\t\t\tTHEN datepart(yy, (select dateadd(d,datediff(d,0,dateadd(s,-1,dateadd(m,datediff(m,0,Date_Value)+1,0))),0))) + 1\r\n\t\t\t\t\t\t\t\t\t\tELSE datepart(yy,(select dateadd(d,datediff(d,0,dateadd(s,-1,dateadd(m,datediff(m,0,Date_Value)+1,0))),0))) END,\r\n\t&#x5B;Finacial_Quarter]\t\t\t\t=\tCASE WHEN datepart(mm, Date_Value) BETWEEN 7 AND 9 THEN 1\r\n\t\t\t\t\t\t\t\t\t\tWHEN datepart(mm, Date_Value) BETWEEN 10 AND 12 THEN 2\r\n\t\t\t\t\t\t\t\t\t\tWHEN datepart(mm, Date_Value) BETWEEN 1 AND 3 THEN 3\r\n\t\t\t\t\t\t\t\t\t\tWHEN datepart(mm, Date_Value) BETWEEN 4 AND 6 THEN 4 END\r\n\tFROM DateCTE a\r\n\tORDER BY Date_Key\r\nOPTION (MAXRECURSION 0)\r\n\r\nSET  @Record_Count = @@ROWCOUNT;\r\n\r\n--Disable identity column to insert explicit values into Dim_Date table for handling unknown members\r\nIF EXISTS\t(SELECT   c.is_identity\r\n\t\t\tFROM   sys.tables t\r\n            JOIN sys.schemas s\r\n            ON t.schema_id = s.schema_id\r\n            JOIN sys.Columns c\r\n            ON c.object_id = t.object_id\r\n            JOIN sys.Types ty\r\n            ON ty.system_type_id = c.system_type_id\r\n\t\t\tWHERE  t.name = 'Dim_date'\r\n            AND s.Name = 'dbo'\r\n            AND c.is_identity=1)\r\nSET IDENTITY_INSERT dbo.dim_date ON\r\n\r\nDECLARE @Day date\r\nSET\t\t@Day='9999-12-31'\r\n\r\n--Insert one records for handling unknown members during date lookups\t\t  \r\nINSERT INTO dbo.Dim_Date\r\n(\r\n    &#x5B;Date_Key],\r\n\t&#x5B;Date_Time],\r\n\t&#x5B;Calendar_Date],\r\n\t&#x5B;Calendar_Year],\r\n\t&#x5B;Calendar_Quarter],\r\n\t&#x5B;Is_Leap_Year],\r\n\t&#x5B;Calendar_Quarter_Number],\r\n\t&#x5B;Calendar_Year_Quarter_Name],\r\n\t&#x5B;Calendar_Month],\r\n\t&#x5B;Calendar_Month_Number],\r\n\t&#x5B;Calendar_Month_Name],\r\n\t&#x5B;Calendar_Year_Month_Name],\r\n\t&#x5B;Calendar_Month_Start],\r\n\t&#x5B;Calendar_Month_End],\r\n\t&#x5B;Calendar_Week],\r\n\t&#x5B;Week_Day],\r\n\t&#x5B;Week_Day_Name],\r\n\t&#x5B;Week_Day_Type],\r\n\t&#x5B;Days_In_Calendar_Year],\r\n\t&#x5B;Days_In_Calendar_Month],\r\n\t&#x5B;Days_In_Calendar_Week],\r\n\t&#x5B;Weeks_In_Calendar_Month],\r\n\t&#x5B;Weeks_In_Calendar_Year],\r\n\t&#x5B;Public_Holiday_Flag],\t\t\t\r\n\t&#x5B;Financial_Year],\t\t\t\t\r\n\t&#x5B;Financial_Quarter] \r\n)\r\nSELECT \r\n\t&#x5B;Date_Key]\t\t\t\t\t\t=\tCAST(CONVERT(CHAR(8),CAST(@Day as DATETIME),112) as INT),\r\n\t&#x5B;Date_Time]\t\t\t\t\t\t=\t@Day,\r\n\t&#x5B;Calendar_Date]\t\t\t\t\t=\tCAST(@day as date),\r\n\t&#x5B;Calendar_Year]\t\t\t\t\t=\t-1,\r\n\t&#x5B;Calendar_Quarter]\t\t\t\t=\t'UNKNOWN',\r\n\t&#x5B;Is_Leap_Year]\t\t\t\t\t=\t-1,\r\n\t&#x5B;Calendar_Quarter_Number]\t\t=\t-1,\r\n\t&#x5B;Calendar_Year_Quarter_Name]\t=\t'UNKNOWN',\r\n\t&#x5B;Calendar_Month]\t\t\t\t=\t-1,\r\n\t&#x5B;Calendar_Month_Number]\t\t\t=\t-1,\r\n\t&#x5B;Calendar_Month_Name]\t\t\t=\t'UNKNOWN',\r\n\t&#x5B;Calendar_Year_Month_Name]\t\t=\t'UNKNOWN',\r\n\t&#x5B;Calendar_Month_Start]\t\t\t=\t-1,\r\n\t&#x5B;Calendar_Month_End]\t\t\t=\t-1,\r\n\t&#x5B;Calendar_Week]\t\t\t\t\t=\t-1,\r\n\t&#x5B;Week_Day]\t\t\t\t\t\t=\t-1,\r\n\t&#x5B;Week_Day_Name]\t\t\t\t\t=\t'UNKNOWN',\r\n\t&#x5B;Week_Day_Type]\t\t\t\t\t=\t'UNKNOWN',\r\n\t&#x5B;Days_In_Calendar_Year]\t\t\t=\t-1,\r\n\t&#x5B;Days_In_Calendar_Month]\t\t=\t-1,\r\n\t&#x5B;Days_In_Calendar_Week]\t\t\t=\t-1,\r\n\t&#x5B;Weeks_In_Calendar_Month]\t\t=\t-1,\r\n\t&#x5B;Weeks_In_Calendar_Year]\t\t=\t-1,\r\n\t&#x5B;Public_Holiday_Flag]\t\t\t=\t-1,\r\n\t&#x5B;Financial_Year]\t\t\t\t=\t-1,\r\n\t&#x5B;Financial_Quarter]\t\t\t\t=\t-1\r\n\t\r\nSET  @Record_Count = @Record_Count + @@ROWCOUNT\t\r\n\t\r\n--Enable identity column on the Dim_Date table\r\nIF EXISTS\t(SELECT   c.is_identity\r\n\t\t\tFROM   sys.tables t\r\n            JOIN sys.schemas s\r\n            ON t.schema_id = s.schema_id\r\n            JOIN sys.Columns c\r\n            ON c.object_id = t.object_id\r\n            JOIN sys.Types ty\r\n            ON ty.system_type_id = c.system_type_id\r\n\t\t\tWHERE  t.name = 'Dim_date'\r\n            AND s.Name = 'dbo'\r\n            AND c.is_identity=1)\r\nSET IDENTITY_INSERT dbo.dim_date OFF\t\r\n\r\nIF @record_Count &lt;&gt; (SELECT CAST (DATEDIFF(DAY, @Start_Day, @End_Day) + 2 as INT))\r\n\tSET @Final_Message = 'Something went wrong! Your table has not been populated.'\r\n\tELSE \r\n\tSET @Final_Message = 'Everything went well! The data should now be inserted into your table.'\r\nEND TRY\r\n\r\nBEGIN CATCH\r\n\t\tIF @@TRANCOUNT &gt; 0\r\n\t\tBEGIN\r\n\t\t\tROLLBACK TRANSACTION\r\n\t\tEND\t\r\n\t\tDECLARE @ErrorMessage NVARCHAR(4000);\r\n\t\tDECLARE @ErrorSeverity INT;\r\n\t\tDECLARE @ErrorState INT;\r\n\t\tSELECT \r\n\t\t\t@ErrorMessage = ERROR_MESSAGE(),\r\n\t\t\t@ErrorSeverity = ERROR_SEVERITY(),\r\n\t\t\t@ErrorState = ERROR_STATE();\r\n\t\tRAISERROR (@ErrorMessage, -- Message text.\r\n\t\t\t\t   @ErrorSeverity, -- Severity.\r\n\t\t\t\t   @ErrorState -- State.\r\n\t\t\t\t   );\r\nEND CATCH\r\n\tIF @@TRANCOUNT &gt; 0\r\n\tBEGIN\r\n\t\tCOMMIT TRANSACTION\r\n\tEND\r\nEND\r\n<\/pre>\n<p style=\"text-align: justify;\">The above query is pretty straightforward \u2013 create CTE (Common Table Expression), populate it with a range of dates also using query hint (MAXRECURSION) to provide looping and finally create all date derivatives i.e. attributes of the Dim_Date dimension that the business can potentially look for\/need as far as reporting and analytics functions. It is essential to ensure that the most common or requested date derivatives are calculated during this process rather than during report query building or cube\/fact table population as this will enable the creation of a central repository for date data, also ensuring that unnecessary server resources are not wasted during further ETL processes, cube population, query execution etc. In most cases this can be established early on i.e. during the business requirements gathering phase or looking at sample legacy reports. Also, as this information is fairly rigid and not susceptible to ongoing changes (unlike other dimensions requiring Slowly Changing Dimensions functionality to cater for future alterations), future updates and maintenance should be fairly easy.<\/p>\n<p style=\"text-align: justify;\">OK, now that we&#8217;ve got the stored procedure compiled, let&#8217;s execute it. You can do that from SQL Server Management Studio or simply by running the following code (note the start and end dates as the table boundaries):<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nDECLARE\t@return_value int,\r\n\t\t@Final_Message varchar(150)\r\nEXEC\t@return_value = &#x5B;dbo].&#x5B;usp_Populate_Date_Dimension]\r\n\t\t@Start_Day = 2012\/01\/01,\r\n\t\t@End_Day = 2013\/12\/31,\r\n\t\t@Final_Message = @Final_Message OUTPUT\r\nSELECT\t@Final_Message as N'@Final_Message'\r\nSELECT\t'Return Value' = @return_value\r\nGO\r\n<\/pre>\n<p style=\"text-align: justify;\">You should now be able query the table content and see all the dates and related attributes. Notice that Public_Holiday_Flag attribute in the Dim_Date table can accommodate NULL values and that during the population process we insert NULLs into it. This is because this column will be populated separately due to its nature i.e. these values cannot be computed or derived and need to be inserted manually. The existence of this field is not applicable to all environments but anywhere where KPIs are calculated based on a number of days from or to an event and the exclusion of public holidays is taken into account, this flag is necessary. Unfortunately, this process requires manual updates to a separate lookup table where public holidays are stored although I have seen automated attempts to pull this information from an online feed instead. As much as I am an \u201cautomation junkie\u201d, I tend to do it manually as it only takes 2 minutes to gather the date values for the whole year. To populate public holidays flag field I\u2019ve created a control table which I update once a year based on the yearly public holidays calendar issued by the state government. As in Australia not all public holidays are celebrated in all states equally, this may require some modifications based on your business requirements e.g. you may have a number of dates pointing to public holidays for each state\/province.<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nCREATE TABLE &#x5B;dbo].&#x5B;Public_Holidays](\r\n&#x5B;Public_Holiday_Date] &#x5B;date] NOT NULL\r\n) ON &#x5B;PRIMARY]\r\nGO\r\n<\/pre>\n<p style=\"text-align: justify;\">All there is left to finish the Dim_Date table population is to issue an UPDATE statement to populate the column according to the Public_Holidays dates as per below:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nUPDATE Dim_Date\r\nSET Public_Holiday_Flag =  'Y'\r\nFROM Dim_Date a INNER JOIN Public_Holidays b\r\nON a.Calendar_Date = b.Public_Holiday_Date\r\nGO\r\nUPDATE Dim_Date\r\nSET Public_Holiday_Flag =  'N'\r\nWHERE Public_Holiday_Flag IS NULL\r\nGO\r\n<\/pre>\n<p style=\"text-align: justify;\">This is just one of the ways to create a \u2018DATE\u2019 dimension table used for a data mart dimensional model. Some of those attributes we calculated and pushed into the table may be obsolete for your project and some may not be there at all. This is why \u2018DATE\u2019 dimension table, although common to all data marts, is an enterprise-specific object and which attributes you use to populate it are driven by your business requirements and needs. If a different data derivative or date format is required, there is no reason why it should not be implemented during this process as apposed to during query structuring for a given output (this really is a separate topic for a separate post). Remember, dimension tables are supposed to be wide, as opposed to fact tables, which are typically long. Having a few extra columns will not hurt a great deal and the CPU cycles we save by ensuring all required attributes are already pre-computed can go a long way during an intensive ETL or report execution process.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I am confident that there is already a number (probably tens if not hundreds) of Internet posts which deal with the process of &#8216;DATE&#8217; dimension table population and I thought long and hard whether I should spend my time to reiterate this information; however, how we customize this routine is very individual and there is [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[12,49],"class_list":["post-460","post","type-post","status-publish","format-standard","hentry","category-sql","tag-code","tag-sql"],"aioseo_notices":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/posts\/460","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/comments?post=460"}],"version-history":[{"count":64,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/posts\/460\/revisions"}],"predecessor-version":[{"id":1913,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/posts\/460\/revisions\/1913"}],"wp:attachment":[{"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/media?parent=460"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/categories?post=460"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/tags?post=460"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}