3

我们有一张表格,用于将培训师分配给计划中的客户安装。在表中,每一位培训师都有一年中每一天的记录。(我们可以而且有时会在周末工作。)我正在构建一个搜索工具,允许我们的调度员搜索在日期 Y 和 Z 之间有 X 天可用的培训师。

Table mySchedule

Trainer        Date       Dirty (Bit)
------------------------------------------------                
Joe         06/01/2013      0
Jessica     06/01/2013      0
Alan        06/01/2013      0
Heather     06/01/2013      0
Joe         06/02/2013      1
Jessica     06/02/2013      1
Alan        06/02/2013      0
Heather     06/02/2013      0
Joe         06/03/2013      1
Jessica     06/03/2013      1
Alan        06/03/2013      1
Heather     06/03/2013      0

这是我的表格的简化版本,涵盖了 3 天的四位培训师。如果他们安排了一些事情,Dirty = 1。如果他们可以自由安排,Dirty = 0。

我想要构建的是一个允许以下内容的查询:

  • 定义工作需要进行的开始和结束日期。

  • 定义需要培训师的连续天数。

  • 返回每个匹配的培训师,以及他们在至少等于请求天数的时间段内可用的第一个日期。

纯文本示例:

客户要求培训师在 6 月的任何时候在现场待两天。查询应返回:

Alan, 06/01/2013
Heather, 06/01/2013

如果客户在 6 月将请求更改为三天,则查询将返回:

Heather, 06/01/2013

我一直在寻找几天,我发现了一些看起来很接近的东西,但最终,我无法让它们工作。在大多数情况下,失败的形式是执行时间过长。这里有一些看起来很有希望的,也许可以被比我包装更强大的 SQL-Fu 的人改编:

4

3 回答 3

0

假设缺少的计划记录默认为“可用”,这样的事情应该可以工作:

WITH cteRawData As
(
   SELECT
      S.Trainer,
      S.Date,

      -- If there are no later busy dates within the period,
      -- assume they're free until the end of the period:
      IsNull(ED.EndDate, DateAdd(day, 1, @EndDate)) As EndDate
   FROM
      mySchedule As S
      CROSS APPLY
      (
         SELECT
            Min(Date) As EndDate
         FROM
            mySchedule As S2
         WHERE
            S2.Trainer = S.Trainer
         And
            S2.Date > S.Date
         And
            S2.Date <= @EndDate
         And
            S2.Dirty = 1
      ) As ED
   WHERE
      S.Date Between @StartDate And @EndDate
   And
      S.Dirty = 0
),
cteData As
(
   SELECT
      Trainer,
      Date,
      DateDiff(day, Date, EndDate) As NumberOfDays
   FROM
      cteRawData
)
SELECT
   Trainer,
   Min(Date) As EarliestStartDate
FROM
   cteData
WHERE
   NumberOfDays >= @NumberOfDays
GROUP BY
   Trainer
;

http://www.sqlfiddle.com/#!3/7b3e2/17

于 2013-04-03T20:43:33.397 回答
0

不确定这将如何针对更大的数据集执行,但它会为所提供的数据集获得正确的结果。缺失的数据点被认为是可用的。

declare @startDate datetime, @endDate datetime, @days int
select @startDate = '6/1/2013', @endDate='6/3/2013', @days=2

select trainer, min(date)
from
    (
    select  trainer,date,
            (select top 1 date
            from mySchedule sInner
            where sInner.date > sOuter.date
                    and sInner.trainer = sOuter.trainer
                    and sInner.Dirty = 1    
                    and sInner.date between @startDate and @endDate
            order by sInner.date) as nextDirtyDate
    from    mySchedule sOuter
    where sOuter.dirty=0
            and sOuter.date between @startDate and @endDate
    ) sub
group by trainer, nextDirtyDate
having dateDiff(d, min(date), isNull(nextDirtyDate,dateAdd(d,1,@endDate))) >= @days
于 2013-04-03T20:45:04.810 回答
0

注意:我不知道性能有多好,因为它使用的是运行总数。

当dirty = 0时,它也会使用dirty flag来获得一个正数,
例如CASE WHEN B.Dirty = 0 THEN 1 ELSE -1 END

它使用查询中 2 个地方的天数
S.Date - NNN + 1 AS StartDate
HAVING Sum(CASE WHEN B.Dirty = 0 THEN 1 ELSE -1 END) = NNN

-- Note the need to put the value of 3 to be put at 2 places
SELECT S.Trainer, S.Date - 3 + 1 AS StartDate,
Sum(CASE WHEN B.Dirty = 0 THEN 1 ELSE -1 END) AS RunningAvl
FROM Schedule S INNER JOIN
Schedule B
ON S.Trainer = B.Trainer
WHERE B.Date <= S.Date
AND S.Date BETWEEN '2013-06-01' AND '2013-06-30'
AND B.Date BETWEEN '2013-06-01' AND '2013-06-30'
GROUP BY S.Trainer, S.Date
HAVING Sum(CASE WHEN B.Dirty = 0 THEN 1 ELSE -1 END) = 3
ORDER BY S.Trainer, S.Date ;

-- Note the need to put the value of 2 to be put at 2 places
SELECT S.Trainer, S.Date - 2 + 1 AS StartDate,
Sum(CASE WHEN B.Dirty = 0 THEN 1 ELSE -1 END) AS RunningAvl
FROM Schedule S INNER JOIN
Schedule B
ON S.Trainer = B.Trainer
WHERE B.Date <= S.Date
AND S.Date BETWEEN '2013-06-01' AND '2013-06-30'
AND B.Date BETWEEN '2013-06-01' AND '2013-06-30'
GROUP BY S.Trainer, S.Date
HAVING Sum(CASE WHEN B.Dirty = 0 THEN 1 ELSE -1 END) = 2
ORDER BY S.Trainer, S.Date

http://www.sqlfiddle.com/#!3/99f2d/1

于 2013-04-03T21:19:06.270 回答