Search code examples
sql-serversql-server-2014

SQL Server - SELECT ROWS have consecutive orders


I have a price schedule table for rooms Day by Day. My table like below.

RoomId        Date(DD.MD.YY)       RemainingQuota      Price
-----------|------------------|--------------------|----------
1           1.5.2017            5                     10.00
1           1.6.1017            0                     10.00
1           1.7.1017            3                     12.00
1           1.8.1017            0                     10.00
1           1.9.1017            6                     15.00
.....
.....

Input params are

@CheckIn date = CAST('1.5.2017' AS date)
@CheckOut date = CAST('1.8.2017' AS date)

I need to select 3 consecutive dates which have RemainingQuota greater then zero and first date closest @CheckIn

I know the CTE recursive solution. However it comes me very expensive. Does any one know a cheap solution?

Thank you.

SOLVED

I have solved the case by myself without using CTE. Here is complate test code.

    BEGIN /*PREPARE SAMPLE DATA*/    
    CREATE TABLE #priceSchedules(
        RoomId int not null,
        [Date] date not null,
        Quota int not null,
        Price decimal(18,4) not null
    )

    INSERT INTO #priceSchedules
    select 
         RoomId = R.RoomId
        ,[Date] = D.[Date]
        ,Quota = abs(checksum(NewId()) % 5)
        ,Price = abs(checksum(NewId()) % 50)
        from
        (
            select top 100 
                RoomId = row_number() over(order by R.number)
            from master..spt_values R 
            ) R
        OUTER apply(
        select top 60 
        [Date] = cast(DATEADD(day, row_number() over(order by t1.number), getdate()) as date)
        from master..spt_values t1 
        ) D
    END

        BEGIN /*PARAMETERS*/
        DECLARE @RoomId int = 5,
                @CheckIn date = cast(dateadd(day, 3, getdate()) as date),
                @CheckOut date = cast(dateadd(day, 6, getdate()) as date);

        DECLARE @TotalNight int = datediff(day, @CheckIn, @CheckOut)
        END


        BEGIN /*QUERY */
        DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
        SET STATISTICS TIME ON; --FOR CHECK PERFORMANCE

        PRINT('-----------START SELECT---------------------------------')

        SELECT 
        * 
        FROM
        (
            SELECT 
            _P.*
            ,RowNo = ROW_NUMBER() OVER(PARTITION BY RoomId ORDER BY DayDiffFromCheckIn)
            FROM(
                SELECT 
                 P.RoomId
                ,StartDate = P.[Date]
                ,EndDate = MAX(P2.[Date])
                ,CalcDay = COUNT(*)
                ,DayDiffFromCheckIn = CAST(ABS(DATEDIFF(day,P.[Date],@CheckIn)) AS INT)
                ,MinQuota = IIF(MIN(P.Quota) > MIN(P2.Quota) ,MIN(P2.Quota),MIN(P.Quota))
                ,TotalPrice = MIN(P.Price) + SUM(P2.Price)
                FROM #priceSchedules P
                JOIN #priceSchedules AS P2 ON P2.RoomId = P.RoomId AND P2.[Date] > P.[Date] AND P2.[Date] < DATEADD(day, @TotalNight ,P.[Date]) AND P2.Quota > 0
                WHERE P.RoomId = @RoomId AND P.Quota > 0 AND P.[Date] >= GETDATE()
                GROUP BY P.RoomId,P.[Date]
                HAVING(COUNT(*) = (@TotalNight) - 1) -- -1 is for first row
            ) _P
        )__P
        WHERE __P.RowNo < 3 -- for top 3 option colsest to @CheckIn date

        PRINT('-----------END SELECT---------------------------------')

        --SELECT * FROM #priceSchedules
        DROP TABLE #priceSchedules
        SET STATISTICS TIME OFF; 
        END

Solution

  • SOLVED

    I have solved the case by myself without using CTE. Here is complate test code.

        BEGIN /*PREPARE SAMPLE DATA*/    
        CREATE TABLE #priceSchedules(
            RoomId int not null,
            [Date] date not null,
            Quota int not null,
            Price decimal(18,4) not null
        )
    
        INSERT INTO #priceSchedules
        select 
             RoomId = R.RoomId
            ,[Date] = D.[Date]
            ,Quota = abs(checksum(NewId()) % 5)
            ,Price = abs(checksum(NewId()) % 50)
            from
            (
                select top 100 
                    RoomId = row_number() over(order by R.number)
                from master..spt_values R 
                ) R
            OUTER apply(
            select top 60 
            [Date] = cast(DATEADD(day, row_number() over(order by t1.number), getdate()) as date)
            from master..spt_values t1 
            ) D
        END
    
            BEGIN /*PARAMETERS*/
            DECLARE @RoomId int = 5,
                    @CheckIn date = cast(dateadd(day, 3, getdate()) as date),
                    @CheckOut date = cast(dateadd(day, 6, getdate()) as date);
    
            DECLARE @TotalNight int = datediff(day, @CheckIn, @CheckOut)
            END
    
    
            BEGIN /*QUERY */
            DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
            SET STATISTICS TIME ON; --FOR CHECK PERFORMANCE
    
            PRINT('-----------START SELECT---------------------------------')
    
            SELECT 
            * 
            FROM
            (
                SELECT 
                _P.*
                ,RowNo = ROW_NUMBER() OVER(PARTITION BY RoomId ORDER BY DayDiffFromCheckIn)
                FROM(
                    SELECT 
                     P.RoomId
                    ,StartDate = P.[Date]
                    ,EndDate = MAX(P2.[Date])
                    ,CalcDay = COUNT(*)
                    ,DayDiffFromCheckIn = CAST(ABS(DATEDIFF(day,P.[Date],@CheckIn)) AS INT)
                    ,MinQuota = IIF(MIN(P.Quota) > MIN(P2.Quota) ,MIN(P2.Quota),MIN(P.Quota))
                    ,TotalPrice = MIN(P.Price) + SUM(P2.Price)
                    FROM #priceSchedules P
                    JOIN #priceSchedules AS P2 ON P2.RoomId = P.RoomId AND P2.[Date] > P.[Date] AND P2.[Date] < DATEADD(day, @TotalNight ,P.[Date]) AND P2.Quota > 0
                    WHERE P.RoomId = @RoomId AND P.Quota > 0 AND P.[Date] >= GETDATE()
                    GROUP BY P.RoomId,P.[Date]
                    HAVING(COUNT(*) = (@TotalNight) - 1) -- -1 is for first row
                ) _P
            )__P
            WHERE __P.RowNo < 3 -- for top 3 option colsest to @CheckIn date
    
            PRINT('-----------END SELECT---------------------------------')
    
            --SELECT * FROM #priceSchedules
            DROP TABLE #priceSchedules
            SET STATISTICS TIME OFF; 
            END