簡體   English   中英

SQL Server 2008 R2遞歸更新循環

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM