【问题标题】:SQL Business Hours Calculation ErrorSQL 营业时间计算错误
【发布时间】:2015-03-07 04:23:28
【问题描述】:

希望有人能告诉我哪里出了问题,我从Calculate business hours between two dates 找到的营业时间查询开始,这是第二个最佳答案,因为最佳答案在测试时没有提供正确的输出。我遇到的问题是当我跨越到新的一天并且结束日期时间超过工作日结束时。这是我的测试:

选择 dbo.BusinessSeconds('09:00:00','17:00:00','2014-12-24 16:59:59','2014-12-26 18:00:00') ;

因此,如果我从 COB 前 1 秒到第二天开始,我得到 1 秒,这是正确的。如果我从同一天开始到第二天结束,我得到 28801,即 1 天零 1 秒——这也是正确的。但是,如果我将日期结束时间延长到 17:00:01 到 17:59:59,它还包括那些秒,但是如果我达到 18:00:00,则时间会截断回原来的 17:00:00 结束日。因此,我将获得任何给定结束时间的正确数据,除非它是在 17:00:00-17:59:59 时间范围内,然后我可能会得到一个我不应该有的额外时间。

任何帮助都将不胜感激,因为我明天有一个可交付成果。

ALTER Function [dbo].[BusinessSeconds](@BusinessDayStart TIME,@BusinessDayEnd TIME,@StartDate DATETIME,@EndDate DATETIME) Returns Int
AS
Begin
--TEST: select dbo.BusinessSeconds('09:00:00','17:00:00','2014-12-24 16:59:59','2014-12-26 18:00:00');
Declare @WorkMin INT = 0   -- Initialize counter
Declare @Reverse BIT       -- Flag to hold if direction is reverse
Declare @StartHour TIME = @BusinessDayStart   -- Start of business hours (can be supplied as an argument if needed)
Declare @EndHour TIME = @BusinessDayEnd    -- End of business hours (can be supplied as an argument if needed)
Declare @Holidays Table (HDate DateTime)   --  Table variable to hold holidayes

-- If dates are in reverse order, switch them and set flag
If @StartDate>@EndDate 
Begin
    Declare @TempDate DateTime=@StartDate
    Set @StartDate=@EndDate
    Set @EndDate=@TempDate
    Set @Reverse=1
End
Else Set @Reverse = 0

-- Get country holidays from table based on the country code (Feel free to remove this or modify as per your DB schema)
-- Insert Into @Holidays(HDate) select DATEADD(month,((LEFT(CAL_ID,4)-1900)*12)+LEFT(RIGHT(CAL_ID,4),2)-1,RIGHT(CAL_ID,2)-1) from B_CALENDAR where CAL_DAY_PK = 263 and HDATE >= DateAdd(dd, DateDiff(dd,0,@StartDate), 0)
Insert Into @Holidays (HDate) Select HolidayDate from V_BUS_HOL Where HolidayDate >= DateAdd(dd, DateDiff(dd,0,@StartDate), 0)

If CAST(@StartDate as TIME) < @StartHour Set @StartDate = CAST(CAST(@StartDate as DATE) as DATETIME) + CAST(@StartHour as DATETIME)  -- If Start time is less than start hour, set it to start hour
If CAST(@StartDate as TIME) >= DATEADD(HOUR,1,@EndHour) Set @StartDate = CAST(CAST(DATEADD(DAY,1,@StartDate) as DATE) as DATETIME) + CAST(@StartHour as DATETIME) -- If Start time is after end hour, set it to start hour of next day
If CAST(@EndDate as TIME) >= DATEADD(HOUR,1,@EndHour) Set @EndDate = CAST(CAST(@EndDate as DATE) as DATETIME) + CAST(@EndHour as DATETIME)  -- If End time is after end hour, set it to end hour
If CAST(@EndDate as TIME) < @StartHour Set @EndDate = CAST(CAST(DATEADD(DAY,-1,@EndDate) as DATE) as DATETIME) + CAST(@EndHour as DATETIME) -- If End time is before start hour, set it to end hour of previous day

