SQL Server FileTable移动目录包含内容T-SQL

时间:2017-04-07 13:42:46

标签: sql-server tsql filetable

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

我找到了如何将文件从一个位置移动到另一个位置的示例,只要该目录为空,此方法就可以在目录上运行,但是当它在其中有内容时,它会引发冲突。

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

非常感谢任何帮助或指导

更新一个

经过对当前版本的SQL Server 2016的大量研究后,如果没有某种形式的递归逻辑,我无法看到这样做的方法。我几乎完全通过在我的开源数据库库中使用C#代码中的递归逻辑来构建解决方案,而不是在T-SQL中完成所有这些。

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

3 个答案:

答案 0 :(得分: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())

edit:我已经实现了整个System.IO.File和System.IO.Directory类,以便与T-SQL和FileTable一起使用,而不是直接通过IO。如有需要,请打我。

答案 1 :(得分:0)

虽然这不是T-SQL的答案,但我认为这可能对此有用,因为它是如何做到这一点的理论。

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

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

一般理论

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

一般摘要

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

答案 2 :(得分:0)

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

限制:

  • 文件夹树的深度必须为<16。
  • 目标文件夹中不应该存在要移动的文件夹! (脚本对此进行检查)

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