【问题标题】:SQL Server FileTable move directory with content T-SQLSQL Server FileTable 移动目录与内容 T-SQL
【发布时间】:2017-04-07 13:42:45
【问题描述】:

我目前正在考虑尝试将 FileTable 中的目录从一个位置移动到另一个位置。我似乎找不到任何有关如何通过 T-SQL 将包含子目录和文件的目录移动到不同位置的信息。

我找到了如何将文件从一个位置移动到另一个位置的示例,只要目录为空,此方法就可以在目录上工作,但当它包含内容时则不会,因为它会引发冲突。

我认为这是因为路径定位器需要在正在移动的目录上的任何下划线内容中重新生成,但我不知道如何实现这一点。

任何帮助或指导将不胜感激

更新一个

在对当前版本的 SQL Server 2016 进行大量研究后,如果没有某种形式的递归逻辑,我看不到执行此操作的方法。我几乎完全拥有了我试图通过在 C# 代码中为我的开源数据库库使用递归逻辑构建的解决方案,而不是全部在 T-SQL 中完成。

一旦完成,我将进一步更新该过程及其工作原理。

【问题讨论】:

    标签: sql-server tsql filetable


    【解决方案1】:

    我今天设法在没有递归的情况下用纯 T-SQL 做到了这一点。您只需要一个源路径和一个目标路径。路径的格式应为FileTableRootPath() + file_stream.GetFileNamespacePath()

    查询应该像 IO.Directory.Move 命令一样工作,即

    • 如果dest文件夹不存在,src重命名为dest路径的最后一位,并移动到dest路径的父文件夹中
    • 如果 dest 文件夹确实存在,则将 src 文件夹移动到 dest 文件夹中
    • 如果 dest 的父级 = src 的父级,那么它只是文件夹的重命名。

    也许这对某人有帮助。我还没有做密集测试。您可能希望在事务中执行此操作并在发生异常时回滚。删除前四行并将@src 和@dest 作为查询参数传递。

    如果你做了一些愚蠢的事情(例如,在移动时射击超过 16 级或@src 不存在),则不会出现优雅的失败。如果@src 不存在,则查询不会做任何事情。如果你违反了 FileTables 的文件夹深度限制,我猜你会在更新过程中遇到错误。

    DECLARE @dest varchar(max)
    DECLARE @src varchar(max)
    SET @src = '\\MachineName\InstanceName\DBName\FileTableName\path\to\src'
    SET @dest = '\\MachineName\InstanceName\DBName\FileTableName\path\to\dest'
    
    DECLARE @srcID hierarchyid;
    SELECT @srcId = GETPATHLOCATOR(@src)
    
    DECLARE @srcParentId hierarchyid
    SELECT @srcParentId = ISNULL(parent_path_locator, 0x) FROM FileTableName WHERE path_locator = @srcId
    
    
    DECLARE @newName varchar(max);
    DECLARE @destParentId hierarchyid;
    SET @destParentId = GetPathLocator(@dest);
    SET @newName = NULL
    IF @destParentId IS NULL
        BEGIN
            SET @destParentID = GetPathLocator(left(@dest, len(@dest) - charindex('\', reverse(@dest) + '\')));
            SET @newName = right(@dest, charindex('\', reverse(@dest) + '\') - 1) 
        END
    
    IF @destParentId != @srcParentId
        UPDATE FileTableName 
        SET path_locator = STUFF(path_locator.ToString(), 1, len(ISNULL(@srcParentId.ToString(), '/')), @destParentId.ToString())
        WHERE path_locator.IsDescendantOf(GetPathLocator(@src)) = 1
    
    IF @newName IS NOT NULL
        UPDATE FileTableName 
        SET name = @newName
        WHERE path_locator = STUFF(@srcId.ToString(), 1, len(ISNULL(@srcParentId.ToString(), '/')), @destParentId.ToString())
    
    

    编辑:我几乎已经实现了整个 System.IO.File 和 System.IO.Directory 类来使用 T-SQL 和 FileTable 而不是直接通过 IO。有需要的可以找我。

    【讨论】:

      【解决方案2】:

      虽然这不是 T-SQL 中的答案,但我认为在这里发布可能会有所帮助,因为它是如何做到这一点的理论。

      我通过使用 C#.Net 解决了这个问题,以便能够创建一个允许我移动目录结构的递归函数。这现在是我的开源数据库库DotNetSDB 的 FileTable 扩展中的内置函数。

      如果您想查看源代码,请随时访问网站并查看 SQL Server General FileTable 扩展更新方法。

      一般理论

      • 递归函数开始
      • 它使用与传递的文件夹相同的名称在新位置创建一个新目录
      • 然后循环遍历旧目录中的所有文件夹并再次运行递归函数,但使用下一个子文件夹层
      • 循环完成后,它会获取所有具有当前目录的文件作为其父级并循环遍历它们,将它们的 parent_path_locators 转移到新文件夹
      • 移动所有文件后,它会获取旧的当前文件夹流 ID
      • 然后它会删除旧的当前目录
      • 删除后,会将新创建的目录流 ID 更新为现在已删除的原始目录。

      一般摘要

      因为这个函数是递归的,它首先创建所有的文件夹结构,然后当它向后工作时,它会将所有文件传输到新位置并一个一个地删除原始目录。在每次递归结束时,我们删除文件夹的原因是我们可以将流 ID 恢复到原来的状态,这样除了物理位置之外没有发生任何变化 搬家了。

      【讨论】:

        【解决方案3】:

        您可以使用此脚本移动包含所有内容的文件夹,但它不是复制粘贴脚本,您需要创建另一个 SP 以在目标中创建空文件夹(在代码中注释)。您可以查看其工作原理的步骤。

        限制:

        • 文件夹树深度必须为
        • 要移动的文件夹不应该存在于目标文件夹中! (脚本检查这个)

        .

        DECLARE @id_movethis UNIQUEIDENTIFIER = 'dc59f988-8c75-49e9-8e42-bdee4dd85f7f' 
        DECLARE @id_moveto UNIQUEIDENTIFIER = '5c80ed42-0742-4f32-a1ed-78a970ba10d0' 
        DECLARE @searchNode_movethis HIERARCHYID; 
        
        SELECT @searchNode_movethis = [path_locator] 
        FROM   [wp].[StorageFiles] 
        WHERE  [stream_id] = @id_movethis 
        
        DECLARE @searchNode_moveto HIERARCHYID; 
        
        SELECT @searchNode_moveto = [path_locator] 
        FROM   [wp].[StorageFiles] 
        WHERE  [stream_id] = @id_moveto 
        
        -- Save the name of the folder to be moved: 
        DECLARE @movingFolderName NVARCHAR(255) 
        
        SELECT @movingFolderName = [name] 
        FROM   [wp].[StorageFiles] 
        WHERE  [path_locator].Getancestor(0) = @searchNode_movethis 
        
        -- Check folder exists in target: 
        DECLARE @isFolderExistsInTarget BIT = 0; 
        
        IF EXISTS (SELECT [stream_id] 
                   FROM   [wp].[StorageFiles] 
                   WHERE  [path_locator].Isdescendantof(@searchNode_moveto) = 1 
                          AND [path_locator].Getlevel() <= 16 
                          AND [name] = @movingFolderName 
                          AND [is_directory] = 1) 
          SET @isFolderExistsInTarget = 1; 
        
        -- Declare variable to save the moved folder path: 
        DECLARE @movedFolderPath NVARCHAR(max) 
        
        IF ( @isFolderExistsInTarget = 1 ) 
          BEGIN 
              PRINT 
          'The specified folder already exists in the target folder. Operation aborted!' 
          END 
        ELSE 
          BEGIN 
              DECLARE @targetPath NVARCHAR(max) = (SELECT [path_locator].Tostring() 
                 FROM   [wp].[StorageFiles] 
                 WHERE  [stream_id] = @id_moveto); 
        
              EXECUTE [wp].[Storage_additemft] 
                -- use your own sp here to create an empty folder 
                @movingFolderName, 
                @targetPath, 
                NULL, 
                'FOLDER', 
                NULL 
        
              SELECT @movedFolderPath = [path_locator].Tostring() 
              FROM   [wp].[StorageFiles] 
              WHERE  [path_locator].Isdescendantof(@searchNode_moveto) = 1 
                     AND [path_locator].Getlevel() <= 16 
                     AND [name] = @movingFolderName 
                     AND [is_directory] = 1; 
        
              -- Generate new path for files and folders and update: 
              DECLARE @replaceThisPart NVARCHAR(max) 
        
              SELECT @replaceThisPart = [path_locator].Tostring() 
              FROM   [wp].[StorageFiles] 
              WHERE  [path_locator].Getancestor(0) = @searchNode_movethis; 
        
              WITH cte 
                   AS (SELECT [stream_id] AS Id 
                       FROM   [wp].[StorageFiles] 
                       WHERE  [path_locator].Isdescendantof(@searchNode_movethis) = 1 
                              AND [path_locator].Getlevel() <= 16 
                       EXCEPT 
                       SELECT [stream_id] AS Id 
                       FROM   [wp].[StorageFiles] 
                       WHERE  [path_locator].Getancestor(0) = @searchNode_movethis) 
              UPDATE [wp].[StorageFiles] 
              SET    [path_locator] = hierarchyid::Parse( 
                                      Replace([path_locator].Tostring(), 
                                             @replaceThisPart, 
                                      @movedFolderPath)) 
              WHERE  [stream_id] IN (SELECT [Id] 
                                     FROM   cte) 
          END 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多