MS SQL Server. Загрузка файлов в БД

Постановка задачи

Необходимо загрузить все файлы, определенного формата из заданной директории в таблицу базы. Рекурсивный обход директории делать не нужно. Вместе с файлом необходимо сохранить некоторые метаданные файла из файловой системы.

Загрузка файла в БД

Для того, чтобы загрузить файл в БД необходимо воспользоваться следующей командой
OPENROWSET(BULK 'data_file' , { SINGLE_BLOB | SINGLE_CLOB | SINGLE_NCLOB } } )
В завиcимости от параметра, данная конструкция возвращает следующие типы данных:
  • varbinary(max) - SINGLE_BLOB (рекомендуется для чтения XML-файлов)
  • varchar(max) - SINGLE_CLOB (используется для считывания ASCII файлов. Использует кодировку текущей базы данных)
  • nvarchar(max) - SINGLE_NCLOB (используется для считывания данных в кодировке юникод)
Обратиться к считываемой информации можно используя конструкцию bulkcolumn. Все содержимое файла будет содержаться в одной ячейке.
-- Загрузка изображения
select bulkcolumn from openrowset(bulk '\\share\icon.png', single_blob) as img

Загрузим теперь текстовый файл. Я скачал первый том произведения "Война и мир".
select bulkcolumn from openrowset(bulk '\\share\Vojna i mir. Tom 1.txt', single_clob) as img
select bulkcolumn from openrowset(bulk '\\share\Vojna i mir. Tom 1.txt', single_nclob) as img
Результат первого запроса некорректно разобрал русские символы, а второй вообще свалился с ошибкой ("SINGLE_NCLOB requires a UNICODE (widechar) input file. The file specified is not Unicode.").


Откроем блокнот и поменяем кодировку файла. Один файл сохраним в кодировки ANSI, второй сохраним в кодировке "Юникод Big Endian" -- UTF16-BE, именно такую кодировку поддерживает MS SQL Server. После загрузки файлов, получим корректные результаты.

Получение информации о файлах  в директории

Получить всё содержимое файловой директории можно используя недокументированную команду master.sys.xp_dirtree, которая принимает следующие параметры
  • directory - имя исследуемой директории;
  • depth - определяет сколько уровней вложенных директорий необходимо отобразить. Параметр по умолчанию 0 отображает все директории;
  • file - определяет отображать ли файлы в каждой директории. По умолчанию 0 не отображает файлы.
Следующая команда возвращает содержимое директории \\Share как файлы, так и вложенные директории. Значение 1 в колонке depth значит, что файл находится в указанной директории
exec master.sys.xp_dirtree '\\Share\', 0, 1;


Ниже приводится код загрузки содержимого директории, отфильтрованного по расширению png во временную таблицу userLogo

declare @folder nvarchar(255) = '\\Share\Files\'
declare @sql nvarchar(max);
declare @fileName nvarchar(255);
declare @filePath nvarchar(500);
declare @files table (
 fileName nvarchar(255),
 depth tinyint,
 isFile bit
);
if object_id('tempdb..#usersLogo') is not null
 drop table #usersLogo;

create table #usersLogo (
 id int identity,
 fileName nvarchar(255),
 fileContent varbinary(max)
)

