2012-05-07 9 views
9

In precedenza, I requested how to create a directory within a FileTable without using File I/O APIs. Ora voglio creare una sottodirectory nella directory padre che ho appena creato. Come si assegna il mio genitore durante l'inserimento? Sembra che parent_path_locator sia una colonna calcolata.Creazione di sottodirectory tramite SQL INSERT utilizzando FileTable

Questo crea miei genitori ...

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

Come si crea una directory figlio a questo genitore nella mia FileTable?

risposta

9

Questo è quello che ho finito con l'utilizzo di creare una sottodirectory dal GetPathLocator() non genererà un nuovo valore path_locator per me - interpreterà solo esistente hierarchyids.

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); 

Il blocco di codice sopra utilizza la default path_locator value discovered here che costruisce una nuova rappresentazione hierarchyid da un GUID (utilizzando newid() metodo e semplice analisi). La funzione GetNewPathLocator() non esiste da nessuna parte in SQL Server che ho trovato (hierarchyid.GetDescendant() è la più vicina che ho trovato, ma non ha utilizzato la struttura nativa che FileTable si basa su). Forse in 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 funzione GetNewPathLocator() richiede anche una vista SQL getNewID per la richiesta di newid() utilizzando il trick from this SO post.

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

Per chiamare GetNewPathLocator(), è possibile utilizzare il parametro di default che genererà un nuovo hierarchyid o di passare in una rappresentazione hiearchyid stringa esistente (.ToString()) per creare un bambino hierarchyid come si vede qui sotto ...

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

piuttosto che cercare di ricreare il hierarchyid nel codice ho optato per aggiornare il path_locator dopo SQL creato il proprio 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) 

Dove 'Attività' è il nome della mia directory FileTable, 'Test' è il nome della directory che voglio mettere il mio file in, 'MyDoc.txt' è il nome del file e 0x è zero ingresso per un FileStream.

Sono sicuro che sto per trasformare questo in una funzione, abbastanza facile.

See ...

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

Dopo alcuni test rigorosi, sembra l'istruzione di aggiornamento è incline a bloccaporte, quando è in parallelo. – Tod

0

Un'altra opzione è quella di utilizzare l'integrazione CLR e creare funzioni e stored procedure come codice C#.

Ho appena creato un progetto di integrazione CLR GitHub per questo. https://github.com/rhyous/Db.FileTableFramework

Ha varie funzioni o procedure che si desidera: CreateFile, CreateDirectory, DirectoryExists. E su GitHub ovviamente può essere modificato e migliorato da chiunque.

0

ho fatto qualche miglioramento alla risposta:

  1. la funzione restituisce hierarchyid invece di stringa
  2. Se c'è un genitore, hierarchyid :: funzione GetReparentedValue viene utilizzato per generare un nuovo ID al posto di stringa concatenazione.

    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