基于日期和时间的工作转移识别

时间:2016-07-14 11:46:37

标签: sql sql-server vb.net loops

我有一份工作班次安排A, B, C, D,其中每班班次都来自7:15 to 7:14 例如:

shift A from 7:15 to 19:14
shift C from 19:15 to 7:14
shift D from 7:15 to 19:14
shift B from 19:15 to 7:14

时间表是这样的:C, A, C, A, D, B, D, B, A, C, A, C, B, D, B, D 然后循环将重新启动。

我想创建一个程序,让我希望转变正在处理特定的日期和时间 例如,{12}的7/14/2016会给我A

知道该怎么做吗?

2 个答案:

答案 0 :(得分:1)

这是编写T-SQL表达式的一种方法,该表达式将评估您正在寻找的内容。

case dateadd(minute, @baseTime, @myTime) / (1440 * 8) % 2
    when 0
        case dateadd(minute, @baseTime, @myTime) / (1440 * 4) % 2
            when 0 then
                case dateadd(minute, @baseTime, @myTime) / (1440 * 4) / 720 % 2
                    when 0 then 'C' when 1 then 'A' end
        when 1 then
            case dateadd(minute, @baseTime, @myTime) / (1440 * 4) / 720 % 2
                when 0 then 'D' when 1 then 'B' end
    when 1
        case dateadd(minute, @baseTime, @myTime) / (1440 * 4) % 2
            when 0 then
                case dateadd(minute, @baseTime, @myTime) / (1440 * 4) / 720 % 2
                    when 0 then 'A' when 1 then 'C' end
        when 1 then
            case dateadd(minute, @baseTime, @myTime) / (1440 * 4) / 720 % 2
                when 0 then 'B' when 1 then 'D' end
end

你可以使用负数来完成这项工作但是过去可能更容易选择一个你不必担心的@baseTime。显然,您的周期每16天重复一次,您可以根据需要在逻辑上向后延伸,以便进行此计算。如果你在VB.Net中需要这个,那么翻译就很简单。只需确保使用整数除法(\)而不是"常规"师(/)。

我开始上面的小公式,然后才意识到在后半部分交换了一对轮班。虽然它很容易扩展,但可能很容易阅读。值得庆幸的是,您可以通过获取分钟数并使用每个班次的值范围来描述任何类型的计划。这非常清晰,易于修改。

declare @min int = datediff(minute, @baseTime, @myTime) % (1440 * 8)
select case
    when @min between     0 and   719 then 'A'
    when @min between   720 and  1439 then 'C'
    ...
    when @min between 10080 and 11519 then 'D'
end

主要是为了好玩,使用下面的公式和"班次编号"也可以使用紧凑的方式编写这种模式。在从零开始并计数到十五的十六个周期内。作为输出,令1对应于A,2对应于B,3对应于C,4与D对应。

1 + 2 * ((shiftNumber + shiftNumber / 8 % 2 + 1) % 2) + (shiftNumber / 4 % 2)

这里有一些T-SQL代码可以看出模式是正确的:

create table T (n int );
insert into T (n) values (0), (1),  (2),  (3),  (4),  (5),  (6),  (7),
                         (8), (9), (10), (11), (12), (13), (14), (15);

select
    n + 1 as num,
    substring('ABCD', 1 + 2 * ((n + n / 8 % 2) % 2) + (n / 4 % 2), 1) as shift
from T order by n;

http://rextester.com/HCHZG96643

在代码中,您可以轻松计算要用于代替n的班次编号。

declare @shiftNumber int = datediff(minute, @baseTime, @myTime) / 720 % 16;

答案 1 :(得分:-3)

在进入问题的解决方案之前,我想尝试澄清有关正在传达的内容的混淆。如果我们按照您的日程安排并用时间框架替换字母,我们就会

C, A, C, A
19:15 to 7:14, 7:15 to 19:14, 19:15 to 7:14, 7:15 to 19:14,

这个序列似乎合乎逻辑,但后来转移到了

D, B, D, B, A, C, A, C,
7:15 to 19:14, 19:15 to 7:14, 7:15 to 19:14, 19:15 to 7:14, 7:15 to 19:14, 19:15 to 7:14, 7:15 to 19:14, 19:15 to 7:14, 

这个序列本身很好但是当你将两个序列结合起来时就出现了问题。在组合点你得到

C, A, D, B
19:15 to 7:14, 7:15 to 19:14, 7:15 to 19:14, 19:15 to 7:14,

开始,这种不一致似乎也会发生
A, C, B, D
7:15 to 19:14, 19:15 to 7:14, 19:15 to 7:14, 7:15 to 19:14,

仍然在你的辩护中,也许有一个看似不一致的原因,整个班次似乎都被忽略了。

