3

何百もの支店での人員予約を何年も前から分単位の精度で管理する生産データベースがあります。

このシステムの一部は、ギャップを強調するレポートです。つまり、支店の営業時間とスタッフの予約を比較して、誰も予約していない支店が開いているかどうかを確認します。

また、重複、二重予約などをすべて同時にチェックします。基本的には、微細なレベルの精度が必要です。

これを行う方法は、整数の集計テーブルを使用して、営業時間と予約の開始時間と終了時間を分に拡張することです。

--===== Create and populate the Tally table on the fly
 SELECT TOP 16777216
        IDENTITY(INT,1,1) AS N
   INTO dbo.Tally
   FROM Master.dbo.SysColumns sc1,
        Master.dbo.SysColumns sc2,
        Master.dbo.SysColumns sc3

--===== Add a Primary Key to maximize performance
  ALTER TABLE dbo.Tally
    ADD CONSTRAINT PK_Tally_N 
        PRIMARY KEY CLUSTERED (N) WITH FILLFACTOR = 100

この静的なインデックス付き集計テーブルを利用して、営業時間と予約を次のように拡張します。

SELECT   [BranchID] ,
        [DayOfWeek] ,
        DATEADD(MINUTE, N - 1, StartTime)
FROM     OpeningHours
        LEFT OUTER JOIN tally ON tally.N BETWEEN 0
                                         AND     DATEDIFF(MINUTE, OpeningHours.StartTime, OpeningHours.EndTime) + 1

問題は、13,000,000の「営業時間」と「予約時間」が決まったら、結果を結合して何がカバーされているかを確認する必要があることです。

SELECT   OpenDatesAndMinutes.[Date] ,
                                OpenDatesAndMinutes.[Time] ,
                                OpenDatesAndMinutes.[BranchID] ,
                                ISNULL(BookedMinutes.BookingCount, 0) AS BookingCount
                       FROM     OpenDatesAndMinutes
                                LEFT OUTER JOIN BookedMinutes ON OpenDatesAndMinutes.BranchID = BookedMinutes.BranchID
                                                                 AND OpenDatesAndMinutes.[Date] = BookedMinutes.[Date]
                                                                 AND OpenDatesAndMinutes.[Time] = BookedMinutes.[Time]

ご想像のとおり、すべてCTEテーブルに格納されている13,000,000行のブランチ、日付と時刻に参加するには、AGESが必要です。1週間実行するのはそれほど悪くはありません。約10秒ですが、6か月(13,000,000分)実行すると、 25分以上

オープン議事録を予約議事録に参加させたら、島のデータをグループ化してユーザーに提示します。

CrossTabPrep ( [Date], [Time], [BranchID], [BookingCount], [Grp] )
  AS ( SELECT   [Date] ,
                [Time] ,
                [BranchID] ,
                [BookingCount] ,
                DATEPART(HOUR, Time) * 60 + DATEPART(MINUTE, Time) - ROW_NUMBER() OVER ( PARTITION BY [BranchID], Date, [BookingCount] ORDER BY Time ) AS [Grp]
       FROM     PreRender
     ),
FinalRender ( [BranchID], [Date], [Start Time], [End Time], [Duration], [EntryCount], [EntryColour] )
  AS ( SELECT   [BranchID] ,
                [Date] ,
                MIN([Time]) AS [Start Time] ,
                MAX([Time]) AS [End Time] ,
                ISNULL(DATEDIFF(MINUTE, MIN([Time]), MAX([Time])), 0) AS Duration ,
                [BookingCount] AS EntryCount ,
                CASE WHEN [BookingCount] = 0 THEN 'Red'
                     WHEN [BookingCount] = 1 THEN 'Green'
                     ELSE 'Yellow'
                END AS EntryColour
       FROM     CrossTabPrep
       GROUP BY [BranchID] ,
                [Date] ,
                [BookingCount] ,
                [Grp]
     )

簡単に言えば、私の方法は効率的ですか?分レベルの精度を維持しながら、この方法を改善できる方法はありますか?このような大規模なCTEテーブルを処理する場合、このデータをインデックス付きの一時テーブルにダンプして、代わりにそれらを結合することにメリットはありますか?

私が検討していたもう1つのことは、大きな結合が使用するDATE&TIME(0)データ型を置き換えることです。これらを整数にキャストすると、より効率的になりますか?

