De fapt, sunt două și jumătate dintre ele. Primul (InitMethod + FillRow + fnRecursive) - este TVF -, primind la intrare calea completă către directorul de fișiere, și în acest caz rekordset restaurarea următoarea structură (Tabel evaluată funcția o funcție care returnează rezultatul într-o formă de tabel.):
ID-ul HierarchyID, FULLNAME nvarchar (1000), DateModified datetime2, DateCreated datetime2, LastAccessed datetime2, dimensiunea bigint, biți isDir, XML Proprietăți
Primul câmp este utilizat ca ID de înregistrare în tabel. În același timp, determină poziția nodului în ierarhie, ceea ce vă permite să faceți fără legarea tradițională a părintelui-copil atunci când construiți o tabelă de copaci. Consultați postările HierarchyID și parent-child pentru acest subiect. Dir () și IerarhideID.
Următoarele câmpuri - un fișier tradițional atribute: numele complet lui (cale), data ultimei modificări, data creării, data de acces și ultima dimensiune (în bytes), și o indicație boolean dacă obiectul este un director sau un fișier.
Dispozitivul CLR-TVF a fost sortat în post. Funcții de tabel CLR pentru TC. Puteți învăța din ea, pentru care aveți nevoie de metoda de ancorare InitMethod, metoda iterativă FillRow și tipul structurii row_item și o listă de elemente de acest tip. În cazul navigării profunde în dosar, este necesar să căutați subfolderul imbricat, provocându-i recursiv aceleași acțiuni ca și pentru dosarul părinte. În principiu, TVF recursiv nu diferă ideologic de cele obișnuite, dar doar în cazul în care puteți citi posturile recursive CLR TVF și Recursive TVF-2. Ca funcție auxiliară pentru organizarea recursului, actele fnRecursive.
Pe măsură ce sunt sortate fișierele, aș dori să aflu câteva informații despre progresul procesului. TVF nu poate scoate nimic din status decât recordul rezultat, așa că am decis să creez un fel de jurnal, unde să scriu dosarul și ora curentă. Ar fi logic să o păstrați împreună cu restul jurnalelor de mesaje SQL Server. Funcția GetSqlErrLogPath () obține un dosar în care SQL Server își păstrează jurnalele de eroare. Locația obiectelor de servicii SQL Server a fost sortată în post. Cum se determină locația implicită pentru fișierele bazei de date. Deoarece această funcție se referă la înregistrare, metoda InitMethod din care este apelată este setată la atributul SystemDataAccessKind.Read.
Procedura de recursive fnRecursive row_item creează un nou tip de intrare în listă pentru fiecare fișier sau podfoldera ceartă, de umplere domeniul ei. Ierarhia este obținută din valoarea părintească și din cel mai apropiat vecin vechi. Atributele extins sunt colectate de către funcția FileExtProps ca XML. care este, de asemenea, inserat în row_item. Alte atribute sunt luate din obiectul IO.FileSystemInfo, care este o generalizare a fișierului și a directorului. Dacă acesta este un fișier, mărimea este încă luată pentru el, în cazul în care directorul, procedura fnRecursive se conectează cu parametrul acestui director.
Ansamblul este realizat sub forma unei biblioteci de clase și va fi executat manual pe SQL Server, deoarece utilizează biblioteca COMOM% windir% \ System32 \ SHELL32.dll. Despre aceasta este posibil să citiți într-un post Utilizați COM dll în SQL CLR. Din același motiv, ansamblul trebuie configurat ca nesigur - consultați Semnarea unui ansamblu extern sau nesigur. Și am pus o duză de lemn pe ea, pentru că nimeni nu va termina să citească acest loc. Conform ultimelor rapoarte Rosstat, eliminarea mssqlsystemresource.mdf vă permite să creșteți performanța serverului SQL cu 12,3%. Construcția ansamblului compilat se realizează prin intermediul T-SQL:
--selectați * din sys.objects
declarând @fullName nvarchar (500), @ cmd nvarchar (1000), @i int = 0
declara cur cursor forward_only scroll_locks pentru selectarea fullName de la t unde isDir = 0 pentru actualizarea conținutului; deschideți cur
în timp ce 1 = 1 începe
aduceți următoarea dată din cur în @fullName
dacă @@ fetch_status <> 0 pauză
imprimare distribuită (@ i ca varchar (10)) + '. '+ @fullName
set @cmd = 'actualizare conținut t set = (selectați BulkColumn din OPENROWSET (vrac' '' + @fullName + '' 'single_blob) ca f) în cazul în care curent de javră'
încearcă să încerci; exec (@ cmd); încercați sfârșitul
începe captura; print 'Nu se poate încărca fișier din cauza' + Error_Message (); captura de capăt
aproape cur; dezarhivează cur
setați oprirea
Am pregătit o altă versiune a descărcării ca procedură LoadDirWithFileContent. Acesta diferă de funcția InitMethod că navele un dosar direct într-un tabel al cărui nume este trecut la ea ca unul dintre parametrii, prin urmare, Blobs, am aploudim pe SQL Server, o dată Persistă și poate fi turnat imediat, fără teama de memorie preaplin. Tabelul, care este încărcat de ipoteză ar trebui să fie obligatoriu set de câmpuri cu nume specificate și tipuri (vezi. Tabelul 5 Dir în scenariu). În plus, pot exista alte câmpuri în tabel. La turnarea bloburilor în procedura CLR, se folosește soluția luată în considerare după postul Import / Export de Câmpuri Blob la Fișiere - CLR. Dacă cineva a auzit despre SqlFileStream API, mă întreb de ce nu utilizează această abordare, citiți post filestream actualizare parțială. Procedura de serviciu spRecursive joacă același rol ca și fnRecursive pentru TVF Dir () LoadDir pentru procedura stocată. Dir () și procedura LoadDir poate fi utilizat în mod independent, și am avut ideea de a le elibera sub forma unor metode UDT, care este pur și simplu privit ca un container pentru funcțiile bibliotecii. Din păcate, TVF nu este permisă sub formă de metode UDT, deci totul va fi în formă risipită. Crearea procedurii LoadDir din ansamblu și lansarea ei arată astfel:
drop Dir
creați proc LoadDir @folder nvarchar (255), bit @ shallowTraversal. @tblName sysname ca nume extern MyAssembly. FileSystem. LoadDirWithFileContent
dacă object_id ('Dir'. 'U') nu este tabelul Dir
crea tabelul Dir (ID HierarchyID. FULLNAME nvarchar (1000), DateModified datetime2. DateCreated datetime2. datetime2 LastAccessed. Proprietăți XML. Dimensiunea bigint. biți isDir. Conținut VARBINARY (max))
exec LoadDir 'c: \ Temp'. 0. 'Dir' - 0, dacă ne scufundăm în subfoldere; 1, dacă înoțiți bine
Această descărcare este exact aceeași cu: \ Temp 450 MB într-un alt tabel. Execuția a durat 4 minute. 30 de secunde care este atipică. În timp ce mă depanasem, timpul tipic pentru ea era de 3 minute. +/- câteva secunde. Se pare că ideea este că nu am scăpat tabelul. De obicei, T șterge tabelele cu rezultatele funcției înainte de a trece la o procedură, iar acum sa transformat dimensiunea zadvoeny a bazei - nu la 450 de metri și 936. Ea a devenit avtopriraschat mdfnik, și în timp ce zăcea în c: \ temp, au început să interfereze cu citirea unul la altul.
Un jurnal este creat în timpul descărcării. Se află în același director ca ErrorLog și se numește hard SqlFSLoader.log. Întrucât Flush () este făcut după fiecare linie terminată, o puteți redescoperi și puteți observa progresul procesului de încărcare.
SqlCommand cmd = cnn.CreateCommand ();
cmd.CommandText = "insert" + tableName + „(ID, FULLNAME, DateModified, DateCreated, LastAccessed, Properties, dimensiune, isDir, Conținut) valori (@hid, @fullName, @dateModified, @dateCreated, @lastAccessed, @properties, @size, @isDir, @content) ";
cmd.Parameters.Add (noul SqlParameter ("@hid" .SqlHierarchyId .Null)); //SqlDbType.SqlHierarchyDe nu este în listă, trebuie să faceți cu valoarea.
cmd.Parameters ["@hid"] .UdtTypeName = "IerarhieID"; // Altfel System.ArgumentException: Proprietatea UdtTypeName trebuie setată pentru parametrii UDT.
cmd.Parameters.Add (noul SqlParameter ("@fullName" .SqlDbType .NVarChar));
cmd.Parameters.Add (noul SqlParameter ("@dateModified" .SqlDbType .DateTime2));
cmd.Parameters.Add (noul SqlParameter ("@dateCreated" .SqlDbType .DateTime2));
cmd.Parameters.Add (noul SqlParameter ("@lastAccessed" .SqlDbType .DateTime2));
cmd.Parameters.Add (noul SqlParameter ("@properties", SqlDbType .Xml));
cmd.Parameters.Add (noul SqlParameter ("@size" .SqlDbType .BigInt));
cmd.Parameters.Add (noul SqlParameter ("@isDir" .SqlDbType .Bit));
cmd.Parameters.Add (noul SqlParameter ("@content" .SqlDbType. VarBinary));
SqlHierarchyId leftSibling = SqlHierarchyId .Null;
Shell32. Folder shellFolder = shell.NameSpace (folderPath);
FileStream fs = null;
foreach (FileSystemInfo fd în fsi)
cmd.Parameters [ "@hid"] .value = leftSibling = parentId.GetDescendant (leftSibling, SqlHierarchyId .Null);
cmd.Parameters ["@fullName"]. Valoare = fd.FullName;
cmd.Parameters ["@properties"]. Valoare = FileExtProps (fd.FullName, shellFolder);
cmd.Parametrii ["@isDir"] .Value = (fd este DirectorInfo);
cmd.Parameters ["@dateModified"] .Value = fd.LastWriteTimeUtc;
cmd.Parameters ["@dateCreated"]. Valoare = fd.CreationTimeUtc;
cmd.Parameters ["@lastAccessed"]. Valoare = fd.LastAccessTimeUtc;
try // În cazul în care nu există acces la fișier, OpenRead () va face excepție
șir de spații = șir nou ("." leftSibling.GetLevel ().);
log.Write (spații + fd.FullName + "." + DateTime.Now.ToString () + "."); log.Flush ();
dacă (fd este FileInfo)
cmd.Parameters ["@size"]. Valoare = ((FileInfo) fd) .Lungime;
fs = ((FișierInfo) fd) .OpenRead ();
cmd.Parameters ["@content"] .Value = SqlBytes noi (fs);
altfel dacă (! shallowTraversal) spRecursive (fd.FullName, false, tableName, leftSibling);