For XML Extension
Преобразование данных из реляционной модели в XML
Зачастую необходимо делать выгрузку из реляционной БД в формате XML для передачи их некоторому приложению или сервису.
В SQL Server это можно выполнить с помощью инструкции FOR XML, добавляемой в конец оператора SELECT.
Пример логической модели данных:
For XML Explicit
Первая попытка Microsoft предоставить инструмент генерации XML заданной структуры из реляционных данных.
Имеет ряд недостатков (прежде всего, сложность описания структуры генерируемого XML, нечитабельность и “тормознутость” кода) и сейчас практически не используется.
—
Режим EXPLICIT преобразует набор строк, получаемый в результате выполнения запроса, в XML-документ. Для того чтобы режим EXPLICIT создал XML-документ, набор строк должен иметь определенный формат.
Запрос должен создавать следующие два столбца метаданных:
- первый столбец должен предоставлять номер тега текущего элемента (целочисленного типа), с именем столбца Tag. В запросе должен быть указан уникальный номер тега для каждого элемента, который будет создан из набора строк;
- второй столбец должен задавать номер тега для родительского элемента, и этот столбец должен иметь имя Parent. Таким образом, столбцы Tag и Parent предоставляют сведения об иерархии.
Значение 0 или NULL в столбце Parent указывает на то, что у соответствующего элемента нет родителя. Такой элемент добавляется в XML в качестве элемента верхнего уровня.
Имена столбцов в результирующем наборе строк должны быть заданы с использованием описанного ниже формата:
ElementName!TagNumber!AttributeName
Далее следует описание частей формата.
ElementName
Результирующий общий идентификатор элемента. Например, если Swimmer задано в качестве ElementName, формируется элемент <Swimmer>.
TagNumber
Уникальное значение тега, присвоенное элементу. Это значение с помощью двух метастолбцов, Tag и Parent, определяет вложенность элементов в результирующем XML.
AttributeName
Предоставляет имя создаваемого атрибута для указанного элемента ElementName.
Для понимания того, как универсальная таблица, сформированная запросом, преобразуется в результат XML, предположим, что написан запрос, который создает универсальную таблицу.
Обратите внимание на следующие особенности этой универсальной таблицы:
Первые два столбца Tag и Parent являются метастолбцами. Эти значения определяют иерархию.
Имена столбцов задаются особым образом, как описано выше в данном подразделе.
При формировании XML из этой универсальной таблицы данные таблицы секционируются вертикально на группы столбцов. Группировка определяется на основании значения Tag и имен столбцов. При конструировании XML логика обработки выбирает одну группу столбцов для каждой строки и создает элемент.
Так как существует два уровня в иерархии, следует написать запросы SELECT
для каждого из них
и применить предложение UNION ALL.
Подробнее о использовании режима EXPLICIT совместно с предложением FOR XML смотрите в справке по адресу.
In [1]:
use tempdb
go
select 1 as Tag,
NULL as Parent,
s.FirstName as [Swimmer!1!FirstName],
s.LastName as [Swimmer!1!LastName],
s.YearOfBirth as [Swimmer!1!YearOfBirth],
NULL as [Team!2!City],
NULL as [Team!2!Name],
NULL as [Coach!3!FirstName],
NULL as [Coach!3!LastName]
from dbo.Swimmer s
union all
select 2 as Tag,
1 as Parent,
s.FirstName,
s.LastName,
s.YearOfBirth,
sc.City,
sc.[Name],
NULL,
NULL
from dbo.Swimmer s
left join dbo.SwimmingClub sc on sc.SwimmingClubID = s.SwimmingClubID
union all
select 3 as Tag,
1 as Parent,
s.FirstName,
s.LastName,
s.YearOfBirth,
NULL,
NULL,
c.FirstName,
c.LastName
from dbo.Swimmer s
left join dbo.SwimmerCoach xref on xref.SwimmerID = s.SwimmerID
inner join dbo.Coach c on c.CoachID = xref.CoachID
order by 3, 4, 5, 6, 7, 8, 9
for xml explicit
Commands completed successfully.
Total execution time: 00:00:00.0019871
(21 rows affected)
Total execution time: 00:00:00.0039637
Out[1]:
<Swimmer FirstName="Gleb" LastName="Bondarec" YearOfBirth="2007">
<Coach FirstName="Алла" LastName="Усенок"/>
<Coach FirstName="Виталий" LastName="Барташевич"/>
<Team City="Минск" Name="ГЦОР Трактор"/>
</Swimmer>
<Swimmer FirstName="Алексей" LastName="Рылько" YearOfBirth="2005">
<Team City="Minsk" Name="ДЮСШ Янтарь"/>
</Swimmer>
<Swimmer FirstName="Анна" LastName="Высоцкая" YearOfBirth="2007">
<Coach FirstName="Евгения" LastName="Жукова"/>
<Team City="Minsk" Name="ДЮСШ Янтарь"/>
</Swimmer>
<Swimmer FirstName="Илья" LastName="Гавриленко" YearOfBirth="2006">
<Coach FirstName="Алла" LastName="Усенок"/>
<Coach FirstName="Виталий" LastName="Барташевич"/>
<Team City="Минск" Name="ГЦОР Трактор"/>
</Swimmer>
<Swimmer FirstName="Максим" LastName="Кликушин" YearOfBirth="2007">
<Coach FirstName="Алла" LastName="Усенок"/>
<Coach FirstName="Виталий" LastName="Барташевич"/>
<Team City="Минск" Name="ГЦОР Трактор"/>
</Swimmer>
<Swimmer FirstName="Матвей" LastName="Данкевич" YearOfBirth="2006">
<Team City="Молодечно" Name="СК Олимпик-2011"/>
</Swimmer>
<Swimmer FirstName="Никита" LastName="Клюй" YearOfBirth="2005">
<Team City="Minsk" Name="ДЮСШ Янтарь"/>
</Swimmer>
For XML Raw
Cамый простой и быстрый способ генерации XML.
Используется в случае, если нет необходимости в нетривиальном формате создаваемого XML документа. В этом режиме каждая строка результирующего набора преобразуется в отдельный элемент <row>.
In [2]:
use tempdb
go
select s.FirstName swimmer_first_name,
s.LastName swimmer_last_name,
s.YearOfBirth swimmer_year_of_birth,
sc.City,
sc.[Name],
c.FirstName coach_first_name,
c.LastName coach_last_name
from dbo.Swimmer s
left join dbo.SwimmingClub sc on sc.SwimmingClubID = s.SwimmingClubID
left join dbo.SwimmerCoach xref on xref.SwimmerID = s.SwimmerID
left join dbo.Coach c on c.CoachID = xref.CoachID
order by 1, 2, 3, 4, 5, 6, 7
for xml raw
Commands completed successfully.
Total execution time: 00:00:00.0023278
(7 rows affected)
Total execution time: 00:00:00.0162922
Out[2]:
<row swimmer_first_name="Анна" swimmer_last_name="Высоцкая" swimmer_year_of_birth="2006" City="Минск" Name="ДЮСШ Янтарь" coach_first_name="Евгения" coach_last_name="Жукова"/>
<row swimmer_first_name="Илья" swimmer_last_name="Гавриленко" swimmer_year_of_birth="2006" City="Минск" Name="ГЦОР Трактор" coach_first_name="Алла" coach_last_name="Усенок"/>
<row swimmer_first_name="Илья" swimmer_last_name="Гавриленко" swimmer_year_of_birth="2006" City="Минск" Name="ГЦОР Трактор" coach_first_name="Виталий" coach_last_name="Барташевич"/>
<row swimmer_first_name="Максим" swimmer_last_name="Кликушин" swimmer_year_of_birth="2007" City="Минск" Name="ГЦОР Трактор" coach_first_name="Алла" coach_last_name="Усенок"/>
<row swimmer_first_name="Максим" swimmer_last_name="Кликушин" swimmer_year_of_birth="2007" City="Минск" Name="ГЦОР Трактор" coach_first_name="Виталий" coach_last_name="Барташевич"/>
<row swimmer_first_name="Матвей" swimmer_last_name="Данкевич" swimmer_year_of_birth="2006" City="Молодечно" Name="СК Олимпик-2011"/>
<row swimmer_first_name="Никита" swimmer_last_name="Клюй" swimmer_year_of_birth="2005" City="Минск" Name="ДЮСШ Янтарь"/>
For XML Auto
Чуть более сложный и одновременно чуть более мощный способ генерации XML.
Позволяет стандартным (шаблонным) способом организовывать нетривиальную иерархическую структуру XML.
In [3]:
use tempdb
go
select s.FirstName swimmer_first_name,
s.LastName swimmer_last_name,
s.YearOfBirth swimmer_year_of_birth,
sc.City,
sc.[Name],
c.FirstName coach_first_name,
c.LastName coach_last_name
from dbo.Swimmer s
left join dbo.SwimmingClub sc on sc.SwimmingClubID = s.SwimmingClubID
left join dbo.SwimmerCoach xref on xref.SwimmerID = s.SwimmerID
left join dbo.Coach c on c.CoachID = xref.CoachID
order by 1, 2, 3, 4, 5, 6, 7
for xml auto
Commands completed successfully.
Total execution time: 00:00:00.0018639
(7 rows affected)
Total execution time: 00:00:00.0152012
Out[3]:
<s swimmer_first_name="Анна" swimmer_last_name="Высоцкая" swimmer_year_of_birth="2006">
<sc City="Минск" Name="ДЮСШ Янтарь">
<c coach_first_name="Евгения" coach_last_name="Жукова"/>
</sc>
</s>
<s swimmer_first_name="Илья" swimmer_last_name="Гавриленко" swimmer_year_of_birth="2006">
<sc City="Минск" Name="ГЦОР Трактор">
<c coach_first_name="Алла" coach_last_name="Усенок"/>
<c coach_first_name="Виталий" coach_last_name="Барташевич"/>
</sc>
</s>
<s swimmer_first_name="Максим" swimmer_last_name="Кликушин" swimmer_year_of_birth="2007">
<sc City="Минск" Name="ГЦОР Трактор">
<c coach_first_name="Алла" coach_last_name="Усенок"/>
<c coach_first_name="Виталий" coach_last_name="Барташевич"/>
</sc>
</s>
<s swimmer_first_name="Матвей" swimmer_last_name="Данкевич" swimmer_year_of_birth="2006">
<sc City="Молодечно" Name="СК Олимпик-2011">
<c/>
</sc>
</s>
<s swimmer_first_name="Никита" swimmer_last_name="Клюй" swimmer_year_of_birth="2005">
<sc City="Минск" Name="ДЮСШ Янтарь">
<c/>
</sc>
</s>
For XML Path
Самый гибкий способ генерации XML практически любой структуры (и поэтому медленный!)
Тот же XML что и в примере про xml explicit:
In [4]:
use tempdb
go
select s.FirstName [Swimmer/@FirstName],
s.LastName [Swimmer/@LastName],
s.YearOfBirth [Swimmer/@YearOfBirth],
sc.City [Swimmer/Team/@City],
sc.[Name] [Swimmer/Team/@Name],
(
select
c.FirstName [@FirstName],
c.LastName [@LastName]
from dbo.SwimmerCoach xref
inner join dbo.Coach c on c.CoachID = xref.CoachID
where xref.SwimmerID = s.SwimmerID
for xml path('Coach'), type
) [Swimmer]
from dbo.Swimmer s
left join dbo.SwimmingClub sc on sc.SwimmingClubID = s.SwimmingClubID
order by 1, 2, 3, 4, 5
for xml path('')
Commands completed successfully.
Total execution time: 00:00:00.0013479
(7 rows affected)
Total execution time: 00:00:00.0098099
Out[4]:
<Swimmer FirstName="Gleb" LastName="Bondarec" YearOfBirth="2007">
<Team City="Минск" Name="ГЦОР Трактор"/>
<Coach FirstName="Алла" LastName="Усенок"/>
<Coach FirstName="Виталий" LastName="Барташевич"/>
</Swimmer>
<Swimmer FirstName="Алексей" LastName="Рылько" YearOfBirth="2005">
<Team City="Minsk" Name="ДЮСШ Янтарь"/>
</Swimmer>
<Swimmer FirstName="Анна" LastName="Высоцкая" YearOfBirth="2007">
<Team City="Minsk" Name="ДЮСШ Янтарь"/>
<Coach FirstName="Евгения" LastName="Жукова"/>
</Swimmer>
<Swimmer FirstName="Илья" LastName="Гавриленко" YearOfBirth="2006">
<Team City="Минск" Name="ГЦОР Трактор"/>
<Coach FirstName="Алла" LastName="Усенок"/>
<Coach FirstName="Виталий" LastName="Барташевич"/>
</Swimmer>
<Swimmer FirstName="Максим" LastName="Кликушин" YearOfBirth="2007">
<Team City="Минск" Name="ГЦОР Трактор"/>
<Coach FirstName="Алла" LastName="Усенок"/>
<Coach FirstName="Виталий" LastName="Барташевич"/>
</Swimmer>
<Swimmer FirstName="Матвей" LastName="Данкевич" YearOfBirth="2006">
<Team City="Молодечно" Name="СК Олимпик-2011"/>
</Swimmer>
<Swimmer FirstName="Никита" LastName="Клюй" YearOfBirth="2005">
<Team City="Minsk" Name="ДЮСШ Янтарь"/>
</Swimmer>
Опции elements и root:
In [5]:
use tempdb
go
select s.FirstName,
s.LastName,
s.YearOfBirth,
sc.City Team_City,
sc.[Name] Team_Name
from dbo.Swimmer s
left join dbo.SwimmingClub sc on sc.SwimmingClubID = s.SwimmingClubID
order by 1, 2, 3, 4, 5
for xml raw('element'), root('persons'), elements
Commands completed successfully.
Total execution time: 00:00:00.0019112
(7 rows affected)
Total execution time: 00:00:00.0079248
Out[5]:
<persons>
<element>
<FirstName>Gleb</FirstName>
<LastName>Bondarec</LastName>
<YearOfBirth>2007</YearOfBirth>
<Team_City>Минск</Team_City>
<Team_Name>ГЦОР Трактор</Team_Name>
</element>
<element>
<FirstName>Алексей</FirstName>
<LastName>Рылько</LastName>
<YearOfBirth>2005</YearOfBirth>
<Team_City>Minsk</Team_City>
<Team_Name>ДЮСШ Янтарь</Team_Name>
</element>
<element>
<FirstName>Анна</FirstName>
<LastName>Высоцкая</LastName>
<YearOfBirth>2007</YearOfBirth>
<Team_City>Minsk</Team_City>
<Team_Name>ДЮСШ Янтарь</Team_Name>
</element>
<element>
<FirstName>Илья</FirstName>
<LastName>Гавриленко</LastName>
<YearOfBirth>2006</YearOfBirth>
<Team_City>Минск</Team_City>
<Team_Name>ГЦОР Трактор</Team_Name>
</element>
<element>
<FirstName>Максим</FirstName>
<LastName>Кликушин</LastName>
<YearOfBirth>2007</YearOfBirth>
<Team_City>Минск</Team_City>
<Team_Name>ГЦОР Трактор</Team_Name>
</element>
<element>
<FirstName>Матвей</FirstName>
<LastName>Данкевич</LastName>
<YearOfBirth>2006</YearOfBirth>
<Team_City>Молодечно</Team_City>
<Team_Name>СК Олимпик-2011</Team_Name>
</element>
<element>
<FirstName>Никита</FirstName>
<LastName>Клюй</LastName>
<YearOfBirth>2005</YearOfBirth>
<Team_City>Minsk</Team_City>
<Team_Name>ДЮСШ Янтарь</Team_Name>
</element>
</persons>
OpenXML
Преобразование данных из XML в реляционный формат
In [6]:
declare @hdoc int,
@xml varchar(1000)
set @xml = '
<root>
<Person City="Bothell" Address="New address">
<Name>
<FirstName>Syed</FirstName>
<LastName>Abbas</LastName>
</Name>
</Person>
<Person City="Carnation" Address="9752 Jeanne Circle">
<Name>
<FirstName>Kim</FirstName>
<LastName>Abercrombie</LastName>
</Name>
</Person>
</root>
'
--получаем дескриптор XML документа
exec sp_xml_preparedocument @hdoc OUTPUT, @xml
select *
from OpenXML(@hdoc,'/root/Person',2)
with(
FirstName varchar(20) 'Name/FirstName',
LastName varchar(20) 'Name/LastName',
City varchar(100) '@City'
) X
--освобождаем ресурсы
exec sp_xml_removedocument @hdoc
(2 rows affected)
Total execution time: 00:00:00.0176054
Out[6]:
For JSON Extention
Преобразование данных из реляционной модели в JSON
Аналогично тому как мы можем генерировать XML, мы можем и с JSON:
In [7]:
use tempdb
go
select s.FirstName [Swimmer.FirstName],
s.LastName [Swimmer.LastName],
s.YearOfBirth [Swimmer.YearOfBirth],
sc.City [Team.City],
sc.[Name] [Team.Name],
(
select
c.FirstName [FirstName],
c.LastName [LastName]
from dbo.SwimmerCoach xref
inner join dbo.Coach c on c.CoachID = xref.CoachID
where xref.SwimmerID = s.SwimmerID
for json path
) [Coach]
from dbo.Swimmer s
left join dbo.SwimmingClub sc on sc.SwimmingClubID = s.SwimmingClubID
order by 1, 2, 3, 4, 5
for json path
Commands completed successfully.
Total execution time: 00:00:00.0019346
Out[7]:
[
{"Swimmer":{"FirstName":"Gleb","LastName":"Bondarec","YearOfBirth":2007},"Team":{"City":"Минск","Name":"ГЦОР Трактор"},"Coach":[{"FirstName":"Алла","LastName":"Усенок"},{"FirstName":"Виталий","LastName":"Барташевич"}]},
{"Swimmer":{"FirstName":"Алексей","LastName":"Рылько","YearOfBirth":2005},"Team":{"City":"Minsk","Name":"ДЮСШ Янтарь"}},
{"Swimmer":{"FirstName":"Анна","LastName":"Высоцкая","YearOfBirth":2007},"Team":{"City":"Minsk","Name":"ДЮСШ Янтарь"},"Coach":[{"FirstName":"Евгения","LastName":"Жукова"}]},
{"Swimmer":{"FirstName":"Илья","LastName":"Гавриленко","YearOfBirth":2006},"Team":{"City":"Минск","Name":"ГЦОР Трактор"},"Coach":[{"FirstName":"Алла","LastName":"Усенок"},{"FirstName":"Виталий","LastName":"Барташевич"}]},
{"Swimmer":{"FirstName":"Максим","LastName":"Кликушин","YearOfBirth":2007},"Team":{"City":"Минск","Name":"ГЦОР Трактор"},"Coach":[{"FirstName":"Алла","LastName":"Усенок"},{"FirstName":"Виталий","LastName":"Барташевич"}]},
{"Swimmer":{"FirstName":"Матвей","LastName":"Данкевич","YearOfBirth":2006},"Team":{"City":"Молодечно","Name":"СК Олимпик-2011"}},
{"Swimmer":{"FirstName":"Никита","LastName":"Клюй","YearOfBirth":2005},"Team":{"City":"Minsk","Name":"ДЮСШ Янтарь"}}
]
Total execution time: 00:00:00.0079642
OpenJSON
Преобразование данных из JSON в реляционный формат
OPENJSON — функция с табличным значением, которая выполняет синтаксический анализ текста JSON и возвращает объекты и свойства из входных данных JSON в виде строк и столбцов.
In [8]:
declare @json nvarchar(4000)
set @json = N'
{
"FirstName": "Илья",
"LastName": "Гавриленко",
"YearOfBirth": 2006,
"Gender": "м",
"Club": {
"Name": "ГЦОР Трактор",
"City": "Минск"
},
"Category": "II",
"Coach": [
{"FirstName": "Алла", "LastName": "Усенок"},
{"FirstName": "Виталий", "LastName": "Барташевич"}
]
}
'
select *
from openjson(@json)
--извлечь все атрибуты, в том числе, более чем 1 уровня вложенности
select *
from openjson(@json)
with
(
FirstName nvarchar(20) N'$.FirstName',
LastName nvarchar(30) N'$.LastName',
YearOfBirth smallint N'$.YearOfBirth',
Gender nchar(1) N'$.Gender',
Category nvarchar(20) N'$.Category',
ClubCity nvarchar(50) N'$.Club.City',
ClubName nvarchar(100) N'$.Club.Name',
Coach nvarchar(max) N'$.Coach' as json
) js
(7 rows affected)
(1 row affected)
Total execution time: 00:00:00.0173023
Out[8]:
Out[8]:
Если вы хотите получить данные из вложенного массива JSON, можно использовать корреляционный вызов OPENJSON с помощью CROSS APPLY или OUTER APPLY:
In [9]:
declare @json nvarchar(4000)
set @json = N'
{
"FirstName": "Илья",
"LastName": "Гавриленко",
"YearOfBirth": 2006,
"Gender": "м",
"Club": {
"Name": "ГЦОР Трактор",
"City": "Минск"
},
"Category": "II",
"Coach": [
{"FirstName": "Алла", "LastName": "Усенок"},
{"FirstName": "Виталий", "LastName": "Барташевич"}
]
}
'
--извлечь все атрибуты, в том числе, более чем 1 уровня вложенности
select js.FirstName,
js.LastName,
js.YearOfBirth,
js.Gender,
js.Category,
js.ClubCity,
js.ClubName,
jsc.CoachFirstName,
jsc.CoachLastName
from openjson(@json)
with
(
FirstName nvarchar(20) N'$.FirstName',
LastName nvarchar(30) N'$.LastName',
YearOfBirth smallint N'$.YearOfBirth',
Gender nchar(1) N'$.Gender',
Category nvarchar(20) N'$.Category',
ClubCity nvarchar(50) N'$.Club.City',
ClubName nvarchar(100) N'$.Club.Name',
Coach nvarchar(max) N'$.Coach' as json
) js
outer apply openjson(js.Coach)
with
(
CoachFirstName nvarchar(20) N'$.FirstName',
CoachLastName nvarchar(30) N'$.LastName'
) jsc
(2 rows affected)
Total execution time: 00:00:00.0071696
Out [9]:
JSON_Value, JSON_Query, JSON_Modify
In [10]:
declare @json nvarchar(4000)
set @json = N'
{
"FirstName": "Илья",
"LastName": "Гавриленко",
"YearOfBirth": 2006,
"Gender": "м",
"Club": {
"Name": "ГЦОР Трактор",
"City": "Минск"
},
"Category": "II",
"Coach": [
{"FirstName": "Алла", "LastName": "Усенок"},
{"FirstName": "Виталий", "LastName": "Барташевич"}
]
}
'
select FirstName = json_value(@json, '$.FirstName'),
LastName = json_value(@json, '$.LastName'),
YearOfBirth = json_value(@json, '$.YearOfBirth'),
Team = json_value(@json, '$.Club.City') + ' ' + json_value(@json, '$.Club.Name'),
YearOfBirth = json_query(@json, '$.Coach') --json_value не подходит
(1 row affected)
Total execution time: 00:00:00.0046983
Out [10]:
In [11]:
declare @json nvarchar(4000)
set @json = N'
{
"FirstName": "Илья",
"LastName": "Гавриленко",
"YearOfBirth": 2006,
"Gender": "м",
"Club": {
"Name": "ГЦОР Трактор",
"City": "Минск"
},
"Category": "II",
"Coach": [
{"FirstName": "Алла", "LastName": "Усенок"},
{"FirstName": "Виталий", "LastName": "Барашевич"}
]
}
'
--корректируем ошибку в фамилии тренера
set @json = json_modify(@json, '$.Coach[1].LastName', N'Барташевич')
--добавляем дополнительный атрибут
set @json = json_modify(@json, '$.Club.Address', N'Долгобродская улица, 37. ст.м. Тракторный Завод')
--добавляем дополнительный элемент в массив
set @json = json_modify(
@json,
'append $.Coach',
json_query(N'{"FirstName": "Наталья", "LastName": "Бученкова"}', '$')
)
select @json
(1 row affected)
Total execution time: 00:00:00.0081801
Out [11]:
{
"FirstName": "Илья",
"LastName": "Гавриленко",
"YearOfBirth": 2006,
"Gender": "м",
"Club": { "Name": "ГЦОР Трактор", "City": "Минск" ,"Address":"Долгобродская улица, 37. ст.м. Тракторный Завод"},
"Category": "II",
"Coach": [ {"FirstName": "Алла", "LastName": "Усенок"}, {"FirstName": "Виталий", "LastName": "Барташевич"} ,
{"FirstName": "Наталья", "LastName": "Бученкова"}]
}
Автор материала – Тимофей Гавриленко, преподаватель Тренинг-центра ISsoft.
Образование: окончил с отличием математический факультет Гомельского Государственного Университета им. Франциска Скорины.
Microsoft Certified Professional (70-464, 70-465).
Работа: c 2011 года работает в компании ISsoft (ETL/BI Developer, Release Manager, Data Analyst/Architect, SQL Training Manager), на протяжении 10 лет до этого выступал как Sysadmin, DBA, Software Engineer.
В свободное время ведет бесплатные образовательные курсы для детей и взрослых, желающих повысить свой уровень компьютерной грамотности и переквалифицироваться в IT-специалиста.