Naweez
Naweez

Reputation: 313

skip holiday & weekly off from two date

i am using sql server 2008 R2 , i am creating application leave form. when user apply leave i.e start date & end date then between two dates , all dates should skip if they fall in holiday & weekly off

CREATE TABLE #HolidayMaster
(
    [HolidayID] [int] IDENTITY(1,1) NOT NULL,
    [HolidayDescription] [nvarchar](50) NULL,
    [HolidayDate] [date] NULL,
) 

INSERT INTO #HolidayMaster 
select 'New Year', '2016-01-01'
union
select 'National Developer Day', '2016-01-05'


CREATE TABLE #ShiftMaster
(
    [ShiftID] [int] IDENTITY(1,1) NOT NULL,
    [Sunday] [float] NULL,
    [Monday] [float] NULL,
    [Tuesday] [float] NULL,
    [Wednesday] [float] NULL,
    [Thursday] [float] NULL,
    [Friday] [float] NULL,
    [Saturday] [float] NULL
) 

INSERT INTO #ShiftMaster 
select 0,1,1,1,1,1,0


select *,DATENAME (dw,[HolidayDate]) as [DayName] from #HolidayMaster
select * from #ShiftMaster

drop table #HolidayMaster
drop table #ShiftMaster

Declare @LeaveStartDate date = '2013-01-01'
Declare @LeaveEndDate date = '2013-01-06'

--expected out put

DName       Date        Desc    
Friday      2016-01-01  Holiday
Saturday    2016-01-02  WeeklyOff
Sunday      2016-01-03  WeeklyOff
Monday      2016-01-04  Working
Tuesday     2016-01-05  Holiday
Wednesday   2016-01-06  Working

Upvotes: 5

Views: 320

Answers (5)

Jatin Patel
Jatin Patel

Reputation: 2104

try this,

DECLARE @LeaveStartDate DATETIME = '2016-01-01'
DECLARE @LeaveEndDate DATETIME = '2016-01-06'

;WITH CTE
AS ( SELECT @LeaveStartDate AS LeaveDate
    UNION ALL
    SELECT LeaveDate + 1 
    FROM CTE
    WHERE LeaveDate < @LeaveEndDate
)

SELECT * ,DATENAME(WEEKDAY, c.LeaveDate)
FROM CTE c
LEFT JOIN #HolidayMaster h ON c.LeaveDate = h.HolidayDate
WHERE h.HolidayDate IS NULL 
    AND EXISTS (    SELECT 1 
                    FROM #ShiftMaster s 
                    WHERE (DATENAME(WEEKDAY, c.LeaveDate) = 'Sunday' AND s.Sunday = 1)
                        OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Monday' AND s.Monday = 1)
                        OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Tuesday' AND s.Tuesday = 1)
                        OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Wednesday' AND s.Wednesday = 1)
                        OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Thursday' AND s.Thursday = 1)
                        OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Friday' AND s.Friday = 1)
                        OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Saturday' AND s.Saturday = 1)
                )

If you are looking to get all dates with status try this,

DECLARE @LeaveStartDate DATETIME = '2016-01-01'
DECLARE @LeaveEndDate DATETIME = '2016-01-06'

;WITH CTE
AS ( SELECT @LeaveStartDate AS LeaveDate
    UNION ALL
    SELECT LeaveDate + 1 
    FROM CTE
    WHERE LeaveDate < @LeaveEndDate
)

SELECT 
        c.LeaveDate,
        DATENAME(WEEKDAY, c.LeaveDate) AS [DayName],
        CASE WHEN   (DATENAME(WEEKDAY, c.LeaveDate) = 'Sunday' AND s.Sunday = 0)
                    OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Monday' AND s.Monday = 0)
                    OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Tuesday' AND s.Tuesday = 0)
                    OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Wednesday' AND s.Wednesday = 0)
                    OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Thursday' AND s.Thursday = 0)
                    OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Friday' AND s.Friday = 0)
                    OR (DATENAME(WEEKDAY, c.LeaveDate) = 'Saturday' AND s.Saturday = 0)
                THEN 'WeeklyOff'
            WHEN h.HolidayDate IS NOT NULL 
                THEN 'Holiday'
            ELSE 'Working'
        END AS [Status]
