Replace cursors with queries

巧了我就是萌 提交于 2019-12-11 05:17:18

问题


Let's say I have a booking covering 6 hours and 3 discounts covering 2 hours each. I want to split my booking into 3 parts so I can allocate 2 hours per discount.

It would return something like this:

BookingId 1 | DiscountId 1 | Qty 2
BookingId 1 | DiscountId 2 | Qty 2
BookingId 1 | DiscountId 3 | Qty 2

I would then insert those records this into another table.

I'm using an heavily optimized query to determine the number of hours available for each discount. However, I can't find a "good" way to allocate my booking to each discount without using a cursor.

(...)

WHILE @@FETCH_STATUS = 0 
BEGIN
    IF @RequiredQty = 0 
       RETURN

    IF @RequiredQty <= @AvailableQty  
    BEGIN
        INSERT INTO discount.Usage (DiscountId, BookingId, Quantity) 
        VALUES (@DiscountId, @BookingId, @RequiredQty)

        SET @RequiredQty = 0
    END

    IF @RequiredQty > @AvailableQty  
    BEGIN
        INSERT INTO discount.Usage (DiscountId, BookingId, Quantity) 
        VALUES (@DiscountId, @BookingId, @AvailableQty)

        SET @RequiredQty -= @AvailableQty
    END

    FETCH NEXT FROM ecursor INTO @DiscountId, @AvailableQty
END

DEALLOCATE ecursor

I tried building the corresponding query but I can't select and assign variables at the same time. Using a cursor is not really a problem (besides some potential performance issues) but I was just curious to see if with the newest SQL Server we can convert our old cursors to something better?

Thanks, Seb


回答1:


You can useCTE RECURSIVE to make a Table.

like this.

DECLARE @BookingId INT = 1;
DECLARE @RequiredQty INT = 2;
DECLARE @Hours INT = 7;

CREATE TABLE #T
(
    BookingId  INT,
    DiscountId INT,
    Quantity   INT
)

;WITH CTE([Count],[Quantity],Rk) AS  
(
    SELECT
           CASE 
                WHEN [HOURS] - @RequiredQty > @RequiredQty THEN @RequiredQty
                ELSE [HOURS] - @RequiredQty
           END ,
           T.HOURS,1
    FROM
    (
        SELECT @Hours [HOURS]
    ) AS T
    UNION ALL
    SELECT CASE  
                WHEN CTE.[Quantity] - @RequiredQty > @RequiredQty THEN @RequiredQty
                ELSE CTE.[Quantity] - @RequiredQty
           END AS [Count],
           CTE.[Quantity] - @RequiredQty, 
           RK + 1
    FROM CTE
    WHERE CTE.[Quantity] - @RequiredQty > 0
)

INSERT INTO #T(BookingId,DiscountId,Quantity)
SELECT @BookingId,Rk,[Count] FROM CTE
option (maxrecursion 0)

select * from #T

SQLDEMO




回答2:


This is another approach, but don't know if this code has better performance than cursor.

DECLARE @DiscountStocks TABLE (Id INT IDENTITY(1,1), DiscountId INT, LastQty INT)
INSERT INTO @DiscountStocks (DiscountId, LastQty) VALUES (1, 5)
INSERT INTO @DiscountStocks (DiscountId, LastQty) VALUES (2, 2)
INSERT INTO @DiscountStocks (DiscountId, LastQty) VALUES (3, 1)

DECLARE @DiscountBookings TABLE (Id INT IDENTITY(1,1), DiscountId INT, BookingId INT, Qty INT)

DECLARE @BookingDiscount TABLE (Id INT IDENTITY(1,1), BookingId INT, DiscountId INT, Qty INT)
INSERT INTO @BookingDiscount (BookingId, DiscountId, Qty) VALUES (1, 1, 4)
INSERT INTO @BookingDiscount (BookingId, DiscountId, Qty) VALUES (1, 2, 2)
INSERT INTO @BookingDiscount (BookingId, DiscountId, Qty) VALUES (1, 3, 1)
INSERT INTO @BookingDiscount (BookingId, DiscountId, Qty) VALUES (2, 1, 1)
INSERT INTO @BookingDiscount (BookingId, DiscountId, Qty) VALUES (2, 2, 2)

SELECT BD.Id AS BDId, DS.Id AS DSId, DS.LastQty, BD.Qty
, DS.LastQty - (SELECT SUM(Qty) FROM @BookingDiscount WHERE Id <= BD.Id AND DiscountId = BD.DiscountId) AS QtyAfterSubstract
INTO #LastDiscountStock
FROM @DiscountStocks DS
INNER JOIN @BookingDiscount BD ON DS.DiscountId = BD.DiscountId
ORDER BY BD.Id, DS.Id

INSERT INTO @DiscountBookings (DiscountId, BookingId, Qty)
SELECT DSId, BDId, Qty
FROM #LastDiscountStock
WHERE QtyAfterSubstract >= 0

DROP TABLE #LastDiscountStock

SELECT * FROM @DiscountBookings


来源:https://stackoverflow.com/questions/49228325/replace-cursors-with-queries

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!