2012-05-07 17 views
9

Anteriormente, I requested how to create a directory within a FileTable without using File I/O APIs. Ahora quiero crear un subdirectorio para el directorio principal que acabo de crear. ¿Cómo puedo asignar a mi padre durante la inserción? Parece que parent_path_locator es una columna calculada.Crear directorio secundario a través de SQL INSERT usando FileTable

Esto crea mis padres ...

INSERT INTO FileTable0 (name,is_directory,is_archive) VALUES ('Directory', 1, 0); 

¿Cómo se crea un directorio niño a este padre en mi FileTable?

Respuesta

9

Esto es lo que terminé usando para crear un subdirectorio desde GetPathLocator() no generará un nuevo valor path_locator para mí - que sólo interpretará hierarchyids existente.

DECLARE @parentdir table(path hierarchyid not null); 
DECLARE @subdir_locator hierarchyid 

-- Create Parent Directory, OUTPUT inserted parent path 
INSERT INTO FileTable0 (name,is_directory,is_archive) 
OUTPUT INSERTED.path_locator into @parentdir 
SELECT 'Directory', 1, 0 

-- Create new path_locator based upon parent 
SELECT @subdir_locator = dbo.GetNewPathLocator(path) from @parentdir 

-- Create Subdirectory 
INSERT INTO FileTable0 (name,path_locator,is_directory,is_archive) 
VALUES ('subdirectory', @subdir_locator, 1, 0); 

El bloque de código anterior utiliza la default path_locator value discovered here que construye una nueva representación hierarchyid de un GUID (utilizando newid() método, y el análisis sencillo). La función GetNewPathLocator() no existe en ningún lugar de SQL Server que pude encontrar (hierarchyid.GetDescendant() es lo más parecido que pude encontrar, pero no utilizó la estructura nativa en la que FileTable se basa). Tal vez en SQL.NEXT ...

CREATE FUNCTION dbo.GetNewPathLocator (@parent hierarchyid = null) RETURNS varchar(max) AS 
BEGIN  
    DECLARE @result varchar(max), @newid uniqueidentifier -- declare new path locator, newid placeholder  
    SELECT @newid = new_id FROM dbo.getNewID; -- retrieve new GUID  
    SELECT @result = ISNULL(@parent.ToString(), '/') + -- append parent if present, otherwise assume root 
        convert(varchar(20), convert(bigint, substring(convert(binary(16), @newid), 1, 6))) + '.' + 
        convert(varchar(20), convert(bigint, substring(convert(binary(16), @newid), 7, 6))) + '.' + 
        convert(varchar(20), convert(bigint, substring(convert(binary(16), @newid), 13, 4))) + '/'  
    RETURN @result -- return new path locator  
END 
GO 

La función GetNewPathLocator() requiere también una vista de SQL getNewID para solicitar una newid() utilizando el trick from this SO post.

create view dbo.getNewID as select newid() as new_id 

Para llamar GetNewPathLocator(), puede utilizar el parámetro por defecto que generará un nuevo hierarchyid o pasar de una representación hiearchyid cadena existente (.ToString()) para crear un niño hierarchyid como se ve a continuación ...

SELECT dbo.GetNewPathLocator(DEFAULT); -- returns /260114589149012.132219338860058.565765146/ 
SELECT dbo.GetNewPathLocator('/260114589149012.132219338860058.565765146/'); -- returns /260114589149012.132219338860058.565765146/141008901849245.92649220230059.752793580/ 
0

en lugar de tratar de recrear la hierarchyid en el código he optado por actualizar el path_locator después de SQL creado su propio ID:

DECLARE @pathID hierarchyid; 
DECLARE @parentdir table(path hierarchyid not null); 

IF NOT EXISTS(SELECT 1 FROM FileAsset WHERE is_directory = 1 AND file_stream.GetFileNamespacePath() = '\Assets\Test') 
INSERT INTO FileAsset (name, is_directory) VALUES('Test', 1) 

SELECT @pathID = FileAsset.path_locator FROM FileAsset WHERE file_stream.GetFileNamespacePath() = '\Assets\Test' 

INSERT INTO FileAsset (name, file_stream) OUTPUT INSERTED.path_locator into @parentdir VALUES('MyDoc.txt', 0x) 

UPDATE FileAsset SET path_locator = '/' + REPLACE(@pathID.ToString(), '/','') + path_locator.ToString() WHERE path_locator = (SELECT [path] FROM @parentdir) 

Donde 'Assets' es el nombre de mi directorio FileTable, 'Test' es el nombre del directorio en el que quiero poner mi archivo, 'MyDoc.txt' es el nombre de archivo y 0x es entrada cero para filestream.

Estoy seguro de que estoy a punto de convertir esto en una función, bastante fácil.

Ver ...

CREATE PROCEDURE InsertFileAsset 

    @fileName varchar(255), 
    @dirName varchar(255), 
    @data varbinary(MAX), 
    @stream_id uniqueidentifier OUTPUT 
AS 
BEGIN 
    DECLARE @pathID hierarchyid; 
    DECLARE @parentdir table(path hierarchyid not null); 
    DECLARE @streamID table(streamID uniqueidentifier not null); 

    IF NOT EXISTS(SELECT 1 FROM FileAsset WHERE is_directory = 1 AND file_stream.GetFileNamespacePath() = '\Assets\' + @dirName) 
    INSERT INTO FileAsset (name, is_directory) VALUES(@dirName, 1) 

    SELECT @pathID = FileAsset.path_locator FROM FileAsset WHERE file_stream.GetFileNamespacePath() = '\Assets\' + @dirName 

    INSERT INTO FileAsset (name, file_stream) OUTPUT INSERTED.path_locator into @parentdir VALUES(@fileName, @data) 

    UPDATE FileAsset SET path_locator = '/' + REPLACE(@pathID.ToString(), '/','') + path_locator.ToString() OUTPUT inserted.stream_id INTO @streamID WHERE path_locator = (SELECT [path] FROM @parentdir) 

    SELECT @stream_id = streamID FROM @streamID 

    RETURN 
END 
GO 
+0

Después de algunas pruebas rigurosas, parece que la instrucción de actualización es propenso a interbloqueos cuando este se llama en paralelo. – Tod

0

Otra opción es utilizar la integración CLR y crear funciones y procedimientos almacenados como código C#.

Acabo de crear un proyecto de integración GitHub CLR para esto. https://github.com/rhyous/Db.FileTableFramework

Tiene varias funciones o procedimientos que usted desearía: CreateFile, CreateDirectory, DirectoryExists. Y en GitHub, por supuesto, puede ser modificado y mejorado por cualquier persona.

0

Hice una cierta mejora de la respuesta:

  1. La función retorna hierarchyid en lugar de cadena
  2. Si hay un padre, hierarchyid :: función GetReparentedValue se utiliza para generar un nuevo ID en lugar de cuerdas concatenación.

    create function doc.GetNewPathLocator (@parent hierarchyid = null) returns hierarchyid 
    as 
    begin 
        declare @id uniqueidentifier = (select new_id from dbo.GetNewID); 
        declare @path hierarchyid = (convert(hierarchyid, '/' + 
          convert(varchar(20), convert(bigint, substring(convert(binary(16), @id), 1, 6))) + '.' +  
          convert(varchar(20), convert(bigint, substring(convert(binary(16), @id), 7, 6))) + '.' +  
          convert(varchar(20), convert(bigint, substring(convert(binary(16), @id), 13, 4))) + '/')); 
        return case when @parent is null then @path else @path.GetReparentedValue(hierarchyid::GetRoot(), @parent) end; 
    end 
    go