December 6, 2004 at 5:14 pm
Hey guys, Looking for some help in converting a access module into a User defined function in SQL. What it does is work out the working days between 2 dates. I have done most of it (I THINK!!) but i am having trouble setting the datatypes of the variables.
Here is the original version:
Attribute VB_Name = "WorkDays"
Option Compare Database
Function Work_Days(BegDate As Variant, EndDate As Variant) As Integer
' Note that this function does not account for holidays.
Dim WholeWeeks As Variant
Dim DateCnt As Variant
Dim EndDays As Integer
On Error GoTo Err_Work_Days
BegDate = DateValue(BegDate)
EndDate = DateValue(EndDate)
WholeWeeks = DateDiff("w", BegDate, EndDate)
DateCnt = DateAdd("ww", WholeWeeks, BegDate)
EndDays = 0
Do While DateCnt <= EndDate
If Format(DateCnt, "ddd") <> "Sun" And _
Format(DateCnt, "ddd") <> "Sat" Then
EndDays = EndDays + 1
End If
DateCnt = DateAdd("d", 1, DateCnt)
Loop
Work_Days = WholeWeeks * 5 + EndDays
Exit Function
Err_Work_Days:
' If either BegDate or EndDate is Null, return a zero
' to indicate that no workdays passed between the two dates. '
If Err.Number = 94 Then
Work_Days = 0
Exit Function
Else
' If some other error occurs, provide a message.
MsgBox "Error " & Err.Number & ": " & Err.Description
End If
End Function
And here is my version that i think i have converted:
CREATE Function Work_Days (@BegDate As nvarchar, @EndDate As numeric) RETURNS INTEGER as
begin
DECLARE @WholeWeeks As integer
DECLARE @DateCnt As varchar
DECLARE @EndDays As IntEGER
set @WholeWeeks = DateDiff("w", @BegDate, @EndDate)
set @DateCnt = DateAdd("ww", @WholeWeeks, @BegDate)
set @EndDays= 0
While @DateCnt < @EndDate
continue
If DATENAME(dw,@DateCnt) <> 'Sunday' And DATENAME(dw,@DateCnt) <> 'Saturday'
Begin
set @EndDays = @EndDays + 1
End
else
Begin
set @DateCnt = DateAdd("d", 1, @DateCnt)
End
return @WholeWeeks * 5 + @EndDays
End
Any help would be great guys. you can email me on slancaster@gates.com or msn slancaster81@hotmail.com
Thanks! and hopefully ill be able to help you out one day.
Scotty
December 7, 2004 at 1:36 am
Actually it would make sense, among other things, if you would use the DATETIME data type for @BeginDate and @EndDate.
And probably even more easier would it be to do a search in the script section here or on the web. You will surely find such a function already existed. So there is no need to reinvent the wheel.
--
Frank Kalis
Microsoft SQL Server MVP
Webmaster: http://www.insidesql.org/blogs
My blog: http://www.insidesql.org/blogs/frankkalis/[/url]
December 7, 2004 at 1:37 am
Hello Scott,
SQL Server has a data type called DATETIME that should be right for you.
In addition, I've had some trouble with the loop that tends to become infinite.
It should be something like this:
CREATE Function Work_Days (@BegDate As datetime, @EndDate As datetime) RETURNS INTEGER as
begin
DECLARE @WholeWeeks As integer
DECLARE @DateCnt As datetime
DECLARE @EndDays As IntEGER
set @WholeWeeks = DateDiff("ww", @BegDate, @EndDate)
set @DateCnt = DateAdd("ww", @WholeWeeks, @BegDate)
set @EndDays= 0
While @DateCnt < @EndDate
begin
If DATENAME(dw,@DateCnt) <> 'Sunday' And DATENAME(dw,@DateCnt) <> 'Saturday'
Begin
set @EndDays = @EndDays + 1
End
set @DateCnt = DateAdd("d", 1, @DateCnt)
End
return @WholeWeeks * 5 + @EndDays
End
There's still a problem if the start and end date are in the same week, but I'm running out of time right now.
I can offer you a similar function I've written some time ago. It also offers the possibility to decide if Saturdays should be counted as workdays or not. In addition, it should also work on a server that is not installed in English (which is unfortunately true for me), as long as Microsoft didn't decide to change the result of DATEPART(dw, ...) depending on the country. I hope the comments will help you understand what the function is doing.
create FUNCTION dbo.fnCalWorkDays
(
@ParStartDate datetime -- Date to start with (included)
,@ParEndDate datetime -- Date to end with (included)
,@ParCountSaturdays bit = 0 -- 1: Saturdays are workdays, 0: Saturdays are free (default)
)
RETURNS int -- Number of working days
AS
BEGIN
-- Calculate the number of working days between two days (including or excluding saturdays)
DECLARE @CountDays int
DECLARE @StartDate datetime
DECLARE @EndDate datetime
DECLARE @WorkDays int
-- Switch the dates if necessary
IF @ParStartDate < @ParEndDate
BEGIN
SET @StartDate = @ParStartDate
SET @EndDate = @ParEndDate
END
ELSE
BEGIN
SET @StartDate = @ParEndDate
SET @EndDate = @ParStartDate
END
IF (@ParCountSaturdays = 1)
BEGIN
-- Saturdays are working days
SET @WorkDays = 6
END
ELSE
BEGIN
-- Saturdays are free
SET @Workdays = 5
END
-- Calculate full weeks times working days per week
SELECT @CountDays = FLOOR(DATEDIFF(dd, @StartDate, @EndDate) / 7) * @Workdays
IF (DATEPART(dw, @StartDate) > DATEPART(dw, @EndDate))
BEGIN
-- Add days from start date to end of week (friday or saturday)
-- and from beginning of week to end date
SELECT @CountDays = @CountDays + (@Workdays + 2 - DATEPART(dw, @StartDate)) + (DATEPART(dw, @EndDate) - 1)
END
IF (DATEPART(dw, @StartDate) < DATEPART(dw, @EndDate))
BEGIN
-- Add days between start weekday and end weekday
-- subtract 1 if starting day is sunday
-- subtract 1 if ending day is saturday and saturday is free
SELECT @CountDays = @CountDays + (DATEPART(dw, @EndDate) - DATEPART(dw, @StartDate) + 1)
IF (DATEPART(dw, @StartDate) = 1)
BEGIN
SELECT @CountDays = @CountDays - 1
END
IF ((@ParCountSaturdays = 0) AND (DATEPART(dw, @EndDate) = 7))
BEGIN
SELECT @CountDays = @CountDays - 1
END
END
RETURN @CountDays
END
I hope I was able to help you.
Christian
December 7, 2004 at 3:46 pm
Thanks Christian and Frank for your answers.
Christian i used both the one you fixed up for me.. thought their might be a problem with the loop statement. And i also compared it to the one you supplied. Both return different values . i used your saying that saturdays were not included. either way, ill have a better look into the script to see if i can work out why it is doing it.
date enddate Scotts Christians
1/01/2004 10/01/2004 7 8
1/01/2004 4/10/2004 200 198
12/01/2004 16/01/2004 4 4
This was the first time i tried creating a UDf so thanks for all you inputs.. Im sure you will see me back here soon enough.
thanks again
Scotty
December 7, 2004 at 7:15 pm
There is a set based way to do this, but first you need to set up a common SQL table called "Sequence":
CREATE TABLE [Sequence] (
[SequenceID] [int] IDENTITY (1, 1) NOT NULL ,
PRIMARY KEY CLUSTERED
(
[SequenceID]
  ON [PRIMARY]
) ON [PRIMARY]
GO
declare @SequenceID Int
While isnull(@SequenceID, 0) <=1000
BEGIN
Insert Sequence default values
select @SequenceID = @@identity
END
Now you can create the function:
create function UTIL_WorkingDaysDiff(@StartDate datetime, @EndDate datetime)
returns int
as
begin
declare @Count int
--check for null values
if @StartDate is null or @EndDate is null
goto fEND
---check that dates aren't the same
if convert(varchar(12), @StartDate, 101) = convert(varchar(12), @EndDate, 101)
BEGIN
Select @Count = 0
goto fEND
END
--check that startdate < endDate
if @StartDate > @EndDate
goto fEND
select @Count =
sum (
Case datepart(weekday, dateadd(dd, SequenceID, @StartDate))
When 1 then 0
When 7 then 0
Else 1
END
 
From Sequence
where SequenceID <= datediff(dd, @StartDate, @EndDate)
fEND:
return @Count
END
If you are worried about date ranges greater than the number of records you would want to check that the datediff(@StartDate, @EndDate) is less then the max(SequenceID), and if it isn't add more records to the Sequence table.
Signature is NULL
December 9, 2004 at 12:33 am
Hello Scott,
this is really weird... I've tried on different servers, but I get different results using my function than you do:
select dbo.fncalworkdays(convert(datetime,'01.01.2004', 104), convert(datetime,'10.01.2004', 104), 0) --> returns 7
select dbo.fncalworkdays(convert(datetime,'01.01.2004', 104), convert(datetime,'04.10.2004', 104), 0) --> return 198
select dbo.fncalworkdays(convert(datetime,'12.01.2004', 104), convert(datetime,'16.01.2004', 104), 0) --> returns 5
Sorry, I'm using German date formats, I'm just used to converting dates this way.
The only thing I could think of is the DATEPART() function, which should return 7 for Saturday and 1 for Sunday. This can be influenced by SET DATEFIRST. The default for English is 7 (Sunday is the first day of the week) and that is the setting we are using.
Maybe your machine is set to DATEFIRST 1 (Monday is the first day of the week), which will lead to a different result.
You might simply try to run SET DATEFIRST 7 before calling the function. As far as I know, this should only influence the current session.
Of course, you can as well change all DATEPART() calls in the function to fit your setting of DATEFIRST.
SELECT @@DATEFIRST will show your current setting.
Christian
December 13, 2004 at 3:47 pm
There's actually a script by Ed Cardin featured in today's email:
http://www.sqlservercentral.com/scripts/contributions/1310.asp
This is definitely a valid option, and provides a way of specifying whether a day is a holiday or not, quarters, etc. I use something similar in my OLAP cubes for date dimensions...
Still, I like my sequence table approach and the fact that it's set based.
Signature is NULL
Viewing 7 posts - 1 through 6 (of 6 total)
You must be logged in to reply to this topic. Login to reply
This website stores cookies on your computer.
These cookies are used to improve your website experience and provide more personalized services to you, both on this website and through other media.
To find out more about the cookies we use, see our Privacy Policy