【问题标题】:Creating a temp table for the off hours of the organization为组织的非工作时间创建临时表
【发布时间】:2021-09-22 13:37:50
【问题描述】:

我需要根据输入参数 sdate 和 edate 创建一个包含 Start_Date 和 End_Date 列的临时表。此表是办公室的下班时间列表,包括工作日下午 6 点至早上 6 点。周末我需要时间如下:

  1. 周五下午 6 点至周六上午 12 点
  2. 周六上午 12 点至周日上午 12 点和
  3. 周日上午 12 点至周一上午 6 点

希望下表能让您了解我想要达到的目标:

Start Start_Date End End_Date
Sun 08/01/2021 00:00:00.000 Mon 08/02/2021 06:00:00.000
Mon 08/02/2021 18:00:00.000 Tue 08/03/2021 06:00:00.000
Tue 08/03/2021 18:00:00.000 Wed 08/04/2021 06:00:00.000
Wed 08/04/2021 18:00:00.000 Thu 08/05/2021 06:00:00.000
Thu 08/05/2021 18:00:00.000 Fri 08/06/2021 06:00:00.000
Fri 08/06/2021 18:00:00.000 Sat 08/07/2021 00:00:00.000
Sat 08/07/2021 00:00:00.000 Sun 08/08/2021 00:00:00.000
Sun 08/08/2021 00:00:00.000 Mon 08/09/2021 06:00:00.000
Mon 08/09/2021 18:00:00.000 Tue 08/10/2021 06:00:00.000
Tue 08/10/2021 18:00:00.000 Wed 08/11/2021 06:00:00.000

我已经尝试了以下代码,但它没有得到正确的时间范围。

ALTER PROCEDURE [dbo].[temptableforoffhours]
    @BDate  varchar(50),
    @EDate   varchar(50),
    @Provider   varchar(1000)=''

AS

BEGIN
SET NOCOUNT ON
    
DECLARE 
    @BeginDate  datetime = '',
    @EndDate    DATETIME = ''

    IF @BeginDate = '' AND @EndDate = ''
    BEGIN 
        SET @BeginDate = DATEADD(YY,-3,GETDATE())
        SET @EndDate = GETDATE()
    END

    IF @BeginDate <> '' AND @EndDate <> ''
    BEGIN
        
        Set @BeginDate =CONVERT(datetime, @BDate, 121)
        
        SET @EndDate =Convert (Datetime, cast(CONVERT(DateTime, DATEADD(DD,1,@EDate), 121) as Date),121)
        

    END

/********************************Creation of #tmptimeFrameAudit table with FrameID and Start_day and end_Day********************************/

Declare @CountTimeFrames int = DateDiff(Day, @BeginDate, @EndDate)


DECLARE @Counter INT
SET @Counter = 1

Create Table #tmptimeFrameAudit (Frameid int,Start_Day datetime, End_Day datetime, doW varchar(10))

WHILE ( @Counter <= @CountTimeFrames)
BEGIN

IF @counter = 1
Begin
    set @BeginDate = @BeginDate
End
Else
Begin
    set @BeginDate = DATEADD(DD,1,@BeginDate)
End

IF (DatePart(weekday,@BeginDate) = 7)
        BEGIN
            Insert Into #tmptimeFrameAudit values (@Counter,@BeginDate,DATEADD(HOUR,24,@BeginDate),  DATENAME(WEEKDAY,@BeginDate))
        END
ELSE IF (DatePart(weekday,@BeginDate) = 6) 
        BEGIN
            Insert Into #tmptimeFrameAudit values (@Counter,Dateadd(HOUR,18,CONVERT(datetime, @BeginDate, 121)),DATEADD(HOUR,6,@BeginDate),  DATENAME(WEEKDAY,@BeginDate))
        END
ELSE IF (DatePart(weekday,@BeginDate) = 1) 
        BEGIN
            Insert Into #tmptimeFrameAudit values (@Counter,@BeginDate,DATEADD(HOUR,30,@BeginDate),  DATENAME(WEEKDAY,@BeginDate))
        END
ELSE  
        BEGIN
            Insert Into #tmptimeFrameAudit values (@Counter,Dateadd(HOUR,18,CONVERT(datetime, @BeginDate, 121)),DATEADD(HOUR,12,@BeginDate),  DATENAME(WEEKDAY,@BeginDate))
        END     

set @Counter = @Counter+1

END

Select * from #tmptimeFrameAudit

drop Table #tmptimeFrameAudit

END

你能告诉我代码有什么问题并帮助我纠正它吗?谢谢!

【问题讨论】:

  • 这里有很多代码味道。一开始所有那些疯狂的约会都是毫无意义的。你设置了一个 datetime = '',然后你检查它是否是你刚刚设置的值,然后你再次检查你设置的值。为什么?你真的应该传入一个日期时间数据类型而不是一个字符串,并假设它是一个有效的日期时间。除此之外,您还需要在这里查看使用计数表而不是循环。那么假期关闭呢?这种事情尖叫需要使用日历表。
  • @BDate varchar(50), 不,重新开始。这些应定义为 DATE。不要鼓励容易导致更多问题的草率编码和隐式转换。而且我建议您不要在过程中添加额外的逻辑来“缓解”调用者避免提供日期参数的尝试。抛出一个错误。而这个@BeginDate datetime = '' 并不像你想的那样。

