使用递归上下文更新游标

Update cursor with a recursive context

根据我之前的问题,我正在尝试更新我的光标以使用递归上下文。然而,它似乎并不是很简单。我无法获得递归上下文来遍历我的折扣并分配相关数量来完全覆盖我的预订。

ALTER FUNCTION [discount].[fn_get_eligible_packages]
(
    @TecTacClientId NVARCHAR(255),
    @StartDate      DATETIME2,
    @EndDate        DATETIME2,
    @ResourceId     INT,
    @BookingId      INT NULL
)
RETURNS @discounts TABLE 
(
    [DiscountId]    UNIQUEIDENTIFIER,
    [Percentage]    INT,
    [StartDate]     DATETIME2,
    [EndDate]       DATETIME2,
    [CoveredQty]    DECIMAL(10,2)
)
AS
BEGIN
    IF DAY(@StartDate) != DAY(@EndDate) 
       OR DATEDIFF(MINUTE, @StartDate, @EndDate) <= 0 
       OR DATEDIFF(MINUTE, @StartDate, @EndDate) >= 1440 RETURN

    DECLARE @Qty DECIMAL(10,2), @RequiredQty DECIMAL(10,2) = DATEDIFF(MINUTE, @StartDate, @EndDate) / 60.0
    DECLARE @DiscountId UNIQUEIDENTIFIER, @Percentage INT, @AvailableQty DECIMAL(10,2)

    DECLARE curs CURSOR FOR 
    SELECT DiscountId, [Percentage], AvailableQty 
    (...)

    OPEN curs
    FETCH NEXT FROM curs INTO @DiscountId, @Percentage, @AvailableQty 

    WHILE @@FETCH_STATUS = 0  
    BEGIN  
        IF @RequiredQty = 0 RETURN
        IF @RequiredQty > @AvailableQty  SET @Qty = @AvailableQty
        IF @AvailableQty >= @RequiredQty SET @Qty = @RequiredQty

        INSERT INTO @discounts 
        VALUES (@DiscountId, @Percentage, @StartDate, DATEADD(MINUTE, @Qty*60, @StartDate), @Qty)

        SET @StartDate = DATEADD(MINUTE, @Qty*60, @StartDate)
        SET @RequiredQty -= @Qty

        FETCH NEXT FROM curs INTO @DiscountId, @Percentage, @AvailableQty
    END
    CLOSE curs
    DEALLOCATE curs
    RETURN
END

根据提出的答案,我尝试了以下代码

DECLARE @StartDate DATETIME2    = '2018-03-14 10:00:00'
DECLARE @EndDate DATETIME2      = '2018-03-14 12:00:00'

;WITH CTE (DiscountId, [Percentage], AvailableQty, Qty) AS  
(
    SELECT 
        'f4156db3-a0e3-4324-acf7-04cf2f37325e' AS DiscountId, 
        100 AS [Percentage], 
        10 AS AvailableQty, 
        CAST(DATEDIFF(MINUTE, @StartDate, @EndDate) / 60.0 AS DECIMAL(10,2)) AS Qty 
    UNION ALL
    SELECT 
        '4f351cda-443a-4d6a-9265-1ea70af0536d' AS DiscountId, 
        100 AS [Percentage], 
        10 AS AvailableQty, 
        CAST(DATEDIFF(MINUTE, @StartDate, @EndDate) / 60.0 AS DECIMAL(10,2)) AS Qty 
    UNION ALL
    SELECT 
        '51846222-7432-43f7-8647-d2a8e70ea3cf' AS DiscountId, 
        100 AS [Percentage], 
        10 AS AvailableQty, 
        CAST(DATEDIFF(MINUTE, @StartDate, @EndDate) / 60.0 AS DECIMAL(10,2)) AS Qty 
    ---------------------------------------------------------------------------------       
    UNION ALL
    SELECT 
        DiscountId,
        [Percentage],
        AvailableQty,
        CAST(Qty - 
            CASE 
                WHEN Qty > AvailableQty THEN AvailableQty
                ELSE Qty
            END AS DECIMAL(10,2))
    FROM CTE
    WHERE Qty < 0
)

SELECT DiscountId, Percentage, Qty FROM CTE

例如,假设我们有以下上下文

预期输出

Discount 1 > 1 hour
Discount 2 > 0.5 hour
Discount 3 > 1.5 hours
This fully covers my 3 hours long booking

另一个例子

预期输出

Discount 1 > 1 hour
This fully covers my 1 hour long booking

另一个例子

预期输出

 - Discount 1 has 1 hour remaining 
 - Discount 2 has 1 hour remaining 
 - Discount 3 has 1 hour remaining
 This fully does NOT cover my 10 hours long booking. Only 3 hours will be covered by a discount

目前,对于长达两个小时的预订,我得到以下信息:

DiscountId                              Percentage  Qty
f4156db3-a0e3-4324-acf7-04cf2f37325e    100         2.00
4f351cda-443a-4d6a-9265-1ea70af0536d    100         2.00
51846222-7432-43f7-8647-d2a8e70ea3cf    100         2.00

而不是:

DiscountId                              Percentage  CoveredQty
f4156db3-a0e3-4324-acf7-04cf2f37325e    100         2.00

问题就是这个sql

造成的
CAST(DATEDIFF(MINUTE, @StartDate, @EndDate) / 60.0 AS DECIMAL(10,2)) AS Qty 

我不应该通过我所有折扣的数量。相反,我应该保留一个共享变量并将其递减。不幸的是我不能同时设置变量和 select...

有什么好主意吗?

此查询不使用 cursorrecursive cte。查询中的最后一列计算每个折扣涵盖的分钟数。您还没有显示您的 table 结构,所以我创建了两个带有折扣和预订的 table。

declare @discount table (id int, name varchar(100), duration int)
declare @booking table (id int, startDate datetime, endDate datetime)

insert into @discount 
values (1, 'Discount 1', 60)
    , (2, 'Discount 2', 30)
    , (3, 'Discount 3', 600)

insert into @booking
values (1, '20180314 10:00', '20180314 13:00')
    , (2, '20180314 10:00', '20180314 11:00')
    , (3, '20180314 10:00', '20180316 13:00')
    , (4, '20180314 10:00', '20180314 10:20')
    , (5, '20180314 10:00', '20180314 11:20')

;with cte as (
    select
        *, rTotal = isnull(sum(duration) over (order by id rows between unbounded preceding and 1 preceding), 0)
    from 
        @discount
)

select
    *
from (
    select 
        a.id, a.startDate, a.endDate, b.name
        , iTime = iif(c.diff >= b.rTotal + b.duration, duration, iif(c.diff <= 0, 0, diff))
    from 
        @booking a
        cross join cte b
        cross apply (select diff = datediff(mi, a.startDate, a.endDate) - b.rTotal) c
) t
where
    iTime > 0
order by 1