set @sql = 'exec master.sys.xp_dirtree ''' + @folder + ''' , 0, 1;';
insert into @files
exec sp_executesql @sql;
select * from @files

declare fileCursor cursor 
for select fileName from @files 
 where isFile = 1 and depth = 1 and fileName like '%.png' 

open fileCursor
fetch next from fileCursor into @fileName

while (@@fetch_status = 0)
begin 
 set @sql = '
 insert into #usersLogo(fileName, fileContent) 
 select 
  $fileName, 
  (select bulkcolumn from openrowset(bulk $filepath, single_blob) t )';
 set @filePath = @folder + @fileName
 set @sql = replace(@sql, '$fileName', '''' + @fileName  + '''' );
 set @sql = replace(@sql, '$filePath', '''' + @filePath + '''' );
    print @sql
 exec sp_executesql @sql;

 fetch next from fileCursor into @fileName
end

select * from #usersLogo
Результаты выполнения приведенного выше скрипта

Права доступа

Для операции загрузки файла в базу данных, необходимо обладать правами на уровне сервера bulkadmin, а также иметь доступ на чтение к директории из которой производится чтение данных.

Ресурсы

  1. MSDN. OPENROWSET
  2. Why is SQL Server Big Endian?



MS Power BI. Справочник

Ресурсы по Power BI

C чего начать

Примеры отличных визуализаций

Custom visuals

DAX

Power BI Embeded

Блоги и стороние ресурсы


Интересные статьи

R, Shiny

Всё течёт, всё меняется.
Для реализации небольших BI проектов не нужно стрелять из пушки по воробьям и можно обойтись бесплатными решениями.

Одно из таких решений фрэймворк Shiny, использующий всю мощь языка R

“Shiny is an open source R package that provides an elegant and powerful web framework for building web applications using R.
Shiny helps you turn your analyses into interactive web applications without requiring HTML, CSS, or JavaScript knowledge.”

Отличительная особенность данного решения – декларативный подход к описанию дашборда и возможность использования огромное число библиотек R, что даёт очень большую гибкость в создании BI решений. Для R есть просто чумовые библиотеки по визуализации данных.

Он не заменит BI платформы, но у данного решения определенно есть своя ниша. Особенно в продвинутой аналитике данных

Примеры:

Очень крутой пример, исходники в открытом доступе https://mbienz.shinyapps.io/tourism_dashboard_prod/
Для того, чтобы понять как работает, можно посмотреть простенький пример с кодом реализации http://shiny.rstudio.com/gallery/telephones-by-region.html
Галерея с исходниками http://shiny.rstudio.com/gallery/
Хорошая библиотека с графиками http://jkunst.com/highcharter/index.html

Анализ данных выборов (исходники, хабр)


Туториалы

Исходники

Редакции

Можно публиковать созданные приложения в облаке http://www.shinyapps.io/
Есть on premise редакция https://www.rstudio.com/products/shiny/shiny-server/

SSIS 2016 new features

В отличии от SSIS 2014, где не было вообще никаких улучшений,  SSIS 2016 порадовал внушительным набором новых фич. Особо углубляться в каждую из фич я не буду, в конце представлен материал для подробного изучения.

1. Поддержка AlwaysOn msdn

2. При обработке ошибок в потоке данных, к колонкам ErrorCode (определяет код ошибки) и ErrorColumn (определяет идентификатор колонки в пакете - lineage Id), добавились колонки ErrorDescription и ErrorColumnName. Из окна Data Viewer в режиме отладки эти колонки отображаются, правда разработчики почему-то не включили их в стандартный выход потока ошибок. Получить эти колонки можно через Script Component

В предыдущих версиях, чтобы получить эту информацию приходилось изрядно извращаться, чтобы получить по lineageId колонки её наименование. Приходилось парсить xml-файл с пакетом и получать маппинг колонок!!! Пример такого изврата.

3. Инкрементальное обновление пакетов, которое позволяет деплоить пакеты(один или несколько) отдельно от проекта. При этом пакет можно опубликовать как в текущий проект, так и отдельно (в новый проект).

4. В дефолтной поставке появились следующие компоненты:
- Balance Data Distributor (давно пора! равномерно делит входной поток на N потоков)
- Data Feed Publishsing (позволяет обращаться к результатам работы пакета через представление, предварительно настроив подключение линкед сервера к SSIS, msdn)
- Коннекторы для платформы Hadoop (для работы с HDFS, для запуска тасков в Pig и Hive, msdn )
- Коннекторы к сервисам Azure (необходимо установить Azure Feature Pack, msdn )
- Поддержка Excel 2016 и OData v4

5. Возможность создать свой собственный уровень логирования событий, очень тонко настроив необходимые статистики и события для логирования. При запуске пакета есть возможность выбрать тип логирования. Добавлен уровень логирования RuntimeLineage.

6. В дизайнере пакетов появился новый функционал, немного упрощающий разработку сложных проектов - Package parts - набор, компонентов потока управления, созданный для повторного использования в других пакетах, или по-другому, шаблон. При изменении шаблона он автоматически изменяется и в родительских пакетах, в которых он используется. При этом в родительском пакете нет возможности изменить package part. Сами шаблоны не публикуются на сервер, так как являются частью пакетов, в которых они используются.
7. Очень полезная фича - опция AutoAdjustBufferSize автоматически вычисляющая размер буфера для потока данных
8. Поддержка SSAS Tabular в компонентах процессинга
9. Добавили роли ssis_logreader - просмотр отчетов по запуску пакетов, ssis_monitor - внутренняя для AlwaysOn

Полезные ссылки:
1. TechNet Virtual Lab: Exploring What's New in SQL Server 2016 Integration Services
2. MSDN. What's New in Integration Services
3. Reuse Control Flow across Packages by Using Control Flow Package Parts
4. Data Flow Performance Features
5. Operationalize your machine learning project using SQL Server 2016 SSIS and R Services
6. What's New in SQL Server Integration Services 2016 - Part 1What's New in Integration Services 2016 - Part 2
7. Improving data flow performance with SSIS AutoAdjustBufferSize property
8. Презентация 

Выполнение пакетов в SSIS при помощи T-SQL

Начиная с SQL Server 2012 структура проектов SSIS претерпела существенные изменения, путем перехода от модели управления единичными пакетами (package deployment model) к модели управления проектами (project deployment model). Последний подход упрощает и унифицирует разработку, позволяя группировать логически связанные пакеты в проекты и управлять ими как общей единицей. При развертывании проекта на сервер, все его компоненты размещаются в общей базе данных SSISDB.

Сегодняшняя статья будет посвящена процессу выполнения пакетов, развернутых в SSIS при помощи project deployment model.

В SSIS, начиная с 2012 версии обновился компонент Execute package task. Для запуска пакета, находящихся в одном проекте можно воспользоваться данным компонентом с опцией Project Reference. В выпадающем списке PackageNameFromProjectReference отобразятся все пакеты текущего проекта. Перейдя на вкладку Parameter Bindings можно задать значения переменных запускаемого пакета. Особых комментариев по работе данного компонента не требуется, за исключением одного. Данные компонент не позволяет выполнить пакет из другого проекта.

Добавление пользователей в группы Sharepoint

$siteUrl = "http://siteUrl"
$siteCollUrl = "http://siteUrl/Reports"
$web = Get-SpWeb -site $siteUrl | Where-Object {$_.Url -eq $siteCollUrl}

$users = Invoke-SQL -sqlCommand  "exec dbo.GetUserRightsForSP"
foreach ($user in $users.Rows)
{
    
    $userSp = Get-SpUser -Identity $user["SpUser"] -Web $web
    Set-SPUser -Identity $userSp -Web $web -Group $user["Group"]
}

Добавление пользователей в локальные группы при помощи PowerShell

В данной статье будет рассмотрен способ добавления пользователей в определенные для них группы, информация по которым будет браться из базы данных.
Следующий командлет PowerShell добавляет пользователя AD в заданную локальную группу пользователей:

function Add-LocalUser{
     Param(
        $computer   = $env:computername,
        $group      = "GroupName",
        $userdomain = $env:userdomain,
        $username   = $env:username
    )
        ([ADSI]"WinNT://$computer/$group,group").psbase.Invoke("Add",([ADSI]"WinNT://$userdomain/$username").path)
}
Следующий командлет выполняет SQL-запрос на заданной базе данных MS SQL Server и возвращает табличный результат.

function Invoke-SQL {
    param(
        [string] $dataSource = "ServerName",
        [string] $database   = "DbName",
        [string] $sqlCommand = "select [User], [Domain], [Group] from dbo.vUserRights"
      )

    $connectionString = "Data Source=$dataSource; " +
            "Integrated Security=SSPI; " +
            "Initial Catalog=$database"

    $connection = new-object system.data.SqlClient.SQLConnection($connectionString)
    $command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
    $connection.Open()

    $adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
    $dataset = New-Object System.Data.DataSet
    $adapter.Fill($dataSet) | Out-Null

    $connection.Close()
    $dataSet.Tables
}
Получаем из базы данных таблицу принадлежности пользователей к группам и добавляем каждого пользователя в определенную для него локальную группу на данной машине.

$users = Invoke-SQL -sqlCommand  "select [User], [Domain], [Group] from dbo.vUserRights";
foreach ($user in $users.Rows)
{
    Add-LocalUser -group $user["Group"] -username $user["User"] -userdomain $user["Domain"] 
}

Дополнительные таблицы. Календарь

Измерение даты и времени -- ключевая и обязательная сущность любого хранилища данных или аналитической системы. В данной статье будет приведен пример создания типовой таблицы календаря с гранулярностью до уровня дня. Для создания данной таблицы  нам понадобится таблица dbo.Numbers из предыдущей статьи. Полный код и результаты работы можно посмотреть здесь.

Создадим таблицу dbo.DimDate. В качестве кластерного ключа таблицы будем использовать суррогатный целочисленный ключ, представленный в формате YYYYMMDD. Ниже представлен скрипт создания таблицы календаря, а также скрипт создания дополнительной таблицы dbo.Numbers.

Запросы к Active Directory

SQL Server позволяет производить выборки данных из множества источников данных при помощи многочисленных доступных драйверов. В том числе MS SQL Server позволяет выполнять запросы к контроллеру домена. В моем случае к Active Directory. Это очень полезная функция, которая может быть использована для множества задач. Я в основном использую ее для динамической настройки прав доступа к отчетным системам. По хорошему, настройка прав должна производиться с использованием групп пользователей домена. В таком случае, при добавлении нового пользователя в группу домена, для него автоматически будут применены соответствующие права. Но не все системы позволяют настроить роли с учетом групп пользователей контроллера домена. Например, я столкнулся с такой проблемой в MS SQL Server SSAS Multidimensional. Чтобы решить данную проблему, необходимо получить список пользователей необходимой группы контроллера домена и автоматически добавить каждого пользователя в нужную нам роль.

Настройка Linked Server

Параметр Значение
Provider
OLE DB provider for Microsoft Directory Services 
Product Name  ADSI
Data Source afsdatasource
Security Be made using the login's current security context

Настройка связного сервера к MS AD не представляет из себя ничего трудного. Достаточно указать следующие свойства:

Запросы к AD

Запросы к AD могу писаться в двух форматах:
  • SQL
  • LDAP
SQL-формат:
SELECT * FROM OPENQUERY(AD,
'SELECT name, ADsPath, title  
FROM ''LDAP://DC=domain_name'' 
WHERE objectCategory = ''User''' 
) 
LDAP-формат:
SELECT * FROM OPENQUERY(AD,'<LDAP://DC=domain_name>;(&(objectcategory=user));name, ADsPath, title ')
Но существует ограничение на получение данных из AD -- 900 строк с данными. Данное ограничение можно обойти настроив соответствующим образом контроллер доменов.
Далее я приведу примеры запросов, которые я использую:

Дополнительные таблицы. Numbers

Для многих задач полезно иметь под рукой дополнительные таблицы. Одной из таких таблиц является таблица с последовательным набором чисел Numbers. Ее можно использовать как для создания тестовых таблиц с данными, так и для прикладных задач. Особенно полезны для работы со строками, для их итеративной обработки.

Существует множество способов создания такой таблицы:
  • master..spt_values
  • row_number() с любой таблицей, например sys.all_objects
  • CTE
  • Рекурсивный CTE
  • Цикл

Загружаем OpenStreetMap в MS SQL Server

На днях подкинули задачку по геокодированию объектов, а именно:
  • По строке адреса определить соответствующие ей географические координаты. Строка с адресом не нормализованна и потенциально может содержать ошибки.
  • Обратная задача. По геокординатам точки предоставить список всех имеющихся в базе объектов в эпсилон окрестности от этой точки.
Сервис по геокодированию должен работать только для российских адресов. Для решения задачи геокодирования можно воспользоваться известными географическими средствами, благо такие возможности имеются. К сожалению у большинства сервисов имеются ограничение на число запросов в сутки:
Каждый сервис реализует собственные алгоритмы и предоставляет определенный уровень достоверности результатов. Однако, предполагается, что объектов, геокодирование которых необходимо производить, будет больше, чем количество допустимых запросов и обработку необходимо производить в пакетном режиме.

