2

SQL Server2008R2を使用してファイルを追跡するファイルシステムアプリがあります。ファイルを削除するときに、パス階層の親を更新して、新しいサイズを反映させたいと思います。現在のFILE_IDに@fid、FILE_SIZEに@ size、階層内の親のFILE_IDに@pidを使用しています。

これが私が使用しているループです:

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

これを実行すると、サイズが更新されません。UPDATEをSELECTに置き換えると、正しく機能しているように見えます。何が起こっている? サイズが更新されないのはなぜですか?これを行うためのより良い方法はありますか?

コンテキストを追加するために、このスニップ-実際には別のループ内で実行されているため、複数のファイルをバッチで削除できます。このコンテキストでのコードは次のとおりです。

-- 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;
4

2 に答える 2

0

役立つ場合に備えて、結果を投稿したいと思いました。これが正しい方法であることを願っています。

影響を受けるパスだけに更新を制限する必要があったので、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;

このスクリプトをストアドプロシージャに分解し始めることができるので、これは素晴らしいことです。デールMに改めて感謝します!

于 2012-12-12T17:54:07.233 に答える
0

これを行うには間違いなくより良い方法があります。概説するアプローチは、手続き型で反復的であり、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)

1つのコマンド、イタリングなし-桁違いに高速である必要があり、毎回葉からずっと正確なファイルサイズが伝播されます。

于 2012-12-12T01:29:07.803 に答える