Please start any new threads on our new 
    site at https://forums.sqlteam.com.  We've got lots of great SQL Server
    experts to answer whatever question you can come up with. 
    
        
            
                
                    
                        
                            
                                | Author | 
                                
                                 Topic  | 
                             
                            
                                    | 
                                         imlycett 
                                        Starting Member 
                                         
                                        
                                        1 Post  | 
                                        
                                        
                                            
                                            
                                             Posted - 2014-04-29 : 05:35:56
                                            
  | 
                                             
                                            
                                            | I have designed a SQL Function similar to Excel WORKDAY that calculates an end-date by counting on a specified number of working days. It allows the specification of a table of custom non-working days. I'm not clear on the performance implications of using a recursive common table expression in this way.After a quick Google search, I found many options that counted non-weekends but I didn't turn up any solutions that offer a custom calendar. If anyone can suggest improvements, or even point me at how I might improve the efficiency, it would be great to hear them.CREATE FUNCTION AddWorkingDays (                    @StartDate Date,                    @NumberOfWorkingDays INT,                    @CalendarToUse INT)RETURNS DateASBEGIN                    -- Declare the return variable here                    DECLARE @EndDate as Date;WITH CTE_Days AS(                    SELECT                                        0 AS RowNum                                                                      ,@StartDate AS [DayToCheck]                                        ,CASE DATEPART("WEEKDAY", @StartDate)                                                            WHEN 1 THEN 1                                                            WHEN 7 THEN 1                                                            ELSE 0                                        END          AS IsWeekend                                        ,CASE WHEN EXISTS(SELECT [Date] FROM dbo.NonWorkingDays WHERE CalendarID = @CalendarToUse AND [Date] = @StartDate) THEN 1 ELSE 0 END AS IsPublicHoliday                                                            UNION ALL                                                         SELECT						CASE							WHEN								DATEPART("WEEKDAY", DATEADD("d", 1, [DayToCheck])) = 1								OR								DATEPART("WEEKDAY", DATEADD("d", 1, [DayToCheck])) = 7 								OR								EXISTS(									SELECT [Date] FROM dbo.NonWorkingDays WHERE CalendarID = @CalendarToUse AND [Date] = DATEADD("d", 1, [DayToCheck])									)								THEN [RowNum] ELSE [RowNum] + 1 END AS RowNum						,DATEADD("d", 1, [DayToCheck])						,CASE DATEPART("WEEKDAY", DATEADD("d", 1, [DayToCheck]))											WHEN 1 THEN 1											WHEN 7 THEN 1											ELSE 0						END          AS IsWeekend 						,CASE WHEN EXISTS(SELECT [Date] FROM dbo.NonWorkingDays WHERE CalendarID = @CalendarToUse AND [Date] = DATEADD("d", 1, [DayToCheck])) THEN 1 ELSE 0 END AS IsPublicHoliday	FROM CTE_Days                    WHERE [RowNum] < @NumberOfWorkingDays)SELECT @EndDate = MAX([DayToCheck]) FROM CTE_Days                    -- Return the result of the function                    RETURN @EndDateENDGO | 
                                             
                                         
                                     | 
                             
       
                            
                       
                          
                            
                                    | 
                                     gbritton 
                                    Master Smack Fu Yak Hacker 
                                     
                                    
                                    2780 Posts  | 
                                    
                                      
                                        
                                          
                                           
                                            Posted - 2014-04-30 : 13:45:36
                                          
  | 
                                         
                                        
                                          | One approach sometimes used in a Data Warehouse setting is to have a Date table that at a minimum has these columns:Date(int), Year(smallint), Month(tinyint), Day(tinyint), MonthName(varchar(9)), DayName(varchar(9)), FullDate (varchar(20)), IsWorkday (bit), IsWeekend (bit), IsStatuatoryHoliday (bit)A typical row might be:(20140430, 2014, 4, 30, 'April', 'Wednesday', 'April 30, 2014' 1, 0, 0)The table would have a PK clustered index on the Date column.  The business would populate this table in advance, usually several years in advance and in arrears, with all proper statutory holidays flagged as such.  Using such a table, it is easy to join it with other tables.  The first column can be used as an FK in referring tables, saving space in those tables and having ready access to tests for workday, weekend, holiday etc. You can add any columns to this table that is meaningful for your business.  Usually the resulting table is not too big (maybe a million bytes or two)Doing it this way you never have to call functions in your queries to make these sorts of tests.  | 
                                         
                                        
                                            | 
                                         
                                       
                                     | 
                                   
                            
                            
                                | 
                                    
                                      
                                     
                                    
                                 | 
                             
                         
                     | 
                 
             
         |   
     
     
            
              
	     |  
		
			
     
          
		 |