В качестве возможного варианта решения, я решил попробовать реализовать собственный поиск, используя для этого открытую базу данных Open Street Map (OSM). В качестве инструмента для первоначальной реализации я выбрал MS SQL Server 2012. В последнем поддерживаются пространственные типы данных, а также имеется возможность создания пространственного индекса.

SQL Server. Storage Engine. Страницы данных

В данной статье я хочу описать внутренние механизмы движка MS SQL Server по хранению данных и механику данного процесса. Это очень полезно для изучения особенностей работы SQL Server и для собственной проверки гипотез и утверждений о том, как работает SQL Server. Как говорится, доверяй, но проверяй.

Структура таблицы

Рассмотрим структуру таблицы в SQL Server.
Каждая таблица или индекс хранит свою информацию в партициях (partition, до 15.000 для sql server 2012). Каждая партиция может содержать один объект  B-дерева (b-tree, если быть точным в виде B+ дерева) или кучу(heap), отсюда, данные структуры называют общим именем hobt (heap or b-tree). Между партицией и hobt-структурой существует отношение один к одному. В зависимости от типа данных колонок таблицы или индекса, hobt может состоять из трех наборов страниц (allocation unit), каждая из которых образует цепочку страниц (iam chain):
  • IN_ROW_DATA (практически все типы данных, кроме описанных ниже)
  • ROW_OVERFLOW_DATA (типы переменной длины, вместе с которыми запись не помещается на страницу)
  • LOB_DATA ( nvarchar(max), filestream, xml, varbinary)