If @StartDate>@EndDate Return 0

-- If Start and End is on same day
If DateDiff(Day,@StartDate,@EndDate) <= 0
Begin
    If Datepart(dw,@StartDate)>1 And DATEPART(dw,@StartDate)<7  -- If day is between sunday and saturday
        If (Select Count(*) From @Holidays Where HDATE=DateAdd(dd, DateDiff(dd,0,@StartDate), 0)) = 0  -- If day is not a holiday
            If @EndDate<@StartDate Return 0 Else Set @WorkMin=DATEDIFF(SECOND, @StartDate, @EndDate) -- Calculate difference
        Else Return 0
    Else Return 0
End
Else Begin
    Declare @Partial int=1   -- Set partial day flag
    While DateDiff(Day,@StartDate,@EndDate) > 0   -- While start and end days are different
    Begin
        If Datepart(dw,@StartDate)>1 And DATEPART(dw,@StartDate)<7    --  If this is a weekday
        Begin
            If (Select Count(*) From @Holidays Where HDATE=DateAdd(dd, DateDiff(dd,0,@StartDate), 0)) = 0  -- If this is not a holiday
            Begin
                If @Partial=1  -- If this is the first iteration, calculate partial time
                Begin 
                    Set @WorkMin=@WorkMin + DATEDIFF(SECOND, @StartDate, DateAdd(hour, DATEPART(HOUR,@EndHour), DateDiff(DAY, 0, @StartDate)))
                    Set @StartDate=DateAdd(hour, DATEPART(HOUR,@StartHour)+24, DateDiff(DAY, 0, @StartDate))
                    Set @Partial=0 
                End
                Else Begin      -- If this is a full day, add full minutes
                    Set @WorkMin=@WorkMin + (DATEPART(HOUR,@EndHour)-DATEPART(HOUR,@StartHour))*60        
                    Set @StartDate = DATEADD(DD,1,@StartDate)
                End
            End
            Else Set @StartDate = DATEADD(DD,1,@StartDate)  
        End
        Else Set @StartDate = DATEADD(DD,1,@StartDate)
    End
    If Datepart(dw,@StartDate)>1 And DATEPART(dw,@StartDate)<7  -- If last day is a weekday
        If (Select Count(*) From @Holidays Where HDATE=DateAdd(dd, DateDiff(dd,0,@StartDate), 0)) = 0   -- And it is not a holiday
            If @Partial=0 Set @WorkMin=@WorkMin + DATEDIFF(SECOND, @StartDate, @EndDate) Else Set @WorkMin=@WorkMin + DATEDIFF(SECOND, DateAdd(hour, DATEPART(HOUR,@StartHour), DateDiff(DAY, 0, @StartDate)), @EndDate)
End 
If @Reverse=1 Set @WorkMin=-@WorkMin
Return @WorkMin
End

【问题讨论】:

  • 那么您想如何处理在@BusinessDayStart 之前开始和/或在@BusinessDayEnd 之后结束的时间?只是截断/忽略这些边界之外存在的时间?听起来就是这样。
  • 1 月 8 日:“我明天有一个可交付的业务”。然后沉默。
  • 抱歉,Ross,我想出了一个对公式的修复,但在这一点上,我不记得实际的解决方案是什么,而且我不再像以前的工作那样访问查询.

标签: sql sql-server-2008 datetime business-intelligence


【解决方案1】:

这是一个旧的,但我相信您的问题是日期时间/时间数据类型。您没有说明您使用的 SQL Server 版本,但如果您有 2008 或更高版本,您可能需要调整您的表和此函数以使用 datetime2 格式。它更准确。 datetime/smalldatetime 都只能精确到某个点,然后它们会四舍五入,这可能会导致您正在查看的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-08
    • 2011-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多