これが役立つ場合の完全なCTEです:

WITH    OpeningHours ( [BranchID], [DayOfWeek], [StartTime], [EndTime] )
          AS ( SELECT   BranchID ,
                        DayOfWeek ,
                        CONVERT(TIME(0), AM_open) ,
                        CONVERT(TIME(0), AM_close)
               FROM     db_BranchDetails.dbo.tbl_ShopOpeningTimes (NOLOCK)
                        INNER JOIN @tbl_Days Filter_Days ON db_BranchDetails.dbo.tbl_ShopOpeningTimes.DayOfWeek = Filter_Days.DayNumber
               WHERE    CONVERT(TIME(0), AM_open) <> CONVERT(TIME(0), '00:00:00')
               UNION ALL
               SELECT   BranchID ,
                        DayOfWeek ,
                        CONVERT(TIME(0), PM_open) ,
                        CONVERT(TIME(0), PM_close)
               FROM     db_BranchDetails.dbo.tbl_ShopOpeningTimes (NOLOCK)
                        INNER JOIN @tbl_Days Filter_Days ON db_BranchDetails.dbo.tbl_ShopOpeningTimes.DayOfWeek = Filter_Days.DayNumber
               WHERE    CONVERT(TIME(0), PM_open) <> CONVERT(TIME(0), '00:00:00')
               UNION ALL
               SELECT   BranchID ,
                        DayOfWeek ,
                        CONVERT(TIME(0), EVE_open) ,
                        CONVERT(TIME(0), EVE_close)
               FROM     db_BranchDetails.dbo.tbl_ShopOpeningTimes (NOLOCK)
                        INNER JOIN @tbl_Days Filter_Days ON db_BranchDetails.dbo.tbl_ShopOpeningTimes.DayOfWeek = Filter_Days.DayNumber
               WHERE    CONVERT(TIME(0), EVE_open) <> CONVERT(TIME(0), '00:00:00')
             ),
        DateRange ( [Date], [DayOfWeek] )
          AS ( SELECT   CONVERT(DATE, DATEADD(DAY, N - 1, @StartDate)) ,
                        DATEPART(WEEKDAY, DATEADD(DAY, N - 1, @StartDate))
               FROM     tally (NOLOCK)
               WHERE    N <= DATEDIFF(DAY, @StartDate, @EndDate) + 1
             ),
        OpenMinutes ( [BranchID], [DayOfWeek], [Time] )
          AS ( SELECT   [BranchID] ,
                        [DayOfWeek] ,
                        DATEADD(MINUTE, N - 1, StartTime)
               FROM     OpeningHours
                        LEFT OUTER JOIN tally ON tally.N BETWEEN 0
                                                         AND     DATEDIFF(MINUTE, OpeningHours.StartTime, OpeningHours.EndTime) + 1
             ),
        OpenDatesAndMinutes ( [Date], [Time], [BranchID] )
          AS ( SELECT   DateRange.[Date] ,
                        OpenMinutes.[Time] ,
                        OpenMinutes.BranchID
               FROM     DateRange
                        LEFT OUTER JOIN OpenMinutes ON DateRange.DayOfWeek = OpenMinutes.DayOfWeek
               WHERE    OpenMinutes.BranchID IS NOT NULL
             ),
        WhiteListEmployees ( [DET_NUMBERA] )
          AS ( SELECT   DET_NUMBERA
               FROM     [dbo].[tbl_ChrisCache_WhiteList]
               WHERE    [TimeSheetV2_SecurityContext] = @TimeSheetV2_SecurityContext
             ),
        BookedMinutesByRole ( [Date], [Time], [BranchID], BookingCount )
          AS ( SELECT   [BookingDate] ,
                        DATEADD(MINUTE, N - 1, StartTime) ,
                        BranchID ,
                        COUNT(BookingID) AS Bookings
               FROM     tbl_Booking (NOLOCK)
                        INNER JOIN tbl_BookingReason  (NOLOCK) ON dbo.tbl_BookingReason.ReasonID = dbo.tbl_Booking.ReasonID
                        INNER JOIN tbl_ChrisCache  (NOLOCK) ON dbo.tbl_Booking.DET_NUMBERA = dbo.tbl_ChrisCache.DET_NUMBERA
                        INNER JOIN @ValidPosCodes AS Filter_PostCodes ON dbo.tbl_ChrisCache.POS_NUMBERA = Filter_PostCodes.POSCODE
                        LEFT OUTER JOIN tally (NOLOCK) ON tally.N BETWEEN 0
                                                                  AND     DATEDIFF(MINUTE, tbl_Booking.StartTime, tbl_Booking.EndTime) + 1
               WHERE    ( Void = 0 )
                        AND tbl_BookingReason.CoverRequired = 0 --#### Only use bookings that dont require cover
                        AND tbl_booking.BranchID <> '023'   --#### Branch 23 will always have messy data
                        AND ( dbo.tbl_Booking.BookingDate BETWEEN @StartDate
                                                          AND     @EndDate )
               GROUP BY [BookingDate] ,
                        BranchID ,
                        DATEADD(MINUTE, N - 1, StartTime)
             ),
        BookedMinutesByWhiteList ( [Date], [Time], [BranchID], BookingCount )
          AS ( SELECT   [BookingDate] ,
                        DATEADD(MINUTE, N - 1, StartTime) ,
                        BranchID ,
                        COUNT(BookingID) AS Bookings
               FROM     tbl_Booking(NOLOCK)
                        INNER JOIN tbl_BookingReason (NOLOCK) ON dbo.tbl_BookingReason.ReasonID = dbo.tbl_Booking.ReasonID
                        INNER JOIN tbl_ChrisCache (NOLOCK) ON dbo.tbl_Booking.DET_NUMBERA = dbo.tbl_ChrisCache.DET_NUMBERA
                        INNER JOIN WhiteListEmployees Filter_WhiteList ON dbo.tbl_Booking.DET_NUMBERA = Filter_WhiteList.DET_NUMBERA
                        LEFT OUTER JOIN tally (NOLOCK) ON tally.N BETWEEN 0
                                                                  AND     DATEDIFF(MINUTE, tbl_Booking.StartTime, tbl_Booking.EndTime) + 1
               WHERE    ( Void = 0 )
                        AND tbl_BookingReason.CoverRequired = 0 --#### Only use bookings that dont require cover
                        AND tbl_booking.BranchID <> '023'   --#### Branch 23 will always have messy data
                        AND ( dbo.tbl_Booking.BookingDate BETWEEN @StartDate
                                                          AND     @EndDate )
               GROUP BY [BookingDate] ,
                        BranchID ,
                        DATEADD(MINUTE, N - 1, StartTime)
             ),
        BookedMinutes ( [Date], [Time], [BranchID], BookingCount )
          AS ( SELECT   [Date] ,
                        [Time] ,
                        [BranchID] ,
                        BookingCount
               FROM     BookedMinutesByRole
               UNION
               SELECT   [Date] ,
                        [Time] ,
                        [BranchID] ,
                        BookingCount
               FROM     BookedMinutesByWhiteList
             ),
        PreRender ( [Date], [Time], [BranchID], [BookingCount] )
          AS ( SELECT   OpenDatesAndMinutes.[Date] ,
                        OpenDatesAndMinutes.[Time] ,
                        OpenDatesAndMinutes.[BranchID] ,
                        ISNULL(BookedMinutes.BookingCount, 0) AS BookingCount
               FROM     OpenDatesAndMinutes
                        LEFT OUTER JOIN BookedMinutes ON OpenDatesAndMinutes.BranchID = BookedMinutes.BranchID
                                                         AND OpenDatesAndMinutes.[Date] = BookedMinutes.[Date]
                                                         AND OpenDatesAndMinutes.[Time] = BookedMinutes.[Time]
             ),
        CrossTabPrep ( [Date], [Time], [BranchID], [BookingCount], [Grp] )
          AS ( SELECT   [Date] ,
                        [Time] ,
                        [BranchID] ,
                        [BookingCount] ,
                        DATEPART(HOUR, Time) * 60 + DATEPART(MINUTE, Time) - ROW_NUMBER() OVER ( PARTITION BY [BranchID], Date, [BookingCount] ORDER BY Time ) AS [Grp]
               FROM     PreRender
             ),
        DeletedBranches ( [BranchID] )
          AS ( SELECT   [ShopNo]
               FROM     [dbo].[vw_BranchList]
               WHERE    [Branch_Deleted] = 1
             ),
        FinalRender ( [BranchID], [Date], [Start Time], [End Time], [Duration], [EntryCount], [EntryColour] )
          AS ( SELECT   [BranchID] ,
                        [Date] ,
                        MIN([Time]) AS [Start Time] ,
                        MAX([Time]) AS [End Time] ,
                        ISNULL(DATEDIFF(MINUTE, MIN([Time]), MAX([Time])), 0) AS Duration ,
                        --dbo.format_timeV2(ISNULL(DATEDIFF(SECOND, MIN([Time]), MAX([Time])), 0)) AS DurationF ,
                        [BookingCount] AS EntryCount ,
                        CASE WHEN [BookingCount] = 0 THEN 'Red'
                             WHEN [BookingCount] = 1 THEN 'Green'
                             ELSE 'Yellow'
                        END AS EntryColour
               FROM     CrossTabPrep
               GROUP BY [BranchID] ,
                        [Date] ,
                        [BookingCount] ,
                        [Grp]
             )
            SELECT  [BranchID] ,
                    CONVERT(VARCHAR(10), DATEADD(DAY, 7, CONVERT(DATETIME, CONVERT(VARCHAR(10), DATEADD(day, -1 - ( DATEPART(dw, [Date]) + @@DATEFIRST - 2 ) % 7, [Date]), 103) + ' 23:59:59', 103)), 103) AS WeekEnding ,
                    [Date] ,
                    [Start Time] ,
                    [End Time] ,
                    [Duration] ,
                    CONVERT(VARCHAR, ( [Duration] * 60 ) / 3600) + 'h ' + CONVERT(VARCHAR, ROUND(( ( CONVERT(FLOAT, ( ( [Duration] * 60 ) % 3600 )) ) / 3600 ) * 60, 0)) + 'm' AS [DurationF] ,
                    [EntryCount] ,
                    [EntryColour] ,
                    CASE WHEN [EntryCount] = 0 THEN 'Red'
                         WHEN [EntryCount] >= 1 THEN 'Green'
                    END AS DurationColour ,
                    CASE WHEN [EntryCount] = 0 THEN 'This period of open-time isnt covered'
                         WHEN [EntryCount] >= 1 THEN 'This period of open-time is covered by ' + CONVERT(VARCHAR, [EntryCount]) + ' booking(s)'
                    END AS [DurationComment]
            FROM    FinalRender
            WHERE   FinalRender.BranchID NOT IN ( SELECT    [BranchID]
                                                  FROM      DeletedBranches )
