ループの2番目の繰り返しにハングし続ける次のスクリプトがあります。
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
私はカーソルの領域が得意ではありませんが、WHILE
ブロック内のすべてがItemIDに基づいて実行される必要があることを私が起こす必要があります。このスクリプトを2つのファイルに分離できます。1つはアセットを更新するだけで、もう1つはインベントリテーブルを更新するだけで、それぞれ期待どおりに実行されます。ぶら下がっている全体にカーソルを合わせた瞬間に見えます。
短い話は、同じItemIDを持つが異なるシリアル番号を持つ多くの在庫レコードがあるということです。私たちはこれをやめ、今では特定のItemIDの数量値のみを単一のInventoryレコードに表示したいと考えています。同じことは、InventoryIDとInventoryテーブルを結合することを除いて、アセットについても言えます。したがって、設定されている関係のため、最初に資産部分を更新し、次に在庫部分を最後に更新する必要があります。
どんな助けでも大歓迎です。
このコードを見るだけで、「明らかに良くない」と飛び出すのは、WHILE
ループを開始するが、IF
の前に2つの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
その効果は、次のステートメントのみがWHILE
の一部であり、次の無限ループを実行することです。
IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
DROP TABLE #TEMP
追加のFETCH
ステートメントに到達していないため、@@FETCH_STATUS
は更新されません。
BEGIN
をWHILE @@FETCH_STATUS = 0
の直後と最初のIF
の前に移動します。
WHILE @@FETCH_STATUS = 0
BEGIN
--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 TRAN
/COMMIT
outsideがあるのはなぜですか?これにより、すべてのItemID
sの処理が、各@ItemID
ごとのステートメントだけではなく、単一の操作になります。これは本当に欲しいですか?または、個々の@ItemID
とそのすべての操作をアトミック操作にしたいが、それでも他のItemID
sから分離したいですか?それらを分離したい場合(これはより可能性が高いようです)、BEGIN TRAN
をBEGIN
のWHILE
のすぐ内側に移動し、COMMIT
をFETCH NEXT FROM LoopCursor
の直前に移動します。ループの終わり。