2

我有一个文件系统应用程序,它使用 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;
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)

一个命令,没有迭代 - 应该更快几个数量级,并且您每次都可以获得从叶子一直向上传播的准确文件大小。

于 2012-12-12T01:29:07.803 回答