4

2 に答える 2

2

あなたは最後にあなたの質問であなた自身の質問に答えたので、それは面白いです。要約する以外は、すべて試してみてください。

  1. パフォーマンスを向上させるためにCTEを具体化します。SQLServerがいつCTEを複数回評価するかはわかりません
  2. 一時テーブルに対してindexexを構築できます。
  3. [DayOfWeek],DATEADD(MINUTE, N - 1, StartTime)他の参加者にどのようにジャンプしたかはわかりませんが[Date],[Time]、ここに2つの列があるのは意味がありません。エポックからのを表すシングルdatetimeまたはbigintのいずれかを使用します。secondsUnixTimestampはここでうまく機能します。
于 2012-11-08T17:02:17.420 に答える
0

私の提案はあなたのデータに基づいているのではなく、生成されたテストデータに基づいているため、完全に適用できるわけではありません。

提案:パフォーマンスの2次劣化から少なくとも線形に移行するために、データがバッチ期間に均等に分散されている場合は、バッチ処理を使用できます。

以下の例では、2年間の予約が3日のバッチ間隔で処理されており、ブランチごとに1日あたりの無料期間を取り戻すのに2分30秒かかります。

テスト実行結果:

2 years - 2 minutes and 30 seconds 
4 years - 4 minutes and 55 seconds.
6 years - 6 minutes and 41 seconds

