带有开始和结束事务的光标

Rob*_*ert 1 cursors sql-server-2012

我有以下脚本,该脚本在循环的第二次迭代中保持不变。

BEGIN
BEGIN TRY
        BEGIN TRANSACTION
        DECLARE @itemID int

        DECLARE LoopCursor CURSOR FOR
        SELECT DISTINCT i.ItemID
        FROM Inventory i
        INNER JOIN Items it ON i.ItemID = it.ItemID
        WHERE i.ItemID IN (226, 231, 232, 233, 234, 235, 245, 247 ,249 ,250 ,253 ,254 ,255 ,257 ,258 ,268 ,270 ,271 ,273 ,286 ,287 ,291 ,293 ,299 ,303 ,304,
                            305, 306, 307, 308, 310, 311, 312, 313, 314, 316, 322, 323, 324, 331, 332, 333, 334, 335, 338, 339, 340, 341, 342, 343, 345, 346 ,347)
        AND it.[Serializable] = 0
        ORDER BY i.ItemID

        OPEN LoopCursor

        FETCH NEXT FROM LoopCursor
        INTO @itemID

        WHILE @@FETCH_STATUS = 0

            --Drop Temp tables if they exist.
            IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
               DROP TABLE #TEMP

            IF OBJECT_ID('tempdb..#INV_TEMP') IS NOT NULL
               DROP TABLE #INV_TEMP

            BEGIN
                    DECLARE @inventoryID int = 0,
                            @quantity int = 0,
                            @statusID int = 10,
                            @maxInvID int       
                    SET @inventoryID = (SELECT MAX(InventoryID) AS InventoryID FROM Inventory WHERE Inventory.ItemID = @itemID)

                    --Store Assets in temp table.
                    SELECT MAX(AssetID) AS AssetID, RoomID, COUNT(*) AS Quantity
                    INTO #TEMP
                    FROM Assets
                    WHERE InventoryID IN (SELECT InventoryID
                                            FROM Inventory
                                            WHERE ItemID = @itemID)
                    GROUP BY RoomID

                    --Update Assets with new Quantity value.
                    UPDATE Assets
                    SET Quantity = t.Quantity
                    FROM Assets a
                    INNER JOIN #TEMP t ON a.RoomID = t.RoomID
                    INNER JOIN Inventory i ON a.InventoryID = i.InventoryID
                    WHERE i.ItemID = @itemID

                    --Delete all other Assets with the same ItemID and that's not in the temp table.
                    DELETE a
                    FROM Assets a
                    INNER JOIN Inventory i ON a.InventoryID = i.InventoryID
                    INNER JOIN #TEMP t ON a.RoomID = t.RoomID
                    WHERE i.ItemID = @itemID
                    AND a.AssetID NOT IN (SELECT AssetID FROM #TEMP)

                    --Update Assets to have all the same InventoryID.
                    UPDATE Assets
                    SET Assets.InventoryID = @inventoryID, DateUpdated = GETDATE()
                    FROM Assets a
                    INNER JOIN Inventory i ON a.InventoryID = i.InventoryID
                    INNER JOIN #TEMP t ON a.RoomID = t.RoomID
                    WHERE i.ItemID = @itemID
                    AND a.AssetID IN (SELECT AssetID FROM #TEMP)

                    --Clean up Inventory now.

                    --Delete all Inventory with specific Statuses.
                    DELETE
                    FROM Inventory
                    WHERE ItemID = @itemID
                    AND StatusID IN (3, 6, 12, 14)

                    --Store Inventory records in a temp table
                    SELECT MAX(InventoryID) AS InventoryID, ItemID, StatusID, COUNT(*) AS Quantity
                    INTO #INV_TEMP
                    FROM Inventory
                    WHERE ItemID = @itemID
                    GROUP BY ItemID, StatusID

                    SELECT @maxInvID = MAX(InventoryID)
                    FROM #INV_TEMP

                    SELECT @quantity = t.Quantity
                    FROM #INV_TEMP t
                    WHERE StatusID = @statusID

                    --If there are no more of these items in inventory, change the status.
                    IF(@quantity = 0)
                        SET @statusID = 17

                    --Update the Quantity and Status for the given InventoryID and ItemID.
                    UPDATE Inventory
                    SET Quantity = @quantity,
                        StatusID = @statusID,
                        DateUpdated = GETDATE()
                    WHERE Inventory.InventoryID = @maxInvID 
                    AND Inventory.ItemID = @itemID

                    --Delete all the other Inventory records with the same ItemID
                    DELETE
                    FROM Inventory
                    WHERE InventoryID <> @maxInvID
                    AND ItemID = @itemID

            FETCH NEXT FROM LoopCursor
            INTO @itemID
        END

    CLOSE LoopCursor
    DEALLOCATE LoopCursor
    COMMIT TRANSACTION
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;

        DECLARE @ErrorNumber INT = ERROR_NUMBER();
        DECLARE @ErrorLine INT = ERROR_LINE();
        DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();
        DECLARE @ErrorSeverity INT = ERROR_SEVERITY();
        DECLARE @ErrorState INT = ERROR_STATE();

        PRINT 'Actual error number: ' + CAST(@ErrorNumber AS VARCHAR(10));
        PRINT 'Actual line number: ' + CAST(@ErrorLine AS VARCHAR(10));

        RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH
END
Run Code Online (Sandbox Code Playgroud)

我在游标方面不太擅长,但我需要做的是WHILE块内的所有内容都需要基于 ItemID 运行。我可以将此脚本分成两个文件......一个仅更新资产,另一个仅更新库存表,每个文件都将按预期运行。当我把光标放在它挂起的整个东西周围的那一刻,它就出现了。

简而言之,我们有许多库存记录,它们具有相同的 ItemID,但具有不同的序列号。我们已经放弃了这一点,他们现在只想显示单个库存记录上给定 ItemID 的数量值。对于资产来说也是如此,只不过它们在 InventoryID 上与 Inventory 表连接。因此,由于已设置的关系,我必须首先更新资产部分,然后最后更新库存部分。

任何帮助是极大的赞赏。

Sol*_*zky 5

仅从这段代码来看,就会发现“显然不好”的是,您启动了一个WHILE循环,但在 之前有 2 个IF语句BEGIN

WHILE @@FETCH_STATUS = 0

   --Drop Temp tables if they exist.
   IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
      DROP TABLE #TEMP

   IF OBJECT_ID('tempdb..#INV_TEMP') IS NOT NULL
      DROP TABLE #INV_TEMP

   BEGIN
Run Code Online (Sandbox Code Playgroud)

其效果应该是只有下一个语句是 的一部分WHILE,因此它会无限循环:

IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
      DROP TABLE #TEMP
Run Code Online (Sandbox Code Playgroud)

因为@@FETCH_STATUS永远不会更新,因为FETCH没有达到任何附加语句。

首先将 移至BEGIN之前和之后:WHILE @@FETCH_STATUS = 0IF

IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
      DROP TABLE #TEMP
Run Code Online (Sandbox Code Playgroud)

另外,为什么光标外面BEGIN TRAN/ ?这有效地使得所有单个操作的处理,而不仅仅是每个单个操作的语句。这是你真正想要的吗?或者您只是希望每个 individual及其所有操作成为原子操作,但仍与其他s 分开?如果希望它们分开(这似乎是更可能的情况),则将 移动到for的内部,并将 移动到循环末尾的之前。COMMIT ItemID@ItemID@ItemIDItemIDBEGIN TRANBEGINWHILECOMMITFETCH NEXT FROM LoopCursor