Database
 sql >> Datenbank >  >> RDS >> Database

Speichern von Dateien in einer SQL-Datenbank mit FILESTREAM – Teil 2

In meinem vorherigen Artikel habe ich beschrieben, wie Sie FILESTREAM in SQL Server konfigurieren und FILESTREAM-fähige Datenbanken und Tabellen erstellen. Außerdem habe ich gezeigt, wie man Daten aus der FILESTREAM-Tabelle einfügt und löscht.

In diesem Artikel werde ich demonstrieren, wie mehrere Dateien mithilfe von T-SQL in eine FILESTREAM-Tabelle eingefügt werden.

In dieser Demo verwenden wir das PowerShell-Modul, um die Liste der Dateien zu füllen und in der SQL-Tabelle zu speichern.

Voraussetzungsprüfungen und nützliche Abfragen zum Abrufen von FILESTREAM-Konfigurationen

Für diese Demo verwende ich:

  1. SQL-Version:SQL Server 2017
  2. Datenbank:FileStream_Demo Datenbank
  3. Tools:PowerShell, SQL Server Management Studio, SQL Server Data Tools.

In meinem vorherigen Artikel habe ich eine Datenbank namens FileStream_Demo erstellt . Die FILESTREAM-Funktion ist auf der SQL Server-Instanz aktiviert, und die Datenbank verfügt über die Zugriffsberechtigung T-SQL und Win32.

Um die Einstellungen der FILESTREAM-Zugriffsebene zu überprüfen, führen Sie die folgende Abfrage aus:

Use FileStream_Demo
Go
SELECT Host_Name() as 'Server Name' ,NAME as 'Database Configuration',
       CASE
         WHEN value = 0 THEN 'FILESTREAM is Disabled'
         WHEN value = 1 THEN
         'Enabled for T-SQL'
         WHEN value = 2 THEN
         'Enabled for T-SQL and Win32'
       END AS 'FILESTREAM Option'
FROM   sys.configurations
WHERE NAME = 'filestream access level'
Go

Die Ausgabe der Abfrage sieht wie folgt aus:

Um die Datenbankdateien und den Speicherort des FILESTREAM-Datencontainers zu überprüfen, führen Sie die folgende Abfrage aus:

Use FileStream_Demo
Go
SELECT Host_Name() as 'Server Name',NAME As 'Filegroup Name',        type_desc as 'Filegroup Type',       physical_name as 'Database File Location'  FROM   sys.database_files

Die Ausgabe der Abfrage sieht wie folgt aus:

Fügen Sie mehrere Dateien mit SQL-Skript ein

So fügen Sie mehrere Dateien in eine SQL-Tabelle ein:

  1. Erstellen Sie zwei SQL-Tabellen mit dem Namen Document_List und Document_Content . Der Document_Content Tabelle hat die FileStreamCol Spalte mit dem Datentyp VARBINARY(MAX) und dem Spaltenattribut FILESTREAM. Der Inhalt der Dateien innerhalb des Verzeichnisses wird in VARBINARY(MAX) konvertiert und in FileStreamCol gespeichert Spalte von Document_Content Tabelle.
  2. Erstellen Sie eine dynamische SQL-Abfrage, die die Document_Location durchläuft Tabelle, um den Pfad der Dateien zu erhalten und Dateien in Document_Content einzufügen Tabellen.
  3. Verpacken Sie den gesamten T-SQL-Code in einer gespeicherten Prozedur.

SQL-Tabellen erstellen

Erstellen Sie zunächst eine globale temporäre Tabelle, um die Details der Dateien zu speichern. Führen Sie dazu die folgende Abfrage in der FileStream_Demo aus Datenbank.

USE [FileStream_Demo]
GO
Create table Document_List
(
    ID int identity(1,1) Primary Key clustered,
    fullname Varchar(max),
    name Varchar(max),
    attributes Varchar(250),
    CreationTime datetime,
    LastAccessTime datetime,
    LastWriteTime datetime,
    Length numeric(10,2)
)

Erstellen Sie außerdem eine Tabelle, um die Dateien in der Tabelle zu speichern. Führen Sie die folgende Abfrage aus, um eine physische Tabelle zu erstellen:

