[英]SQL Server 2008 R2 Recursive Update Loop
我有一個使用SQL Server 2008 R2來跟蹤文件的文件系統應用程序。 刪除文件時,我想更新路徑層次結構中的父級以反映其新大小。 我將@fid用作當前的FILE_ID,將@size用作FILE_SIZE,並將@pid用作層次結構中父級的FILE_ID。
這是我正在使用的循環:
SELECT @pid=PARENT_ID FROM FILES WHERE FILE_ID=@fid;
WHILE @pid<>0
BEGIN
UPDATE FILES
SET
FILE_SIZE =
-- Avoid potential situation where new file size might incorrectly drop below 0
CASE
WHEN FILE_SIZE-@size>=0 THEN FILE_SIZE-@size
ELSE 0
END
WHERE FILE_ID=@pid;
SET @fid=@pid;
SELECT @pid=PARENT_ID FROM FILES WHERE FILE_ID=@fid;
END
當我運行它時,大小沒有更新。 如果我將SELECT替換為UPDATE,則看起來應該可以正常工作。 到底是怎么回事? 為什么尺寸沒有更新? 有一個更好的方法嗎?
要添加一些上下文,此snip-it實際上在另一個循環中運行,因此可以批量刪除多個文件。 這是此上下文中的代碼:
-- Declarations
DECLARE @fid int, @size int, @pid int;
DECLARE c CURSOR FOR
SELECT
FILE_ID, FILE_SIZE
FROM
FILES
OPEN c;
-- Initialize variables
FETCH NEXT FROM c
INTO @fid, @size;
-- Main loop
WHILE @@FETCH_STATUS = 0
BEGIN
-- Statements to delete the file --
-- Loop to update sizes --
SELECT @pid=PARENT_ID FROM FILES WHERE FILE_ID=@fid;
WHILE @pid<>0
BEGIN
UPDATE FILES
SET
FILE_SIZE =
CASE
WHEN FILE_SIZE-@size>=0 THEN FILE_SIZE-@size
ELSE 0
END
WHERE FILE_ID=@pid;
SET @fid=@pid;
SELECT @pid=PARENT_ID FROM FILES WHERE FILE_ID=@fid;
END
FETCH NEXT FROM c
INTO @fid, @size;
END
CLOSE c;
DEALLOCATE c;
肯定有更好的方法可以做到這一點。 您概述的方法是過程性的,迭代式的,並且對於C#和Visual Basic這樣的語言非常有用,但是,對於SQL游標中最有效的解決方案,您應該尋求的最后一個技巧。
這應該工作
WITH FileList
AS
(
-- Select all the files that have no children
SELECT f.FILE_ID
,f.FILE_SIZE
,f.PARENT_ID
,0 AS CHILD_ID
,0 AS Depth
FROM FILES f
LEFT JOIN
FILES f1 ON f.FILE_ID=f1.PARENT_ID
WHERE f1.PARENT_ID IS NULL
UNION ALL
-- Then recursively select thrir parents
SELECT f.FILE_ID
,fl.FILE_SIZE
,f.PARENT_ID
,fl.FILE_ID
,fl.Depth + 1
FROM FileList fl
INNER JOIN
FILES f ON f.FILE_ID=fl.PARENT_ID
)
-- With this data update the file size with the sum of all leaf nodes
UPDATE FILES
SET FILE_SIZE = (SELECT SUM(FILE_SIZE)
FROM FileList fl
WHERE fl.FILE_ID=FILES.FILE_ID
GROUP BY FILE_ID)
一個命令,不發信號-應該快幾個數量級,並且您將獲得每次從葉子一直向上傳播的准確文件大小。
我想發布結果,以防萬一。 我希望這是正確的方法。
我需要將更新限制為僅受影響的路徑,因此根據Dale M的建議,我最終使用了類似的方法:
-- Since I already have @size and @pid, I use them here
WITH FileList
AS
(
-- Select the parent of the delete target
SELECT f.FILE_ID
,f.PARENT_ID
,f.FILE_SIZE-@size AS FILE_SIZE
FROM FILES f
WHERE f.FILE_ID=@pid
UNION ALL
-- Then recursively select its parents
SELECT f.FILE_ID
,f.PARENT_ID
,f.FILE_SIZE-@size AS FILE_SIZE
FROM FileList fl
INNER JOIN
FILES f ON f.FILE_ID=fl.PARENT_ID
)
-- Update FILES with the size, already adjusted in the CTE above
UPDATE FILES
SET FILE_SIZE=fl.FILE_SIZE
FROM FILES
INNER JOIN
FileList fl ON fl.FILE_ID=FILES.FILE_ID;
很好,因為我可以開始將此腳本分解為存儲過程。 再次感謝Dale M!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.