Database
 sql >> Base de Dados >  >> RDS >> Database

Armazenando arquivos no banco de dados SQL usando FILESTREAM – Parte 2


Em meu artigo anterior, descrevi como configurar FILESTREAM no SQL Server, criar banco de dados e tabelas habilitados para FILESTREAM. Além disso, demonstrei como inserir e excluir dados da tabela FILESTREAM.

Neste artigo, vou demonstrar como inserir vários arquivos em uma tabela FILESTREAM usando T-SQL.

Nesta demonstração, usaremos o módulo PowerShell para preencher a lista de arquivos e armazená-la na tabela SQL.

Verificações de pré-requisitos e consultas úteis para obter configurações de FILESTREAM


Para esta demonstração, estou usando:
  1. Versão do SQL:SQL Server 2017
  2. Banco de dados:FileStream_Demo banco de dados
  3. Ferramentas:PowerShell, SQL Server Management Studio, SQL Server Data Tools.

Em meu artigo anterior, criei um banco de dados chamado FileStream_Demo . O recurso FILESTREAM está habilitado Na instância do SQL Server, e o banco de dados tem permissão de nível de acesso T-SQL e Win32.

Para revisar as configurações de nível de acesso do FILESTREAM, execute a seguinte consulta:
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

A saída da consulta é a seguinte:



Para revisar os arquivos de banco de dados e a localização do contêiner de dados FILESTREAM, execute a seguinte consulta:
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

A saída da consulta é a seguinte:


Inserir vários arquivos usando script SQL


Para inserir vários arquivos em uma tabela SQL:
  1. Crie duas tabelas SQL chamadas Document_List e Conteúdo_Documento . O Conteúdo_Documento tabela tem o FileStreamCol coluna com o tipo de dados VARBINARY(MAX) e o atributo de coluna FILESTREAM. O conteúdo dos arquivos dentro do diretório será convertido em VARBINARY(MAX) e armazenado no FileStreamCol coluna do Conteúdo_Documento tabela.
  2. Crie uma consulta SQL dinâmica que itere por meio de Document_Location tabela para obter o caminho dos arquivos e inserir arquivos no Document_Content tabelas.
  3. Agrupe todo o código T-SQL em um procedimento armazenado.

Criar tabelas SQL


Primeiramente, crie uma tabela temporária global para armazenar os detalhes dos arquivos. Para isso, execute a seguinte consulta no FileStream_Demo base de dados.
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)
)

Além disso, crie uma tabela para armazenar os arquivos na tabela. Execute a seguinte consulta para criar uma tabela física:
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

Para melhorar o desempenho da consulta de seleção, adicione um índice clusterizado no FileName e Tipo de arquivo colunas do Conteúdo_Documento tabela. Para isso, execute o seguinte código:
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

Criar módulo do PowerShell para preencher os detalhes do arquivo


Depois que as tabelas forem criadas, execute o script do PowerShell para inserir detalhes dos arquivos na Document_List tabela. O script do PowerShell é executado no procedimento armazenado T-SQL, portanto, para escrever todo o código no procedimento SQL, você precisa criar uma função do PowerShell. O caminho do diretório é um parâmetro de entrada obrigatório da função. O script obtém a lista de arquivos, reside no parâmetro de diretório usado para executar a função do PowerShell.

O código é o seguinte:
  1. Crie uma função e declare parâmetros de entrada obrigatórios. O código é o seguinte:
    function global:getFileList
    {
    param(
        [Parameter(Position=0,mandatory=$true)]
        [string[]] $FilePath
    )
  2. Construa uma string que tenha uma consulta "Inserir". Veja o seguinte código:
    [email protected]'
    INSERT INTO ##Document_List(
    	fullname,	name,	attributes,	CreationTime,	LastAccessTime,	LastWriteTime,
        Length
    ) 
    VALUES (
    	'{0}',
    	'{1}',
    	'{2}',
    	'{3}',
    	'{4}',
    	'{5}',
        '{6}'
    )
    '@
  3. Obter a lista de arquivos usando o comando Get-ChildItem -Recurse formate a saída do comando. O código é o seguinte:
    Get-ChildItem -Recurse $Directorypath | 
    select @{Label="FullName";Expression={split-path($_.FullName)}},	name,	attributes,	CreationTime,	LastAccessTime,	LastWriteTime,@{Label="Length";Expression={$_.Length / 1MB -as [int] }}
  4. Usando o loop For-Each, armazene a saída no Document_content tabela. Para executar a consulta no FileStream_Demo banco de dados, o script usa Invoke-Sqlcmd . O código é o seguinte:
    ForEach-Object {
    		$SQL = $sqltmplt -f $_.FullName, $_.name, $_.attributes, $_.CreationTime, $_.LastAccessTime, $_.LastWriteTime,$_.Length	
    		Invoke-sqlcmd -Query $SQL -ServerInstance TTI412-VM\SQL2017 -database FileStream_Demo
    	}

