Биты и байты.

Биты и байты.

суббота, 3 мая 2014 г.

SQL 2012 - FILESTREAM и FILETABLE

Начиная с 2008 версии в SQL сервере появилась возможность хранить данные (фото, музыку, видео и прочие документы) типа VARBINARY(MAX) в файловой системе за пределами файла БД и управлять ими используя сам SQL сервер.
Данная опция называется FILESTREAM  и задается она следующим образом:

Обязательно в таблице должен быть задан столбец  с указателями UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL UNIQUE !!
В итоге в файловой системе в специальной папке появятся файлы с названиями типа GUID которые будут связаны с этой таблицей
Использовать эту опцию будет целесообразно если средний размер  данных в столбце VARBINARY(MAX) больше 1-2 Мб.

Подробнее можно прочитать в книге The Art of SQL Server FILESTREAM (Jacob Sebastian & Sven Aelterman).
Здесь можно найти скрипты для настроек, примеры кода для работы с FILESTREAM для ASP.NET, C#, Silverlight, SSIS, SSRS и
много полезных советов.

Однако опция FILESTREAM  имеет ряд недостатков это и односторонняя целостность (если удалить файл со стороны файловой системы, информация из таблицы связанной таблицы не будет удалена  и работа с таблицей будет невозможна) и неудобная работа со стороны файловой системы (GUID названия файлов, сложно определить какой файл соответствует данному GUID, его тип и реальное название).

В 2012 версии эта опция была дополнена надстройкой FILETABLE которая позволяет обращаться к данным по сетевому пути и работать с данными уже как с сетевой папкой читать, копировать, изменять, удалять файлы. Также добавлена двусторонняя ссылочная целостность (при удалении файлов удаляются и записи в таблицах) .

Для того чтобы воспользоваться  опцией  FILETABLE необходимо сделать всего лишь 5 простых шагов!


1.Включить на уровне службы SQL Server опцию FILESTREAM
Enable FILESTREAM for file I/O streaming access – позволяет получить доступ к файлам со стороны файловой системы
Allow remote clients to have streaming access to FILESTREAM data – позволяет получить доступ к файлам удаленно, например  по виртуальному сетевому пути.


2.Разрешить использование FILESTREAM  на уровне экземпляра SQL сервера
Disabled – отключает FILESTREAM для экземпляра
Transact-SQL access enabled – разрешает доступ только со стороны  T-SQL
Full access enabled – включает FILESTREAM для T-SQL и  файловой системы



3.Создать файловую группу и файл для хранения данных FILESTREAM
Группа создается в  отдельном разделе FILESTREAM



Включить в группу FILESTREAM файл. По сути это даже не файл, а всего лишь указатель на папку  где будут физически храниться данные FILESTREAM в системе,
в поле Path задайте путь для хранения данных FILESTREAM, например, C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA


4.Включить опцию FILESTREAM  на уровне базы данных
Здесь необходимо задать две опции Filestream Directory Name  = это подпапка которая будет создана  в общей папке  FILESTREAM для нашей БД
Выставить опцию Non-Transacted Access для  доступа к файлам вне транзакций
Off запрещает доступ к файлам вне транзакций
Read-Only – разрешает  не транзакционный доступ только для чтения файлов, создавать удалять файлы нельзя
Full – разрешает полный доступ для клиентских приложений со стороны файловой системы используя Windows API


5.Создать таблицу типа FILETABLE  
Таблица FILETABLE имеет уже жестко определенную структуру поэтому необходимо указать лишь ключевое слово AS FILETABLE
И две опции  FILETABLE_DIRECTORY —корневой каталог для всех файлов и каталогов, хранящихся в FileTable.
Если при создании не указано имя каталога, то в качестве него используется имя самой таблицы.
FILETABLE_COLLATE_FILENAME указывает имя параметров сортировки, применяемых к столбцу Name в таблице FileTable.
Если значение не указано или задано как database_default, столбец унаследует параметры сортировки текущей базы данных.


Теперь можно перейти к тестированию нашего механизма!
Для это можно зайти на сервер по сетевому пути или выбрав в SSMS опцию Explore FileTable Directory


Видно что на сервере появилась общая папка mssqlserver, которая была
указана в настройке FILESTREAM и подпапки MEDIA и FILES. Скопируем в папку FILES необходимые файлы…



Выполним SQL запрос к нашей файловой таблице, видим чтофайлы появились в таблице MEDIA_DATA
в столбце File_stream  находится наше содержимое файлов, поэтому не стоит делать запрос ко всей таблице вместе с этим полем)


Настроить FILESTREAM также можно через SQL

--включение FILESTREAM для экземпляра SQL SERVER
--0 – FILESTREAM is disabled; 1 – FILESTREAM is enabled for T-SQL access;2 – FILESTREAM is enabled for T-SQL and Win32 streaming access.
EXEC sp_configure filestream_access_level, 2;
GO
RECONFIGURE
GO
--Проверить настройки FILESTREAM для экземпляра
--     0 – FILESTREAM is disabled
--     1 – FILESTREAM is enabled for T-SQL access
--     2 – FILESTREAM is enabled for T-SQL and Win32 streaming access (local only)
--     3 – FILESTREAM is enabled for T-SQL and Win32 streaming access (local and remote)
SELECT     
 SERVERPROPERTY('FilestreamEffectiveLevel') AS EffectiveValue,  
 SERVERPROPERTY('FilestreamConfiguredLevel') AS WindowsConfiguredValue,