USE [FileStream_Demo]
GO
CREATE TABLE [dbo].[Document_Content ](
	[ID] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
	[RootDirectory] [varchar](max) NULL,
	[FileName] [varchar](max) NULL,
	[FileAttribute] [varchar](150) NULL,
	[FileCreateDate] [datetime] NULL,
	[FileSize] [numeric](10, 5) NULL,
	[FileStreamCol] [varbinary](max) FILESTREAM  NULL,
UNIQUE NONCLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] FILESTREAM_ON [Dummy-Documents]
GO

Um die Leistung der Auswahlabfrage zu verbessern, fügen Sie einen gruppierten Index für FileName hinzu und Dateityp Spalten des Document_Content Tisch. Führen Sie dazu folgenden Code aus:

USE [FileStream_Demo]
GO
CREATE CLUSTERED INDEX [ICX_Document_Content_FileName] ON [dbo].[Document_Content]
(
	[FileName] ASC,
	[FileType] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] FILESTREAM_ON [Dummy-Documents]
GO

PowerShell-Modul erstellen, um Dateidetails aufzufüllen

Sobald die Tabellen erstellt sind, führen Sie das PowerShell-Skript aus, um Details der Dateien in die Document_List einzufügen Tisch. Das PowerShell-Skript wird innerhalb der gespeicherten T-SQL-Prozedur ausgeführt. Um den gesamten Code in die SQL-Prozedur zu schreiben, müssen Sie daher eine PowerShell-Funktion erstellen. Der Verzeichnispfad ist ein obligatorischer Eingabeparameter der Funktion. Das Skript ruft die Liste der Dateien ab und befindet sich in dem Verzeichnisparameter, der zum Ausführen der PowerShell-Funktion verwendet wird.

Der Code lautet wie folgt:

  1. Erstellen Sie eine Funktion und deklarieren Sie obligatorische Eingabeparameter. Der Code lautet wie folgt:
    function global:getFileList
    {
    param(
        [Parameter(Position=0,mandatory=$true)]
        [string[]] $FilePath
    )
  2. Konstruieren Sie einen String mit einer „Insert“-Abfrage. Siehe folgenden Code:
    [email protected]'
    INSERT INTO ##Document_List(
    	fullname,	name,	attributes,	CreationTime,	LastAccessTime,	LastWriteTime,
        Length
    ) 
    VALUES (
    	'{0}',
    	'{1}',
    	'{2}',
    	'{3}',
    	'{4}',
    	'{5}',
        '{6}'
    )
    '@
  3. Rufen Sie die Liste der Dateien mit dem Befehl Get-ChildItem -Recurse formatieren Sie die Ausgabe des Befehls. Der Code lautet wie folgt:
    Get-ChildItem -Recurse $Directorypath | 
    select @{Label="FullName";Expression={split-path($_.FullName)}},	name,	attributes,	CreationTime,	LastAccessTime,	LastWriteTime,@{Label="Length";Expression={$_.Length / 1MB -as [int] }}
  4. Speichern Sie die Ausgabe mithilfe der For-Each-Schleife im Document_content Tisch. Um die Abfrage auf FileStream_Demo auszuführen Datenbank verwendet das Skript Invoke-Sqlcmd . Der Code lautet wie folgt:
    ForEach-Object {
    		$SQL = $sqltmplt -f $_.FullName, $_.name, $_.attributes, $_.CreationTime, $_.LastAccessTime, $_.LastWriteTime,$_.Length	
    		Invoke-sqlcmd -Query $SQL -ServerInstance TTI412-VM\SQL2017 -database FileStream_Demo
    	}

Der gesamte Code der PowerShell-Funktion sieht wie folgt aus:

function global:getFileList
{
param(
    [Parameter(Position=0,mandatory=$true)]
    [string[]] $FilePath
)
Write-Output "Inserting files"
[email protected]'
INSERT INTO dbo.Document_List(
	fullname,	name,	attributes,	CreationTime,	LastAccessTime,	LastWriteTime,
    Length    
) 
VALUES (
	'{0}',
	'{1}',
	'{2}',
	'{3}',
	'{4}',
	'{5}',
    '{6}'
)
'@
Invoke-Sqlcmd -Query "Truncate Table Document_List" -ServerInstance TTI412-VM\SQL2017 -database FileStream_Demo
Get-ChildItem -Recurse $FilePath  | 
select @{Label="FullName";Expression={split-path($_.FullName)}},name,attributes,	CreationTime,	LastAccessTime,	LastWriteTime,@{Label="Length";Expression={$_.Length / 1MB -as [int] }}|
	ForEach-Object 
{
$SQL = $sqltmplt -f $_.FullName, $_.name,$_.attributes, $_.CreationTime, $_.LastAccessTime, $_.LastWriteTime,$_.Length		

        Invoke-sqlcmd -Query $SQL -ServerInstance TTI412-VM\SQL2017 -database FileStream_Demo
}
Write-Output "File Inserted successfully... Below Is a list of files."
}

Um die PowerShell-Funktion innerhalb der gespeicherten SQL-Prozedur zu verwenden, müssen wir das obige Skript als PowerShell-Modul registrieren. Erstellen Sie dazu ein Verzeichnis mit dem Namen getFileList unter C:\Windows\System32\WindowsPowerShell\v1.0\Modules . Um ein beliebiges PowerShell-Skript als Modul zu registrieren, sollten die Skript- und Verzeichnisnamen identisch sein. Speichern Sie daher das obige Skript als getFileList.psm1 in der getFileList Verzeichnis.

Wenn wir nun das PowerShell-Skript von T-SQL ausgeführt haben, müssen wir die getFileList importieren Modul. Fügen Sie dazu den folgenden Code im PowerShell-Profil hinzu. Das PowerShell-Profil wird unter C:\Windows\System32\WindowsPowerShell\v1.0 erstellt Ort.

import-module getFileList

Wenn das Profil nicht existiert, führen Sie den folgenden Befehl aus, um ein Profil zu erstellen.

New-Item -Type File -Path $PROFILE.AllUsersAllHosts -Force

Erstellen Sie eine gespeicherte Prozedur zum Importieren von Dateien

Sobald wir die Dateiliste und Informationen in der SQL-Tabelle gespeichert haben, werden wir die Dateien in den Document_Content einfügen Tabelle.

Um diese Aufgabe effektiv auszuführen, erstellen Sie eine parametrisierte gespeicherte Prozedur mit dem Namen sp_Insert_Documents . Es wird die FileLocation verwenden Parameter, der vom Datentyp varchar ist. Die Prozedur füllt die Liste der Dateien aus dem im Parameter angegebenen Speicherort und fügt alle Dateien in Document_Content ein Tabelle.

Schritt 1:Ändern Sie die Konfigurationsparameter.

Um den PowerShell-Befehl mit T-SQL auszuführen, aktivieren Sie xp_cmdshell Konfigurationsmöglichkeit. Es ist eine erweiterte Konfigurationsoption; daher vor dem Aktivieren von xp_cmdshell , aktivieren Sie die Option "Erweitert anzeigen" Konfigurationsmöglichkeit. Führen Sie dazu die folgenden T-SQL-Befehle nacheinander aus.

use master
go
exec sp_configure 'show advanced option',1
reconfigure with override

Exec sp_configure 'xp_cmdshell',1
Reconfigure with override

Schritt 2:Verwenden Sie das PowerShell-Skript, um die Dateiliste innerhalb des T-SQL-Codes zu füllen

Um ein PowerShell-Skript mit T-SQL auszuführen, verwenden Sie xp_cmdshell Verfahren. Es führt den PowerShell-Befehl aus, der eine Liste von Dateien und ihre Details in der Document_List auffüllt Tabelle.
Der Code lautet wie folgt:

declare @PSScript varchar(2500)
set @PSScript= 'powershell.exe getFileList ''' + @FileLoc +'''' 
exec xp_cmdshell @PSScript

Schritt 3:Erstellen Sie eine dynamische SQL-Abfrage, um den Speicherort der Datei abzurufen

Erstellen Sie eine dynamische SQL-Abfrage, die die Document_List durchläuft Tabelle, lädt den Inhalt der Datei, die sich unter dem in FullName angegebenen Pfad befindet Spalte, wandelt sie in die Spalte VARBINAR(MAX) um und fügt sie in den Document_Content ein Tisch. Zusammen mit Datei fügt das Skript Dateiname, Dateiattribut, Dateigröße und Dateityp in den Document_Content ein Tisch. Das Skript verwendet die Groß-/Kleinschreibung Ausdruck, um den Dateityp zu bestimmen.

Der Code lautet wie folgt:

SET @FileCount = (SELECT Count(*)
                  FROM   Document_List)
                  WHILE ( @i < @FileCount )       
BEGIN
   SET @FileName = (SELECT TOP 1 name
                    FROM   Document_List) 

   /* Concate DirectoryLocation and FileName column to generate FQDN. */    
   SET @FileName = (SELECT TOP 1  Name 
                        FROM   Document_List) 
   SET @FileLocation = (SELECT TOP 1  fullname 
                        FROM   Document_List where name= @FileName)
   SET @FileAttribute = (SELECT TOP 1  attributes 
                        FROM   Document_List where name= @FileName)
   SET @FileCreateDate = (SELECT TOP 1  CreationTime 
                        FROM   Document_List where name= @FileName) 
   SET @FileSize = (SELECT TOP 1  Length 
                        FROM   Document_List where name= @FileName) 
   SET @FileType = (SELECT TOP 1  CASE
               WHEN ( name     LIKE '%jpg%' )
                     OR ( name LIKE '%png%' )
                     OR ( name LIKE '%jpg%' )
                     OR ( name LIKE '%bmp%' ) THEN 'Images'

				WHEN ( name  LIKE '%txt%' )THEN 'Text Files'
                     When ( name LIKE '%xls%' )THEN 'Text Files'
                     When ( name LIKE '%doc%' ) THEN 'Text Files'
               ELSE 'Other Files'
             END                AS 'File Type'
                        FROM   Document_List where name= @FileName) 
   SET @SQLText = 'Insert into Document_Content (ID, RootDirectory, FileName, FileAttribute,FileCreateDate,FileSize,FileType,FileStreamCol)      
   Select NEWID(),
   ''' + @FileLocation + ''',
   ''' + @FileName + ''',
   ''' + @FileAttribute + ''',
   ''' + @FileCreateDate + ''',
   ''' + @FileSize + ''',
   ''' + @FileType + ''',
    bulkColumn
    from Openrowset(Bulk '''+ @FileLocation + ''', Single_Blob) 
   as tb'
   EXEC Sp_executesql @SQLText
   DELETE FROM Document_List WHERE name = @FileName
   SET @I = @I + 1
END

Schritt 4:Verpacken Sie den gesamten SQL-Code in einer gespeicherten Prozedur

Erstellen Sie eine parametrisierte gespeicherte Prozedur namens sp_Insert_Files und verpacke den Code darin.

Der Code der gespeicherten Prozedur lautet wie folgt:

use FileStream_Demo
go
Create Procedure sp_Insert_Files
@FileLoc varchar(max)
as 
begin 
DECLARE @FileCount INT
DECLARE @I INT = 0
DECLARE @FileName NVARCHAR(max)
DECLARE @SQLText NVARCHAR(max)
declare @PSScript varchar(2500)
DECLARE @FileLocation NVARCHAR(max)
declare @FileAttribute varchar(50)
declare @FileCreateDate varchar(50)
declare @FileSize varchar(10)
declare @FileType varchar(20)
set @PSScript= 'powershell.exe getFileList ''' + @FileLoc +'''' 
exec xp_cmdshell @PSScript
SET @FileCount = (SELECT Count(*)
                  FROM   Document_List)
                  WHILE ( @i < @FileCount )       
BEGIN
   /* Get the File Name from Document_Name table */
   SET @FileName = (SELECT TOP 1 name
                    FROM   Document_List) 

   /* Populate File details from Document_List table*/    
   SET @FileName = (SELECT TOP 1  Name 
                        FROM   Document_List) 
   SET @FileLocation = (SELECT TOP 1  fullname 
                        FROM   Document_List where name= @FileName)
   SET @FileAttribute = (SELECT TOP 1  attributes 
                        FROM   Document_List where name= @FileName)
   SET @FileCreateDate = (SELECT TOP 1  CreationTime 
                        FROM   Document_List where name= @FileName) 
   SET @FileSize = (SELECT TOP 1  Length 
                        FROM   Document_List where name= @FileName) 
/*Determine type of file*/   
SET @FileType = (SELECT TOP 1  CASE
               WHEN ( name     LIKE '%jpg%' )
                     OR ( name LIKE '%png%' )
                     OR ( name LIKE '%jpg%' )
                     OR ( name LIKE '%bmp%' ) THEN 'Images'

				WHEN ( name  LIKE '%txt%' )THEN 'Text Files'
                     When ( name LIKE '%xls%' )THEN 'Text Files'
                     When ( name LIKE '%doc%' ) THEN 'Text Files'
               ELSE 'Other Files'
             END                AS 'File Type'
                        FROM   Document_List where name= @FileName) 
   SET @SQLText = 'Insert into Document_Content (ID, RootDirectory, FileName, FileAttribute,FileCreateDate,FileSize,FileType,FileStreamCol)      
   Select NEWID(),
   ''' + @FileLocation + ''',
   ''' + @FileName + ''',
   ''' + @FileAttribute + ''',
   ''' + @FileCreateDate + ''',
   ''' + @FileSize + ''',
   ''' + @FileType + ''',
    bulkColumn
    from Openrowset(Bulk '''+ @FileLocation + ''', Single_Blob) 
   as tb'
   EXEC Sp_executesql @SQLText
   DELETE FROM Document_List WHERE name = @FileName
   SET @I = @I + 1
END
End

Dateien mit gespeicherter Prozedur einfügen

Testen Sie nun die gespeicherte Prozedur. Ich habe ein paar Dateien zu E:\Files hinzugefügt Verzeichnis. Fügen Sie die Dateien in die SQL-Tabelle ein, indem Sie die gespeicherte Prozedur ausführen. Der Code lautet wie folgt:

use FileStream_Demo
go
exec sp_Insert_Files 'E:\Files'

Lassen Sie uns überprüfen, ob Dateien in die Tabelle kopiert wurden. Führen Sie dazu folgenden Code aus:

select 
    RootDirectory as 'File Location',
    FileName as 'File Name',
    FileAttribute as 'Attribute',
    FileCreateDate as 'Attribute',
    FileSize as 'File Size',
    FileType as 'File Type',
    FileStreamCol as 'File Content'
from Document_Content where FileType='Images'

Die Ausgabe der Abfrage sieht wie folgt aus:

Verwenden Sie für den Zugriff auf die Datei im FILESTREAM-Datenspeicher mithilfe der Win32-API den Pfadnamen () Methode von FILESTREAM. Mit dem Pfadnamen () -Methode können wir den logischen Pfad identifizieren, um die Datei im FILESTREAM-Datenspeicher eindeutig zu erkennen.

Der Code lautet wie folgt:

select 
    RootDirectory as 'File Location',
    FileName as 'File Name',
    FileAttribute as 'Attribute',
    FileCreateDate as 'Attribute',
    FileSize as 'File Size',
    FileType as 'File Type',
  FileStreamCol.PathName() AS FilePath
from Document_Content where FileName='RowDesign.png'

Die Ausgabe der Abfrage sieht wie folgt aus:

Navigieren wir zum FILESTREAM-Datencontainer (E:\Dummy-Documents), um zu überprüfen, ob Dateien eingefügt wurden. Siehe folgenden Screenshot:

Wie Sie sehen können, wurden alle Dateien in SQL-Tabellen und den FileStream-Container eingefügt.

Zusammenfassung

In diesem Artikel habe ich behandelt:

  1. Nützliche Abfrage zur Überprüfung der Voraussetzungen der FILESTREAM-Funktion.
  2. So registrieren Sie eine PowerShell-Funktion als Modul.
  3. Erklären Sie den PowerShell-Code zum Einfügen der Dateiliste in die SQL-Tabelle mithilfe des PowerShell-Skripts.
  4. Erläuterte den Code der gespeicherten Prozedur zum Einfügen mehrerer Dateien in eine SQL-Tabelle.
  5. Nützliche Abfragen zum Sammeln einer Liste von Dokumenten, die im FILESTREAM-Container gespeichert sind.

In zukünftigen Artikeln werde ich erklären, wie man eine FILESTREAM-fähige Datenbank sichert und wiederherstellt.

Bleiben Sie dran!