第二,你说你有4个班次,如果那是完全正确的那么你可以有一个C,B,C,B的序列并且它们完全有效,但是我猜不是这样,你实际上只是有2个班次,但你也可能有2个工作人员或其他影响,我也注意到他们似乎是成对的班次,所以你的第一个表可能会更好地显示为:

Shift A1 from 7:15 to 19:14
Shift A2 from 19:15 to 7:14
Shift B1 from 7:15 to 19:14
Shift B2 from 19:15 to 7:14

这仍然没有考虑到看似缺失的班次,但这有助于澄清一些你正在努力的事情。

再次在你的辩护中,这些都没有任何区别,因为我们真的只是在使用以下模式在哪一天工作:

C,A,C,A,D,B,D,B,A,C,A,C,B,D,B,D

当然Matt Wilko表示你需要提供第一班的开始日(19:15到7:14或C)时,确实表示该功能的一个参数。

当然,如果为最终开发人员明确定义所需的警告,则没有一个限制为此创建函数,如下所示:

使用C#语法和功能编写,因为我在C#中编写代码更快,但同时也是VBA程序员,我知道它可以很容易地翻译成VBA

/*
Parameters:  SeqStartDate (DateTime) - The date that the beginning of the schedule sequence falls upon (aka the date that the first C of C, A, C, A falls on) 
             RequstPeriod (DateTime) - The specific period you are wanting the schedule for.

Returns:     Shift (int) - A numeric representation of the specific shift being worked, for the requested time period
                           (0 = No Shift, 1 = Shift A, 2 = Shift C, 3 = Shift D, 4 = Shift B)

                  Error: - If an error occurs it will return a negative number corresponding to the error
                           However for demonstration purposes it will for now just return -1

Assumptions: Since there are missing shifts a zero has been added to account for these
             That only future shift schedules are desired
             RequstPeriod must occur at least a day after the SeqStartDate
             RequstPeriod must occur at least a day after this program is being run

Modification Note: This could be made more robust and eliminate some of the assumptions but
                   I think what is here should give the basic concept and I will leave the
                   modifying to fit the developers specific needs to the developer
*/
public int GetShift(SeqStartDate as DateTime, RequstPeriod as DateTime)
{
    // First Validate Inputs

    // Today’s Date 1 Second past Midnight
    TodayIs = ((DateTime.Now()).Date).AddTicks(1);

    TimeSpan SeqTSpan = RequstPeriod.Subtract(SeqStartDate)
    if (SeqTSpan.Minutes < 1440)
    {
        return -1;
    }
    TimeSpan TodayTSpan = RequstPeriod.Subtract(TodayIs)
    if (TodayTSpan.Minutes < 1440)
    {
        return -1;
    }

    long SchdMins = 0;
    long SeqShftr = 0;
    int SchdIndx = 0;

    // A Zero represents a missing shift within the continuous schedule sequence
    int ShftSchedSeq[] = {1,2,1,0,3,4,3,4,1,2,1,2,0,4,3,4,3,2};
    long ShftSeqStartMins[] = {57,1155,1497,2595,2937,4035,4377,5475,5817,6915,7257,8355,5697,9795,10137,11235,11577,12675}
    const long FirstStartTimeMin = 57;
    const int IndxCnt = 18;  // Note C# is a 0 based array thus the adjustment later

    // The following assumes Integer division truncates and gives a number from 1 to 8 in SeqShftr
    TimeSpan SeqShftrTSpan = TodayIs.Subtract((SeqStartDate.Date).AddTicks(1))

    // Note  %  means Modulo to ease translation
    SeqShftr = ((((SeqShftrTSpan.Minutes) / 1440) % 9) * 2);

    SchdMins = SeqTSpan.Minutes % 12960

    // Is the SchdMins less than smallest value in Shift Schedule Array?
    if (SchdMins < FirstStartTimeMin)
    {
       // If yes then it is part of the previous sequence shift which would be the last sequence shift
       ShftIndx = IndxCnt - 1;
    }
    else
    {
        // For Loop Counts Down from MaxIndx-1 to 0
        for (int Indx = (IndxCnt-1); Indx => 0; Indx—-)
        {
            if (ShftSeqStartMins[Indx] <= SchdMins)
            {
                ShftIndx = Indx;
                break;
            }
        }
    }

    ShftIndx = (ShftIndx + SeqShftr);
    if (ShftIndx > (IndxCnt - 1))
    {
        ShftIndx = ShftIndx - (IndxCnt - 1);
    }

    return ShftSchedSeq[ShftIndx];    
}

最后注意:看了你的一条评论我发现你似乎有点困惑 - 仍然要调整上面的程序,你需要做的就是调整ScheduleSequence数组中的值(也许是ShftSeqStartMins数组)一旦弄清楚你想要的是什么,他们应该是什么样的。正如你在我所指的评论中所述,晨班也不能是夜班,反之亦然。