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:
- Versão do SQL:SQL Server 2017
- Banco de dados:FileStream_Demo banco de dados
- 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:
- 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.
- 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.
- 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:
- 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 )
- 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}' ) '@
- 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] }}
- 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:
- Consulta útil para verificar os pré-requisitos do recurso FILESTREAM.
- Como registrar uma função do PowerShell como um módulo.
- Explicar o código do PowerShell para inserir a lista de arquivos na tabela SQL usando o script do PowerShell.
- Explicou o código do procedimento armazenado para inserir vários arquivos na tabela SQL.
- 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!