sql >> Base de Datos >  >> RDS >> Database

Almacenamiento de archivos en la base de datos SQL usando FILESTREAM - Parte 2

En mi artículo anterior, describí cómo configurar FILESTREAM en SQL Server, crear bases de datos y tablas habilitadas para FILESTREAM. Además, demostré cómo insertar y eliminar datos de la tabla FILESTREAM.

En este artículo, demostraré cómo insertar varios archivos en una tabla FILESTREAM usando T-SQL.

En esta demostración, usaremos el módulo PowerShell para completar la lista de archivos y almacenarla en la tabla SQL.

Comprobaciones de requisitos previos y consultas útiles para obtener configuraciones de FILESTREAM

Para esta demostración, estoy usando:

  1. Versión SQL:Servidor SQL 2017
  2. Base de datos:FileStream_Demo base de datos
  3. Herramientas:PowerShell, SQL Server Management Studio, herramientas de datos de SQL Server.

En mi artículo anterior, creé una base de datos llamada FileStream_Demo . La función FILESTREAM está habilitada en la instancia de SQL Server y la base de datos tiene permiso de nivel de acceso T-SQL y Win32.

Para revisar la configuración del nivel de acceso de FILESTREAM, ejecute la siguiente 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

El resultado de la consulta es el siguiente:

Para revisar los archivos de la base de datos y la ubicación del contenedor de datos FILESTREAM, ejecute la siguiente 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

El resultado de la consulta es el siguiente:

Insertar varios archivos usando SQL Script

Para insertar varios archivos en una tabla SQL:

  1. Cree dos tablas SQL llamadas, Document_List y Contenido_del_documento . El Contenido_del_Documento la tabla tiene el FileStreamCol columna con el tipo de datos VARBINARY(MAX) y el atributo de columna FILESTREAM. El contenido de los archivos dentro del directorio se convertirá en VARBINARY (MAX) y se almacenará en el FileStreamCol columna del Document_Content mesa.
  2. Cree una consulta SQL dinámica que repita a través de Document_Location tabla para obtener la ruta de los archivos e insertar archivos en el Document_Content mesas.
  3. Envuelva todo el código T-SQL en un procedimiento almacenado.

Crear tablas SQL

En primer lugar, cree una tabla temporal global para almacenar los detalles de los archivos. Para ello, ejecute la siguiente consulta en el FileStream_Demo base de datos.

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

Además, cree una tabla para almacenar los archivos en la tabla. Ejecute la siguiente consulta para crear una tabla 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 mejorar el rendimiento de la consulta de selección, agregue un índice agrupado en FileName y Tipo de archivo columnas del Document_Content mesa. Para ello ejecuta el siguiente 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

Crear un módulo de PowerShell para completar los detalles del archivo

Una vez creadas las tablas, ejecute el script de PowerShell para insertar detalles de los archivos en la Document_List mesa. El script de PowerShell se ejecuta dentro del procedimiento almacenado de T-SQL, por lo tanto, para escribir el código completo en el procedimiento de SQL, debe crear una función de PowerShell. La ruta del directorio es un parámetro de entrada obligatorio de la función. El script obtiene la lista de archivos, reside en el parámetro de directorio utilizado para ejecutar la función de PowerShell.

El código es el siguiente:

  1. Cree una función y declare parámetros de entrada obligatorios. El código es el siguiente:
    function global:getFileList
    {
    param(
        [Parameter(Position=0,mandatory=$true)]
        [string[]] $FilePath
    )
  2. Construya una cadena que tenga una consulta "Insertar". Consulte el siguiente código:
    [email protected]'
    INSERT INTO ##Document_List(
    	fullname,	name,	attributes,	CreationTime,	LastAccessTime,	LastWriteTime,
        Length
    ) 
    VALUES (
    	'{0}',
    	'{1}',
    	'{2}',
    	'{3}',
    	'{4}',
    	'{5}',
        '{6}'
    )
    '@
  3. Obtenga la lista de archivos usando el comando Get-ChildItem -Recurse formateando la salida del comando. El código es el siguiente:
    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 el ciclo For-Each, almacene la salida en el Document_content mesa. Para ejecutar la consulta en FileStream_Demo base de datos, el script usa Invoke-Sqlcmd . El código es el siguiente:
    ForEach-Object {
    		$SQL = $sqltmplt -f $_.FullName, $_.name, $_.attributes, $_.CreationTime, $_.LastAccessTime, $_.LastWriteTime,$_.Length	
    		Invoke-sqlcmd -Query $SQL -ServerInstance TTI412-VM\SQL2017 -database FileStream_Demo
    	}