FROM CTE c
CROSS JOIN #ShiftMaster s 
LEFT JOIN #HolidayMaster h ON c.LeaveDate = h.HolidayDate

Upvotes: 0

nazark
nazark

Reputation: 1240

try

Declare @LeaveStartDate date = '2016-01-01'
Declare @LeaveEndDate date = '2016-01-06'


    ;with dts(dt) as (select @LeaveStartDate 
    union all 
    select dateadd(day,1,dt) from dts where dt<@LeaveEndDate),

        shifmstr as (
        select shiftID,'Sunday' as dname,[sunday] tp from #ShiftMaster union all
        select shiftID,'Monday' ,Monday from #ShiftMaster union all
        select shiftID,'Tuesday' ,Tuesday from #ShiftMaster union all
        select shiftID,'Wednesday',Wednesday from #ShiftMaster union all
        select shiftID,'Thursday',Thursday from #ShiftMaster union all
        select shiftID,'Friday',Friday from #ShiftMaster union all
        select shiftID,'Saturday',Saturday from #ShiftMaster)

    select datename(dw,dt) DName,Dt, 
    case when c.HolidayDate is not null then 'Holiday'
     when tp=1 then 'Working' else 'WeeklyOff'    end descr
    from dts a join shifmstr b on 
    datename(dw,a.dt)=b.dname left join 
    #HolidayMaster c on a.dt=c.HolidayDate

Upvotes: 0

Pரதீப்
Pரதீப்

Reputation: 93694

You need a calendar table. I have used tally table to generate dates.

Declare @LeaveStartDate date = '2016-01-01'
Declare @LeaveEndDate date = '2016-01-06'

;WITH Nbrs_3( n ) AS ( SELECT 1 UNION SELECT 0 ),
     Nbrs_2( n ) AS ( SELECT 1 FROM Nbrs_3 n1 CROSS JOIN Nbrs_3 n2 ),
     Nbrs_1( n ) AS ( SELECT 1 FROM Nbrs_2 n1 CROSS JOIN Nbrs_2 n2 ),
     Nbrs_0( n ) AS ( SELECT 1 FROM Nbrs_1 n1 CROSS JOIN Nbrs_1 n2 ),
     Nbrs  ( n ) AS ( SELECT 1 FROM Nbrs_0 n1 CROSS JOIN Nbrs_0 n2 ),
     calendar(dates) as 
     (
SELECT Dateadd(dd, n - 1, @LeaveStartDate) AS Date
FROM   (SELECT Row_number()OVER (ORDER BY n)
        FROM   Nbrs) D ( n )
WHERE  n <= Datediff(day, @LeaveStartDate, @LeaveEndDate)+ 1
)
SELECT DName =d.day_name,
       Date = c.dates,
       CASE
         WHEN HolidayDate IS NOT NULL THEN 'Holiday'
         WHEN leav_iden = 0 THEN 'WeeklyOff'
         WHEN HolidayDate IS NOT NULL
              AND leav_iden = 0 THEN 'Holiday/WeeklyOff'
         ELSE 'Working'
       END AS [Desc]
FROM   calendar c
       JOIN (SELECT *
             FROM   #ShiftMaster
                    CROSS apply (VALUES ([Sunday],'Sunday'),
                                        ([Monday],'Monday'),
                                        ([Tuesday],'Tuesday'),
                                        ([Wednesday],'Wednesday'),
                                        ([Thursday],'Thursday'),
                                        ([Friday],'Friday'),
                                        ([Saturday],'Saturday') ) cs(leav_iden, day_name)) d
         ON Datename(WEEKDAY, c.dates) = d.day_name
       LEFT JOIN #HolidayMaster h
              ON h.HolidayDate = c.dates 

Result :

DName       Date        Desc
-----       ----------  -------------
Sunday      2016-01-03  WeeklyOff
Monday      2016-01-04  Working
Tuesday     2016-01-05  Holiday
Wednesday   2016-01-06  Working
Friday      2016-01-01  Holiday
Saturday    2016-01-02  WeeklyOff

Upvotes: 1

artm
artm

Reputation: 8584

Recursive CTE to generate dates between the date range, left join on HolidayMaster and left join on ShiftMaster (I changed the ShiftMaster to have day names to join on), then select the proper description depending on the values:

CREATE TABLE #HolidayMaster
(
    [HolidayID] [int] IDENTITY(1,1) NOT NULL,
    [HolidayDescription] [nvarchar](50) NULL,
    [HolidayDate] [date] NULL,
) 

INSERT INTO #HolidayMaster 
select 'New Year', '2016-01-01'
union
select 'National Developer Day', '2016-01-05'


CREATE TABLE #ShiftMaster
(
    [ShiftID] [int] IDENTITY(1,1) NOT NULL,
    [Day] nvarchar(20) NULL,
    IsHoliday bit NULL
) 

INSERT INTO #ShiftMaster 
values ('Sunday', 0), ('Monday', 1), ('Tuesday', 1), ('Wednesday',1), ('Thursday', 1), ('Friday', 1), ('Saturday',0)


select *,DATENAME (dw,[HolidayDate]) as [DayName] from #HolidayMaster
select * from #ShiftMaster

Declare @LeaveStartDate date = '2016-01-01'
Declare @LeaveEndDate date = '2016-01-06'

;WITH Dates AS (
        SELECT
         [Date] = @LeaveStartDate
        UNION ALL SELECT
         [Date] = DATEADD(DAY, 1, [Date])
        FROM
         Dates
        WHERE
         Date < @LeaveEndDate
) SELECT
DATENAME(dw, [Date])
 , [Date]
 , CASE WHEN hm.HolidayDescription IS NULL THEN 
      CASE WHEN sm.IsHoliday = 1 THEN 'Working' ELSE 'Weekly Off' END
      ELSE 'Holiday' END AS Description 
FROM
 Dates
 left join #HolidayMaster hm on hm.HolidayDate = [Date]
 left join #ShiftMaster sm on sm.Day = DATENAME(dw, [Date])


drop table #HolidayMaster
drop table #ShiftMaster

Upvotes: 0

Alexei - check Codidact
Alexei - check Codidact

Reputation: 23078

First thing you need is to generate the dates based on an interval. A nice an quick way is shown here. Then, you can LEFT JOIN with your #HolidayMaster table to check if a date is a holiday or not.

The code should look like this:

Declare @LeaveStartDate date = '2016-01-01'
Declare @LeaveEndDate date = '2016-01-06'

;WITH Dates_CTE AS (
    SELECT  TOP (DATEDIFF(DAY, @LeaveStartDate, @LeaveEndDate) + 1)
            Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @LeaveStartDate)
    FROM    sys.all_objects a
            CROSS JOIN sys.all_objects b
)
SELECT DATENAME (dw,[Date]) as [DayName],
    [Date],
    (CASE   WHEN DATEPART(dw, [Date]) IN (1, 7) THEN 'WeeklyOff'
            WHEN HM.HolidayID IS NOT NULL THEN 'Holiday'    -- HM.Description can be used here to actually display holiday name
            ELSE 'Working' END) [Desc]
FROM Dates_CTE D
    LEFT JOIN #HolidayMaster HM ON HM.HolidayDate = D.[Date]

Usually LEFT JOIN .. IS NULL check is slower than NOT EXISTS, so an an alternative is to remove LEFT JOIN and check for existence, but I find this form more readable.

Upvotes: 0

Related Questions