数字を使用して一致しない分を見つけることにより、問題で使用されているのと同じロジックが組み込まれています。

スキーマとテストデータの作成:

    IF OBJECT_ID('vwRandomNumber') IS NOT NULL
        DROP VIEW vwRandomNumber
    GO
    IF OBJECT_ID('dbo.fnRandNumber') IS NOT NULL
    DROP FUNCTION  dbo.fnRandNumber
    GO
    IF OBJECT_ID('dbo.fnRandomInt') IS NOT NULL
    DROP FUNCTION dbo.fnRandomInt
    GO
    IF OBJECT_ID('tblNumbers') IS NOT NULL
    DROP TABLE dbo.tblNumbers
    GO
    IF OBJECT_ID('Branches') IS NOT NULL
    DROP TABLE Branches
    GO
    IF OBJECT_ID('OpeningHours') IS NOT NULL
    DROP TABLE OpeningHours
    GO
    IF OBJECT_ID('Bookings') IS NOT NULL
    DROP TABLE Bookings
    GO

    CREATE VIEW vwRandomNumber
    AS
    SELECT Rand() RandomNumber;
    GO

    CREATE FUNCTION dbo.fnRandNumber()
    RETURNS FLOAT
    AS
    BEGIN
      RETURN (SELECT TOP 1 RandomNumber FROM vwRandomNumber)
    END;
    GO

    CREATE FUNCTION dbo.fnRandomInt(@FromNumber INT, @ToNumber INT)
    RETURNS INT
    AS
    BEGIN
      RETURN (@FromNumber + ROUND(dbo.fnRandNumber()*(@ToNumber - @FromNumber),0))
    END;
    GO

    CREATE TABLE tblNumbers 
    (
       NumberID INT PRIMARY KEY 
    )

    CREATE TABLE Branches
    (
       BranchID INT
      ,BranchName NVARCHAR(100)
    );
    GO

    ;WITH cteNumbers AS (
      SELECT 1 N 
      UNION ALL
      SELECT N+1 FROM cteNumbers WHERE N<100
    )
    INSERT INTO
        Branches
    SELECT N, CAST(NEWID() AS NVARCHAR(100)) FROM cteNumbers
    OPTION(MAXRECURSION 0)

    CREATE TABLE OpeningHours
    (
        BranchID INT 
      , Date DATETIME
      , OpenFrom DATETIME
      , OpenTo DATETIME 
    );
    GO

    CREATE CLUSTERED INDEX CIX_OpeningHours
    ON OpeningHours ([Date], [BranchID])

    GO

    CREATE TABLE Bookings
    (
         BranchID INT
       , BookingDate DATETIME
       , BookingFrom DATETIME
       , BookingTo DATETIME  
    )

    CREATE CLUSTERED INDEX CIX_Bookings
    ON Bookings ([BookingDate],[BranchID])

    DECLARE @StartDate DATETIME = DATEADD(month,0,DATEADD(D,0,DATEDIFF(d,0,GETDATE())))

    ;WITH cteNumbers AS (
      SELECT 1 N 
      UNION ALL
      SELECT N+1 FROM cteNumbers WHERE N<2000
    )
    INSERT INTO
      OpeningHours
      (
          BranchID
        , Date
        , OpenFrom
        , OpenTo
      )
    SELECT
      Branches.BranchID
    , Dates.Day
    , DATEADD(hour,7,Dates.Day)
    , DATEADD(hour,19,Dates.Day)
    FROM
      (
        SELECT 
          DATEADD(d,N,@StartDate) Day
        FROM
          cteNumbers
      ) Dates
    CROSS JOIN
      Branches
    OPTION(MAXRECURSION 0);

    INSERT INTO Bookings
    SELECT 
       OpeningHours.BranchID
      ,OpeningHours.Date
      ,BookingHours.StartDate
      ,BookingHours.ToDate
    FROM
      OpeningHours
    CROSS APPLY
      (
         SELECT DATEADD(hour, dbo.fnRandomInt(0,3), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(4,9), OpeningHours.OpenFrom) ToDate UNION ALL
         SELECT DATEADD(hour, dbo.fnRandomInt(1,5), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(6,9), OpeningHours.OpenFrom) UNION ALL
         SELECT DATEADD(hour, dbo.fnRandomInt(2,5), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(5,8), OpeningHours.OpenFrom) TODate UNION ALL
          SELECT DATEADD(hour, dbo.fnRandomInt(0,3), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(4,9), OpeningHours.OpenFrom) ToDate UNION ALL
         SELECT DATEADD(hour, dbo.fnRandomInt(1,5), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(6,9), OpeningHours.OpenFrom) UNION ALL
         SELECT DATEADD(hour, dbo.fnRandomInt(2,5), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(5,8), OpeningHours.OpenFrom) TODate UNION ALL
         SELECT DATEADD(hour, dbo.fnRandomInt(0,3), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(4,9), OpeningHours.OpenFrom) ToDate UNION ALL
         SELECT DATEADD(hour, dbo.fnRandomInt(1,5), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(6,9), OpeningHours.OpenFrom) UNION ALL
         SELECT DATEADD(hour, dbo.fnRandomInt(2,5), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(5,8), OpeningHours.OpenFrom) TODate UNION ALL
          SELECT DATEADD(hour, dbo.fnRandomInt(0,3), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(4,9), OpeningHours.OpenFrom) ToDate UNION ALL
         SELECT DATEADD(hour, dbo.fnRandomInt(1,5), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(6,9), OpeningHours.OpenFrom) UNION ALL
         SELECT DATEADD(hour, dbo.fnRandomInt(2,5), OpeningHours.OpenFrom) StartDate
                ,DATEADD(hour, dbo.fnRandomInt(5,8), OpeningHours.OpenFrom) TODate 
      ) BookingHours;

    ;WITH cteNumbers AS (
      SELECT 1 N 
      UNION ALL
      SELECT N+1 FROM cteNumbers WHERE N<5000
    )
    INSERT INTO
        tblNumbers
    SELECT N FROM cteNumbers
    OPTION(MAXRECURSION 0)

    --SELECT COUNT(*) FROM Bookings WHERE 

