Add 60 days and exclude weekends and holidays

Hi all.

I have a Calendar table with all the days for the next 100 years, Monday-Friday makes workday = 1 and holiday dates are holiday = 1. I want to add 60 days to the CollectionDate field but I want to exclude weekends and holidays. I've tried the code below which adds 60 days and excludes holidays but it's including weekend days (Saturday & Sunday). Any idea how I can tweak it to make it the PromiseDate add 60 days to the CollectionDate and exclude holidays and exclude weekends as well?

Thank you in advance.

SELECT     TableA.SalesOrderNumber, TableA.CollectionDate, SDI.dbo.Calendar.CalendarDate AS PromiseDate, 
                      TableA.LineIndex, TableA.ItemNumber
FROM         TableB INNER JOIN
                      TableA ON TableB.ItemNumber = TableA.ItemNumber CROSS JOIN
                      SDI.dbo.Calendar
WHERE     (SDI.dbo.Calendar.CalendarDate =
                          (SELECT     MIN(CalendarDate) AS Expr1
                            FROM          SDI.dbo.Calendar AS Calendar_1
                            WHERE      (workingday = 1) AND (holiday = 0) AND CalendarDate > (DATEADD(d, 60, TableA.CollectionDate) ))) AND 
                      (TableA.PromiseDate IS NULL) AND (TableB.ProductType = 'D')

Open in new window

printmediaAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Mark WillsTopic AdvisorCommented:
If Mon - Fri are flagged as 1 does that mean weekends are flagged 0 ?

Will have a closer look, but the where clause seems *interesting*

Seems to be getting first working day 60 days from collectiondatee
printmediaAuthor Commented:
That is correct Mon-Friday has the workingday = 1. If it's a Saturday or Sunday then workingday = 0. And holidays have the following: holiday = 1
Scott PletcherSenior DBACommented:
SELECT     TableA.SalesOrderNumber, TableA.CollectionDate, PromiseDate,
                      TableA.LineIndex, TableA.ItemNumber
FROM         TableB INNER JOIN
                      TableA ON TableB.ItemNumber = TableA.ItemNumber CROSS JOIN
                          (SELECT MAX(CalendarDate) AS PromiseDate
                           FROM (
                                SELECT     TOP (60) C.CalendarDate
                                FROM          SDI.dbo.Calendar AS C
                                WHERE      (C.workingday = 1) AND (C.holiday = 0) AND
                                    C.CalendarDate > TableA.CollectionDate
                                ORDER BY C.CalendarDate
                                ) AS Calendar_1
                          ) AS Calendar_2
WHERE
                      (TableA.PromiseDate IS NULL) AND (TableB.ProductType = 'D')

Btw, for best performance, make sure the Calendar table is (uniquely) clustered on CalendarDate (and not on, say, an identity column).
Big Business Goals? Which KPIs Will Help You

The most successful MSPs rely on metrics – known as key performance indicators (KPIs) – for making informed decisions that help their businesses thrive, rather than just survive. This eBook provides an overview of the most important KPIs used by top MSPs.

printmediaAuthor Commented:
Thanks for the reply. But I get the following error message when I try running the SQL statement:

The multi-part identifier "TableA.CollectionDate" could not be bound.
Mark WillsTopic AdvisorCommented:
I think you want cross apply - a cross join with 100 years is huge...
Scott PletcherSenior DBACommented:
D'OH! Sorry, I forgot one change, "CROSS APPLY" rather than "CROSS JOIN":

...
FROM         TableB INNER JOIN
                      TableA ON TableB.ItemNumber = TableA.ItemNumber CROSS APPLY
                          (SELECT MAX(CalendarDate) AS PromiseDate
...

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
printmediaAuthor Commented:
That was it! Thank you for your help!
Mark WillsTopic AdvisorCommented:
Created my own calendar table #calendartable which you will need to replace with your 'correct' table name

but as a first pass, check out :
SELECT       TableA.SalesOrderNumber, TableA.CollectionDate
           , max(d.calendardate) AS PromiseDate 
           , TableA.LineIndex, TableA.ItemNumber
FROM         TableB 
INNER JOIN   TableA ON TableB.ItemNumber = TableA.ItemNumber 
CROSS APPLY (select top 60 calendardate from #calendartable where calendardate > tablea.collectiondate and holiday = 0 and workingday = 1) d
WHERE       TableA.PromiseDate IS NULL 
AND         TableB.ProductType = 'D'
Group By    TableA.SalesOrderNumber, TableA.CollectionDate,TableA.LineIndex, TableA.ItemNumber

Open in new window

Mark WillsTopic AdvisorCommented:
Dang... Too quick to accept, and no "thank you" or assisted for highlighting the CROSS APPLY

(shouldnt have wasted time creating a calendar table)
Scott PletcherSenior DBACommented:
We cross-posted at almost exactly the same time; the ids are even sequential. I'm fully familiar with CROSS APPLY.

From a performance standpoint, it makes no sense to have to GROUP BY every column in the outer query just to get the last calendar date; that forces a massive sort, which is a *very* expensive operation. Getting the max date within the CA means no sort is required.
Mark WillsTopic AdvisorCommented:
Yes Scott, the group by is not needed.

No Scott, I didnt look at the id's

Yes Scott, I would have come to the same conclusion about where to max(calendardate). And had annotated "first pass". I am fully familiar with group by

Truth is I did waste too much time creating a test environment and should have simply started typing the answer....

enough said.
Ramesh D JaiswalCommented:
Excellent logic Scott.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Query Syntax

From novice to tech pro — start learning today.