0

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

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

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

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

更新一

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

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

4

3 回答 3

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。如果您需要任何东西,请联系我。

于 2019-06-03T14:09:20.770 回答
0

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

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

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

一般理论

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

一般摘要

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

于 2017-04-11T16:25:19.480 回答
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 
于 2020-07-17T12:23:59.750 回答