予約なしで期間を取得するためのスクリプト:

    SET NOCOUNT ON

    IF OBJECT_ID('tblBranchFreePeriods') IS NOT NULL
        DROP TABLE tblBranchFreePeriods

    IF OBJECT_ID('tblFreeMinutes') IS NOT NULL
        DROP TABLE tblFreeMinutes

    CREATE TABLE tblBranchFreePeriods
    (
          BranchID INT
        , Date DATETIME
        , PeriodStartDate DATETIME
        , PeriodEndDate DATETIME
    )

    CREATE TABLE tblFreeMinutes 
    (
         BranchID INT 
        ,Date DATETIME
        ,FreeMinute INT
    )

    IF OBJECT_ID('dbo.tblStartDates') IS NOT NULL
                DROP TABLE tblStartDates

    CREATE TABLE tblStartDates
    (
          BranchID INT
        , Date DATETIME 
        , PeriodStartDate DATETIME
    )

    CREATE CLUSTERED INDEX CIX_tblStartDates
        ON tblStartDates([BranchID],[Date])

    IF OBJECT_ID('dbo.tblEndDates') IS NOT NULL
        DROP TABLE tblEndDates

    CREATE TABLE tblEndDates
    (
          BranchID INT
        , Date DATETIME 
        , PeriodEndDate DATETIME
    )

    CREATE CLUSTERED INDEX CIX_tblEndDate
        ON tblEndDates ([BranchID],[Date])


    CREATE CLUSTERED INDEX CIX_tblFreeMinutes
        ON tblFreeMinutes ([BranchID],[Date],FreeMinute)

    DECLARE @ProcessFromDate DATETIME, @ProcessTo DATETIME
    SELECT @ProcessFromDate = MIN(OpenFrom), @ProcessTo = DATEADD(year,2,@ProcessFromDate) FROM OpeningHours 

    DECLARE @BatchSize INT = 3

    DECLARE @StartTime DATETIME = GETDATE()

    WHILE (@ProcessFromDate <= @ProcessTo) BEGIN

            TRUNCATE TABLE tblFreeMinutes
            TRUNCATE TABLE tblStartDates
            TRUNCATE TABLE tblEndDates

            SET @StartTime = GETDATE()              

            DECLARE @DateFrom DATETIME = @ProcessFromDate, @DateTo DATETIME = DATEADD(d,@BatchSize,@ProcessFromDate)

            PRINT 'Date From ' + CAST(@DateFrom AS NVARCHAR(50))
            PRINT 'Date To ' + CAST(@DateTO AS NVARCHAR(50))

            INSERT INTO
                tblFreeMinutes
            SELECT
                OpeningHours.BranchID
               ,OpeningHours.Date
               ,tblOpeningHourMinutes.NumberID Minute   
            FROM
                OpeningHours
            INNER JOIN
              tblNumbers tblOpeningHourMinutes
            ON
                NumberID 
                    BETWEEN DATEDIFF(minute,OpeningHours.Date,OpeningHours.OpenFrom)
                AND
                    DATEDIFF(minute,OpeningHours.Date,OpeningHours.OpenTo)
            LEFT OUTER JOIN
               Bookings
            ON
                    Bookings.BookingDate = OpeningHours.Date   
                AND
                    Bookings.BranchID = OpeningHours.BranchID 
                AND
                    tblOpeningHourMinutes.NumberID
               BETWEEN
                   DATEDIFF(minute,Bookings.BookingDate,Bookings.BookingFrom)
               AND
                   DATEDIFF(minute,Bookings.BookingDAte,Bookings.BookingTo)              
            WHERE
               OpeningHours.Date BETWEEN @DateFrom AND @DateTo
            AND
               Bookings.BookingDate IS NULL
            OPTION ( FORCE ORDER )

            PRINT 'Populate free minutes ' + CAST(DATEDIFF(millisecond,@StartTime,GETDATE()) AS NVARCHAR(50))
            SET @StartTime = GETDATE()


            INSERT INTO
                tblStartDates
            SELECT 
                  tblFreeMinutes.BranchID
                , tblFreeMinutes.Date
                , DATEADD(minute,tblFreeMInutes.FreeMinute,tblFreeMinutes.Date)
            FROM
                tblFreeMinutes
            LEFT OUTER JOIN
                tblFreeMinutes tblFreeMinutesIn  
            ON
                tblFreeMinutesIn.Date = tblFreeMinutes.Date
            AND
                tblFreeMinutesIn.BranchID = tblFreeMinutes.BranchID
            AND
                tblFreeMinutesIn.FreeMinute = tblFreeMinutes.FreeMinute-1
            WHERE
                tblFreeMinutesIn.BranchID IS NULL

            PRINT 'Populate start dates ' + CAST(DATEDIFF(millisecond,@StartTime,GETDATE()) AS NVARCHAR(50))
            SET @StartTime = GETDATE()

            INSERT INTO
                tblEndDates
            SELECT 
                  tblFreeMinutes.BranchID
                , tblFreeMinutes.Date
                , DATEADD(minute,tblFreeMInutes.FreeMinute,tblFreeMinutes.Date)
            FROM
                tblFreeMinutes
            LEFT OUTER JOIN
                tblFreeMinutes tblFreeMinutesIn  
            ON
                tblFreeMinutesIn.Date = tblFreeMinutes.Date
            AND
                tblFreeMinutesIn.BranchID = tblFreeMinutes.BranchID
            AND
                tblFreeMinutesIn.FreeMinute = tblFreeMinutes.FreeMinute+1
            WHERE
                tblFreeMinutesIn.BranchID IS NULL

            PRINT 'Populate end dates ' + CAST(DATEDIFF(millisecond,@StartTime,GETDATE()) AS NVARCHAR(50))
            SET @StartTime = GETDATE()

            INSERT INTO
                tblBranchFreePeriods
            SELECT 
                  tblStartDates.BranchID
                , tblStartDates.Date
                , tblStartDates.PeriodStartDate
                , tblEndDate.PeriodEndDate 
            FROM 
                tblStartDates 
            CROSS APPLY
                (
                    SELECT TOP 1 
                        *
                    FROM
                        tblEndDates
                    WHERE
                        tblEndDates.BranchID = tblStartDates.BranchID
                    AND
                        tblEndDates.Date = tblStartDates.Date
                    AND
                        tblEndDates.PeriodEndDate > tblStartDates.PeriodStartDate
                    ORDER BY
                        PeriodEndDate ASC
                ) tblEndDate

            PRINT 'Return intervals ' + CAST(DATEDIFF(millisecond,@StartTime,GETDATE()) AS NVARCHAR(50))
            SET @StartTime = GETDATE()

            SET @ProcessFromDate = DATEADD(d,@BatchSize+1,@ProcessFromDate)

            PRINT ''
            PRINT ''

            RAISERROR ('',0,0) WITH NOWAIT

            --SELECT * FROM tblBranchFreePeriods

           --BREAK      
    END

    SELECT 
        *
    FROM
        tblBranchFreePeriods
    ORDER BY
        1,2,3
于 2012-11-08T23:48:08.850 に答える