使用递归上下文更新游标
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
例如,假设我们有以下上下文
- 预订时长 3 小时
- 折扣 1 的可用数量 = 1 小时
- 折扣 2 的可用数量 = 0.5 小时
- 折扣 3 的可用数量 = 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 的可用数量 = 1 小时
- 折扣 2 的可用数量 = 0.5 小时
- 折扣 3 的可用数量 = 10 小时
预期输出
Discount 1 > 1 hour
This fully covers my 1 hour long booking
另一个例子
- 预订时长 10 小时
- 折扣 1 的可用数量 = 1 小时
- 折扣 2 的可用数量 = 1 小时
- 折扣 3 的可用数量 = 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
我不应该通过我所有折扣的数量。相反,我应该保留一个共享变量并将其递减。不幸的是我不能同时设置变量和 select...
有什么好主意吗?
此查询不使用 cursor
或 recursive 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
根据我之前的问题,我正在尝试更新我的光标以使用递归上下文。然而,它似乎并不是很简单。我无法获得递归上下文来遍历我的折扣并分配相关数量来完全覆盖我的预订。
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
例如,假设我们有以下上下文
- 预订时长 3 小时
- 折扣 1 的可用数量 = 1 小时
- 折扣 2 的可用数量 = 0.5 小时
- 折扣 3 的可用数量 = 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 的可用数量 = 1 小时
- 折扣 2 的可用数量 = 0.5 小时
- 折扣 3 的可用数量 = 10 小时
预期输出
Discount 1 > 1 hour
This fully covers my 1 hour long booking
另一个例子
- 预订时长 10 小时
- 折扣 1 的可用数量 = 1 小时
- 折扣 2 的可用数量 = 1 小时
- 折扣 3 的可用数量 = 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
我不应该通过我所有折扣的数量。相反,我应该保留一个共享变量并将其递减。不幸的是我不能同时设置变量和 select...
有什么好主意吗?
此查询不使用 cursor
或 recursive 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