SERVERPROPERTY('FilestreamShareName')      AS FilestreamShare
-----------------------------------------------
---создание файловой группы FILESTREAM
ALTER DATABASE MEDIA    
ADD FILEGROUP FILESTREAM_DATA     
CONTAINS FILESTREAM;
GO 
--добавление файла в группу FILESTREAM
ALTER DATABASE MEDIA   
ADD FILE (      NAME = MEDIA_FILE, 
     FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA') TO FILEGROUP FILESTREAM_DATA;
------------------------------------------------
--включение не транзакционного доступа для таблиц FILETABLE в текущей БД
ALTER DATABASE MEDIA SET FILESTREAM (NON_TRANSACTED_ACCESS = FULL, DIRECTORY_NAME = 'MEDIA')

--проверить настройки доступа к данным FILESTREAM вне транзакций
SELECT non_transacted_access,  non_transacted_access_desc,  directory_name
FROM sys.database_filestream_options
WHERE database_id = DB_ID('MEDIA')

--создание таблицы типа FILETABLE
--http://eazybi.blogspot.ru/ 
CREATE TABLE IMAGES AS FILETABLE
WITH
(
    FILETABLE_DIRECTORY = 'Files',
    FILETABLE_COLLATE_FILENAME = database_default
)
GO


+ несколько полезных запросов

--физический путь к контейнерам FILESTREAM
SELECT      db_name() AS DatabaseName,      physical_name AS FilestreamFolder
 FROM sys.database_files WHERE type_desc = 'FILESTREAM'
--таблицы с опцией FILESTREAM
SELECT DISTINCT      OBJECT_NAME(OBJECT_ID) AS TableName FROM sys.columns c  WHERE c.is_filestream = 1

--связь между таблицами и физическими папками с названиями GUID
SELECT      t.name AS TableName,     d.name AS FileGroup,     p.filestream_guid AS FolderName
 FROM sys.tables t INNER JOIN sys.system_internals_partitions p      ON p.object_id = t.object_id
INNER JOIN sys.data_spaces d      ON d.data_space_id = t.filestream_data_space_id
WHERE p.filestream_filegroup_id > 0

--связь между таблицами и физическими папками  для секционированных таблиц
SELECT      t.Name AS TableName,     sip.partition_number AS PartitionNumber,    
sip.filestream_guid AS FilestreamFolder
FROM sys.system_internals_partitions sip
INNER JOIN sys.partitions p ON p.partition_id = sip.partition_id     
AND sip.filestream_guid IS NOT NULL
INNER JOIN sys.tables t     ON t.object_id = p.object_id  

--получить абсолютный или относительный путь к файлу, таблице, сконвертировать
--путь во внутренний path_locator
select FileTableRootPath('Images') as [FileTableRootPath],
        i.file_stream.GetFileNamespacePath(0)  as [FileRelativePath],
        i.file_stream.GetFileNamespacePath(1)  as [FileAbsoutePath],
             GetPathLocator(i.file_stream.GetFileNamespacePath(1)) [FilePathLocator],
             [path_locator]
             ,path_locator.GetLevel() AS 'level'
             ,CAST(path_locator AS VARCHAR(4000)), name
             ,is_directory
from images i

  --создание под папки
INSERT INTO Images ([name], is_directory) VALUES ('TEST_FOLDER', 1)

--------------------------------------------------------------------------------
--вставка данных в таблицу FILETABLE в указанную папку
GO
DECLARE @parent_path_locator HIERARCHYID 
 SELECT @parent_path_locator = path_locator FROM Images WHERE name = 'TEST_FOLDER'    
 AND path_locator.GetLevel() = 1     AND is_directory = 1

-- Create a new path_locator value that places the new file -- as a child of the parent directory
 DECLARE @path_locator VARCHAR(675)
 SET @path_locator = @parent_path_locator.ToString() +    
 CONVERT(VARCHAR(20), CONVERT(BIGINT, SUBSTRING(         CONVERT(BINARY(16), NEWID()), 1, 6))) + '.' +    
 CONVERT(VARCHAR(20), CONVERT(BIGINT, SUBSTRING(         CONVERT(BINARY(16), NEWID()), 7, 6))) + '.' +
 CONVERT(VARCHAR(20), CONVERT(BIGINT, SUBSTRING(         CONVERT(BINARY(16), NEWID()), 13, 4))) + '/'

--вставка нового файла в указанную папку
INSERT INTO Images ([name],  path_locator,file_stream)
 SELECT 'MyMonitor2.jpg',       @path_locator,
CAST(bulkcolumn AS VARBINARY(MAX))
FROM OPENROWSET( BULK 'C:\Shared\Images\Monitor.jpg', SINGLE_BLOB  ) as x

--сборка мусора от FILESTREAM  вручную
--http://eazybi.blogspot.ru/
EXEC sp_filestream_force_garbage_collection @dbname = N'MEDIA';



About