标签: sql-server datetime dayofweek date-manipulation


【解决方案1】:

不确定我是否理解您的 Provider 参数的意义。它根本没有在您的代码中引用。我还强烈建议您传递正确的数据类型而不是字符串。

我建议使用日历表是一种更好的方法,因为您可以灵活地处理假期休假,甚至在某些日子延长或缩短工作时间。但这超出了您所要求的范围。

我在这里使用的是计数表而不是循环。我在我的数据库中保留一个这样的视图。

create View [dbo].[cteTally] as

WITH
    E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
    E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
    cteTally(N) AS 
    (
        SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
    )
select N from cteTally

现在我们可以利用这个计数表来解决这个问题。

create PROCEDURE [dbo].[temptableforoffhours]
(
    @BDate date
    , @EDate date
    , @Provider varchar(1000)=''
) as

    set nocount on;
        
    declare @StartDate datetime = @BDate 
        , @EndDate datetime = @EDate;

    with Dates as
    (
        select CurrentDate = dateadd(day, t.N - 1, @StartDate)
        from cteTally t
        where t.N <= datediff(day, @StartDate, @EndDate)
    )

    select Start = datename(weekday, d.CurrentDate)
        , Start_Date = dateadd(hour, case datename(weekday, d.CurrentDate) when 'Sunday' then 0 else 18 end, d.CurrentDate)
        , [End] = case datename(weekday, d.CurrentDate) when 'Friday' then dateadd(day, 1, d.CurrentDate) when 'Saturday' then dateadd(day, 1, d.CurrentDate) else dateadd(hour, 30, d.CurrentDate) end
        , End_Date = case datename(weekday, d.CurrentDate) when 'Friday' then dateadd(day, 1, d.CurrentDate) when 'Saturday' then dateadd(day, 1, d.CurrentDate) else dateadd(hour, 30, d.CurrentDate) end
    from Dates d;

这将返回您在问题中显示的结果。

exec temptableforoffhours @BDate = '20210801', @EDate = '20210811', @Provider = ''

【讨论】:

  • 谢谢肖恩!这有帮助!
【解决方案2】:

我还建议一个日历表,这是我日历表的一小部分

select c.CalendarID,
       c.DateCalendar,
       c.DayNameID,
       c.DayName,
       c.IsWeekDay
from   tblCalendar c
where  DateCalendar between '20210801' and '20210810'

CalendarID DateCalendar DayNameID DayName   IsWeekDay
2770       2021-08-01   7         Sunday    False
2771       2021-08-02   1         Monday    True
2772       2021-08-03   2         Tuesday   True
2773       2021-08-04   3         Wednesday True
2774       2021-08-05   4         Thursday  True
2775       2021-08-06   5         Friday    True
2776       2021-08-07   6         Saturday  False
2777       2021-08-08   7         Sunday    False
2778       2021-08-09   1         Monday    True
2779       2021-08-10   2         Tuesday   True

它从我的公司开始的地方开始,直到 2100 年

这样,您的预期结果很容易实现

select 
       c.DayName,
       case when DayNameID in (6, 7) then c.DateCalendar
            else dateadd(hour, 18, convert(datetime,c.DateCalendar))
       end as start_Date,
       case when DayNameID = 7 then dateadd(hour, 6, convert(datetime,c.DateCalendar))
            when DayNameID in (5, 6) then dateadd(hour, 24, convert(datetime,c.DateCalendar))
            else dateadd(hour, 30, convert(datetime,c.DateCalendar))
       end as end_Date
       
from   tblCalendar c
where  c.DateCalendar between '20210801' and '20210810'

它返回这个

DayName    start_Date          end_Date
Sunday     01/08/2021 00:00:00  01/08/2021 06:00:00
Monday     02/08/2021 18:00:00  03/08/2021 06:00:00
Tuesday    03/08/2021 18:00:00  04/08/2021 06:00:00
Wednesday  04/08/2021 18:00:00  05/08/2021 06:00:00
Thursday   05/08/2021 18:00:00  06/08/2021 06:00:00
Friday     06/08/2021 18:00:00  07/08/2021 00:00:00
Saturday   07/08/2021 00:00:00  07/08/2021 00:00:00
Sunday     08/08/2021 00:00:00  08/08/2021 06:00:00
Monday     09/08/2021 18:00:00  10/08/2021 06:00:00
Tuesday    10/08/2021 18:00:00  11/08/2021 06:00:00

我发现有这样一张桌子为我解决了很多问题,它也可以为你做同样的事情

【讨论】:

  • 谢谢GuidoG!我用它来调整代码中的一些东西!
猜你喜欢
  • 2023-02-13
  • 2018-06-02
  • 1970-01-01
  • 2016-03-06
  • 2019-10-23
  • 1970-01-01
  • 2012-12-15
  • 2019-09-09
  • 1970-01-01
相关资源
最近更新 更多