El código completo de la función de PowerShell tendrá el siguiente aspecto:

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 la función de PowerShell dentro del procedimiento almacenado de SQL, debemos registrar el script anterior como módulo de PowerShell. Para esto, cree un directorio llamado getFileList en C:\Windows\System32\WindowsPowerShell\v1.0\Modules . Para registrar cualquier secuencia de comandos de PowerShell como módulo, los nombres de la secuencia de comandos y del directorio deben ser los mismos. Por lo tanto, guarde el script anterior como getFileList.psm1 en getFileList directorio.

Ahora, cuando ejecutamos el script de PowerShell desde T-SQL, necesitamos importar el getFileList módulo. Para ello, agregue el siguiente código en el perfil de PowerShell. El perfil de PowerShell se creará en C:\Windows\System32\WindowsPowerShell\v1.0 ubicación.

import-module getFileList

Si el perfil no existe, ejecute el siguiente comando para crear un perfil.

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

Cree un procedimiento almacenado para importar archivos

Una vez que almacenemos la lista de archivos y la información en la tabla SQL, insertaremos los archivos en el Document_Content mesa.

Para realizar esta tarea de manera efectiva, cree un procedimiento almacenado parametrizado llamado sp_Insert_Documents . Utilizará la FileLocation parámetro que es del tipo de datos varchar. El procedimiento completa la lista de archivos desde la ubicación dada en el parámetro e inserta todos los archivos en el Document_Content mesa.

Paso 1:Cambie el parámetro de configuración.

Para ejecutar el comando de PowerShell con T-SQL, habilite xp_cmdshell opción de configuración Es una opción de configuración avanzada; por lo tanto, antes de habilitar xp_cmdshell , habilite la opción Mostrar avanzada opción de configuración Para esto, ejecute los siguientes comandos T-SQL en secuencia.

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

Exec sp_configure 'xp_cmdshell',1
Reconfigure with override

Paso 2:use el script de PowerShell para completar la lista de archivos dentro del código T-SQL

Para ejecutar un script de PowerShell usando T-SQL, use xp_cmdshell procedimiento. Ejecuta el comando PowerShell, que completa una lista de archivos y sus detalles en Document_List tabla.
El código es el siguiente:

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

Paso 3:Cree una consulta SQL dinámica para obtener la ubicación del archivo

Cree una consulta SQL dinámica que repita a través de Document_List table, carga el contenido del archivo, ubicado en la ruta dada en el FullName columna, la convierte en la columna VARBINAR(MAX) y la inserta en el Document_Content mesa. Junto con Archivo, la secuencia de comandos inserta Nombre de archivo, Atributo de archivo, Tamaño de archivo y Tipo de archivo en el Contenido_del_documento. mesa. El script usa el caso expresión para determinar el tipo de archivo.

El código es el siguiente:

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

Paso 4:Envuelva todo el código SQL en un procedimiento almacenado

Cree un procedimiento almacenado parametrizado llamado sp_Insert_Files y envuelve el código en él.

El código del procedimiento almacenado es el siguiente:

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

Insertar archivos utilizando el procedimiento almacenado

Ahora pruebe el procedimiento almacenado. Agregué algunos archivos a E:\Files directorio. Inserte los archivos en la tabla SQL ejecutando el procedimiento almacenado. El código es el siguiente:

use FileStream_Demo
go
exec sp_Insert_Files 'E:\Files'

Verifiquemos que los archivos se hayan copiado en la tabla. Para ello ejecuta el siguiente 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'

El resultado de la consulta es el siguiente:

Para acceder al archivo en el almacén de datos de FILESTREAM usando la API de Win32, use el Pathname () método de FILESTREAM. Con el nombre de la ruta () método, podemos identificar la ruta lógica para detectar el archivo en el almacén de datos de FILESTREAM de forma única.

El código es el siguiente:

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'

El resultado de la consulta es el siguiente:

Naveguemos al contenedor de datos FILESTREAM (E:\Dummy-Documents) para verificar que se hayan insertado los archivos. Vea la siguiente captura de pantalla:

Como puede ver, todos los archivos se han insertado en tablas SQL y el contenedor FileStream.

Resumen

En este artículo, he cubierto:

  1. Consulta útil para verificar los requisitos previos de la función FILESTREAM.
  2. Cómo registrar una función de PowerShell como módulo.
  3. Explicar el código de PowerShell para insertar la lista de archivos en la tabla SQL mediante el script de PowerShell.
  4. Explicó el código del procedimiento almacenado para insertar varios archivos en la tabla SQL.
  5. Consultas útiles para recopilar una lista de documentos almacenados en el contenedor FILESTREAM.

En artículos futuros, explicaré cómo hacer una copia de seguridad y restaurar una base de datos habilitada para FILESTREAM.

¡Estén atentos!