3

計画された顧客のインストールにトレーナーを割り当てるために使用されるテーブルがあります。テーブルには、各トレーナーの 1 年の各日に関するレコードがあります。(私たちは週末に働くことができ、時にはそうする.

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 日間で 4 人のトレーナーを対象としています。何か予定がある場合は、ダーティ = 1 です。予定が空いている場合は、ダーティ = 0 です。

私が構築したいのは、次のことを可能にするクエリです。

  • 作業が発生する必要がある開始日と終了日を定義します。

  • トレーナーが必要となる連続した日数を定義します。

  • 一致する各トレーナーを、要求された日数と少なくとも等しい期間利用できる最初の日付とともに返します。

平文の例:

顧客は、6 月のいつでも 2 日間、トレーナーがオンサイトにいることを希望しています。クエリは次を返す必要があります。

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

顧客が 6 月にリクエストを 3 日間に変更した場合、クエリは次のように返します。

Heather, 06/01/2013

ここ数日探していて、近いと思われるものをいくつか見つけましたが、最終的にはそれらを機能させることができませんでした. ほとんどの場合、失敗は非常に長い実行時間という形で発生します。以下に有望と思われるものをいくつか示します。おそらく、私が詰め込んでいるよりも強力な SQL-Fu を持つ人によって適応される可能性があります。

4

3 に答える 3

0

注:現在の合計を使用するため、パフォーマンスがどれほど優れているかはわかりません。

また、dirty = 0 の場合に正の数を取得するために、dirty フラグを使用します
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 に答える
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 に答える