Todo o código da função do PowerShell terá a seguinte aparência:
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."
}

Para usar a função PowerShell dentro do procedimento SQL Stored, precisamos registrar o script acima como Módulo PowerShell. Para isso, crie um diretório chamado getFileList em C:\Windows\System32\WindowsPowerShell\v1.0\Modules . Para registrar qualquer script do PowerShell como um módulo, os nomes do script e do diretório devem ser os mesmos. Portanto, salve o script acima comogetFileList.psm1 na getFileList diretório.

Agora, quando executamos o script do PowerShell do T-SQL, precisamos importar o getFileList módulo. Para isso, adicione o seguinte código no perfil do PowerShell. O perfil do PowerShell será criado em C:\Windows\System32\WindowsPowerShell\v1.0 localização.
import-module getFileList

Se o perfil não existir, execute o comando a seguir para criar um perfil.
New-Item -Type File -Path $PROFILE.AllUsersAllHosts -Force

Criar um procedimento armazenado para importar arquivos


Assim que armazenarmos a lista de arquivos e as informações na Tabela SQL, inseriremos os arquivos no Document_Content tabela.

Para fazer essa tarefa com eficiência, crie um procedimento armazenado parametrizado chamado sp_Insert_Documents . Ele usará o FileLocation parâmetro que é do tipo de dados varchar. O procedimento preenche a lista de arquivos do local fornecido no parâmetro e insere todos os arquivos no Document_Content tabela.

Etapa 1:altere o parâmetro de configuração.

Para executar o comando do PowerShell usando T-SQL, habilite o xp_cmdshell opção de configuração. É uma opção de configuração avançada; portanto, antes de ativar o xp_cmdshell , ative a opção Mostrar avançado opção de configuração. Para isso, execute os seguintes comandos T-SQL em sequência.
use master
go
exec sp_configure 'show advanced option',1
reconfigure with override

Exec sp_configure 'xp_cmdshell',1
Reconfigure with override

Etapa 2:use o script do PowerShell para preencher a lista de arquivos no código T-SQL

Para executar um script do PowerShell usando T-SQL, use o xp_cmdshell procedimento. Ele executa o comando do PowerShell, que preenche uma lista de arquivos e seus detalhes na Document_List table.
O código é o seguinte:
declare @PSScript varchar(2500)
set @PSScript= 'powershell.exe getFileList ''' + @FileLoc +'''' 
exec xp_cmdshell @PSScript

Etapa 3:crie uma consulta SQL dinâmica para obter o local do arquivo

Crie uma consulta SQL dinâmica que itere por meio da Document_List table, carrega o conteúdo do arquivo, localizado no caminho fornecido em FullName coluna, converte-a na coluna VARBINAR(MAX) e a insere no Document_Content tabela. Junto com o arquivo, o script insere o nome do arquivo, atributo do arquivo, tamanho do arquivo e tipo de arquivo no Document_Content tabela. O script usa o case expressão para determinar o tipo de arquivo.

O código é o seguinte:
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

Etapa 4:encerrar todo o código SQL em um procedimento armazenado

Crie um procedimento armazenado parametrizado chamado sp_Insert_Files e enrole o código nele.

O código do Stored Procedure é o seguinte:
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

Inserir arquivos usando o procedimento armazenado


Agora teste o procedimento armazenado. Adicionei alguns arquivos ao E:\Files diretório. Insira os arquivos na tabela SQL executando o procedimento armazenado. O código é o seguinte:
use FileStream_Demo
go
exec sp_Insert_Files 'E:\Files'

Vamos verificar se os arquivos foram copiados para a tabela. Para isso, execute o seguinte código:
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'

A saída da consulta é a seguinte:



Para acessar o arquivo no armazenamento de dados FILESTREAM usando a API Win32, use o Pathname () método de FILESTREAM. Com o nome de caminho () método, podemos identificar o caminho lógico para detectar o arquivo no armazenamento de dados FILESTREAM exclusivamente.

O código é o seguinte:
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'

A saída da consulta é a seguinte:



Vamos navegar até o contêiner de dados FILESTREAM (E:\Dummy-Documents) para verificar se os arquivos foram inseridos. Veja a captura de tela a seguir:



Como você pode ver, todos os arquivos foram inseridos nas tabelas SQL e no container FileStream.

Resumo


Neste artigo, abordei:
  1. Consulta útil para verificar os pré-requisitos do recurso FILESTREAM.
  2. Como registrar uma função do PowerShell como um módulo.
  3. Explicar o código do PowerShell para inserir a lista de arquivos na tabela SQL usando o script do PowerShell.
  4. Explicou o código do procedimento armazenado para inserir vários arquivos na tabela SQL.
  5. Consultas úteis para reunir uma lista de documentos, armazenados no contêiner FILESTREAM.

Em artigos futuros, explicarei como fazer backup e restaurar o banco de dados habilitado para FILESTREAM.

Fique atento!