Шпаргалка по типам данных

Обзор типов и их размерностей

DATATYPE
MIN
MAX
STORAGE
NOTES
Bit
0
1
1 to 8 bit columns in the same table requires a total of 1 byte, 9 to 16 bits = 2 bytes, etc...
Decimal
-10^38+1
10^38–1
Precision 1-9 = 5 bytes, precision 10-19 = 9 bytes, precision 20-28 = 13 bytes, precision 29-38 = 17 bytes
The Decimal and the Numeric data type is exactly the same. Precision is the total number of digits. Scale is the number of decimals. For both the minimum is 1 and the maximum is 38.
Numeric
same as Decimal
same as Decimal
same as Decimal
Money
От -922 337 203 685 477,5808 до 
922 337 203 685 477,5807
8 bytes
Smallmoney
От -214 748,3648 
214 748,3647
4 bytes
Float(n)
-1.79E + 308
1.79E + 308
4 bytes when precision is less than 25
8 bytes when precision is 25 through 53
Precision is specified from 1 to 53.
Real
-3.40E + 38
3.40E + 38
4 bytes
Precision is fixed to 7 digits.
Datetime
1753-01-01 00:00:00.000
9999-12-31 23:59:59.997
8 bytes
If you are running SQL Server 2008 or later and need milliseconds precision, use datetime2(3) instead to save 1 byte.
Smalldatetime
1900-01-01 00:00
2079-06-06 23:59
Date
0001-01-01
9999-12-31
3 bytes
Time(n)
00:00:00.0000000
23:59:59.9999999
Presicion 0-2 = 3 bytes
Presicion 3-4 = 4 bytes 
Presicion 5-7 = 5 bytes
Specifying the precision is possible. TIME(3) will have milliseconds precision. TIME(7) is the highest and the default precision. Casting values to a lower precision will round the value.
Datetime2(n)
0001-01-01 00:00:00.0000000
9999-12-31 23:59:59.9999999
Presicion 1-2 = 6 bytes precision 3-4 = 7 bytes precision 5-7 = 8 bytes
Combines the date datatype and the time datatype into one. The precision logic is the same as for the time datatype.
Datetimeoffset(n)
0001-01-01 00:00:00.0000000 -14:00
9999-12-31 23:59:59.9999999 +14:00
Presicion 1-2 = 8 bytes precision 3-4 = 9 bytes precision 5-7 = 10 bytes
Is a datetime2 datatype with the UTC offset appended

  

Собственные типы данных -- домены 
Allocation_Units

Datetime vs Datetime2

http://stackoverflow.com/questions/1334143/sql-server-datetime2-vs-datetime

Decimal vs Numeric

Money vs Decimal vs Float


Nvarchar(max) cautions
http://stackoverflow.com/questions/12639948/sql-nvarchar-and-varchar-limits

Nvarchar(max) vs Nvarchar(255)
http://stackoverflow.com/questions/148398/are-there-any-disadvantages-to-always-using-nvarcharmax
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/4d9c6504-496e-45ba-a7a3-ed5bed731fcc/varcharmax-vs-varchar255?forum=sqlgetstarted


Implicit convertions

Работа с данными

http://msdn.microsoft.com/en-us/library/ff848728.aspx



Функции по просмотру метаданных типов и колонок

SELECT DATALENGTH(@variable)

SELECT

    SQL_VARIANT_PROPERTY(@variable, 'BaseType'),

    SQL_VARIANT_PROPERTY(@variable, 'TotalBytes'),

    SQL_VARIANT_PROPERTY(@variable, 'MaxLength')
;with cte
(
select * from t
)
select * from cte