使用递归上下文更新游标

时间:2018-03-14 03:36:30

标签: sql sql-server cursor recursive-query sql-server-2017

按照我之前的问题,我尝试更新光标以使用递归上下文。然而,它似乎并不是那么简单。我无法获得递归上下文来循环我的折扣并分配相关数量以完全覆盖我的预订。

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

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

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

例如,我们说我们有以下背景

  • 预订3小时
  • 折扣1可用qty = 1小时
  • 折扣2可用qty = 0.5小时
  • 折扣3可用qty = 10小时

预期产出

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

另一个例子

  • 预订1小时
  • 折扣1可用qty = 1小时
  • 折扣2可用qty = 0.5小时
  • 折扣3可用qty = 10小时

预期产出

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

另一个例子

  • 预订10小时
  • 折扣1可用qty = 1小时
  • 折扣2可用qty = 1小时
  • 折扣3可用qty = 1小时

预期产出

 - 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 

我不应该通过我的所有折扣传递数量。相反,我应该保留一个共享变量并减少它。不幸的是,我无法设置变量并同时选择...

有什么好主意吗?

1 个答案:

答案 0 :(得分:0)

此查询不使用cursorrecursive cte。查询中的最后一列计算每个折扣所涵盖的分钟数。你没有展示你的桌子结构所以我创建了两张折扣和预订的桌子。

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