Графовые базы данных SQL Server

Графовые базы данных SQL Server

Кардинальным отличием графовых баз данных в SQL Server от графовых баз данных направления NoSQL является то, что для моделирования графа в SQL Server используются таблицы. Это таблицы двух специальных видов. Один вид таблиц используется для создания узлов, и другой – для создания ребер (связей). Отметим, что табличное представление узлов и ребер дает возможность писать запросы к этим таблицам на языке SQL.

Таблица типа узла описывает некую сущность. Экземпляры этой сущности представлены строками таблицы и характеризуются одинаковым набором свойств (столбцами таблицы). Таблица типа ребра определяет характер и направление связи между узлами. Скажем, таблица ToBeFriends (дружить) могла бы описывать дружественную связь между экземплярами одной или разных сущностей.

Вероятно, пора переходить к примерам. Предлагаю взять учебную базу «Окраска», и отобразить реляционную структуру этой базы на структуру графа без потери информации. Это позволит нам писать запросы и сравнивать результаты на двух моделях, представляющих в разной форме одну и ту же информацию.

Итак, у нас есть два типа узлов, представляющих сущности квадрата и баллона, и одна связь между ними, которую можно выразить так: баллон окрашивает квадрат. Направление связи – от баллона к квадрату.

Создадим таблицы узлов:

--Квадраты
CREATE TABLE utqG (
    q_id INT PRIMARY KEY,
    q_name VARCHAR(35) NOT NULL,
) AS NODE;
--Баллончики
CREATE TABLE utvG (
    v_id INT PRIMARY KEY,
    v_name VARCHAR(35) NOT NULL,
    v_color char(1) not null
) AS NODE;

Как видите, таблицы создаются аналогично обычным реляционным за исключением указания типа – AS NODE (т.е. узел). Теперь посмотрим на структуру созданных таблиц:

select table_name, column_name, data_type
from information_schema.columns
where table_name='utqG';
table_namecolumn_namedata_type
utqGgraph_id_AA21DCF7CB44457BB308B21482806B87bigint
utqG$node_id_EEFDE0FB86F243E4A6667A5CE470F4F6nvarchar
utqGq_idint
utqGq_namevarchar

При создании таблицы узлов, помимо пользовательских, автоматически создаются еще два псевдостолбца – graph_id и $node_id. Давайте наполним таблицу данными и посмотрим, что находится в этих столбцах.

insert into utqG(q_id, q_name)
select * from [sql-ex]..utq;

Здесь мы просто берем готовые данные, которые находятся в учебной базе данных с именем sql-ex.

select top 2 * from utqG;
$node_id_EEFDE0FB86F243E4A6667A5CE470F4F6q_idq_name
{“type”:“node”,“schema”:“dbo”,“table”:“utqG”,“id”:0}1Square # 01
{“type”:“node”,“schema”:“dbo”,“table”:“utqG”,“id”:1}2Square # 02

Столбец graph_id отсутствует в выборке. Причиной является то, что этот столбец используется ядром СУБД и недоступен пользователю напрямую. Действительно, если выполнить запрос

select graph_id from utqG;

то мы получим сообщение об ошибке:

Недопустимое имя столбца "graph_id".

Столбец $node_id является уникальным идентификатором узла, представленным в формате JSON. Шестнадцатеричный суффикс в имени столбца делает имя столбца глобально уникальным, однако для доступа к столбцу он не используется. Например,

select top 2 $node_id, q_id from utqG;
$node_id_EEFDE0FB86F243E4A6667A5CE470F4F6q_id
{“type”:“node”,“schema”:“dbo”,“table”:“utqG”,“id”:0}1
{“type”:“node”,“schema”:“dbo”,“table”:“utqG”,“id”:1}2

Таблица utvG выглядит аналогично таблице utqG, и поэтому мы не будем представлять тут её содержимое.

Теперь создадим таблицу ребра.

CREATE TABLE utbG (
    b_datetime datetime not null,
    b_vol tinyint NOT NULL,
) AS EDGE;

Эта таблица содержит столбцы свойств – время окраски (B_datetime) и количество нанесенной из баллончика краски (b_vol). Она отличается от таблиц узлов типом – теперь это EDGE, а не NODE. Рассмотрим структуру этой таблицы:

select column_name, data_type
from information_schema.columns where table_name='utbG';
column_namedata_type
graph_id_9D6121CFD14948A5B03BBD6A4BDB4774bigint
$edge_id_EAB5B85BC07649ED89435D6F2A2ACE83nvarchar
from_obj_id_D0D5B23329B5409895672D5C32283E51int
from_id_526FECFDEBB84E86B2F7440FF625BCF1bigint
$from_id_BF1E69FB306E4225A58F4DAFAA8FBE94nvarchar
to_obj_id_14C309807A224F7BA7BD8F88425A15ABint
to_id_295F2F2BD42A425CAAD6ACC9F2174BC7bigint
$to_id_79504454FC184B7F849B1AB28BCCC670nvarchar
b_datetimedatetime
b_voltinyint

Как и для узлов, автоматически были созданы несколько псевдостолбцов, среди которых доступными пользователю являются:

  • $edge_id – идентификатор ребра, формируется автоматически;
  • $from_id – идентификатор узла, откуда исходит ребро;
  • $to_id – идентификатор узла, куда входит ребро.

Заполним и эту таблицу данными. Если мы вспомним структуру исходной таблицы utb, то для каждой строки в столбец $from_id мы должны поместить идентификатор того узла из таблицы utvG, для которого v_id равен b_v_id таблицы utb. Тогда столбец $to_id должен содержать идентификатор того узла из таблицы utqG, для которого q_id равен, соответственно, b_q_id из той же строки таблицы utb. Читателю, наверное, будет проще понять оператор, который вставит описанные данные:

insert into utbG(b_datetime, b_vol, $from_id, $to_id)
select b_datetime, b_vol,
(select $node_id from utvG where v_id = orig.b_v_id),
(select $node_id from utqG where q_id = orig.b_q_id)
from [sql-ex]..utb orig

Теперь мы можем посмотреть на данные в таблице ребра, которая устанавливает связь между двумя узлами – баллончиком с краской и окрашиваемым квадратом.

select top 1 * from utbG;

Поскольку ширина результирующей таблицы значительно превышает ширину страницы, представим результат в формате «ключ: значение»

$edge_id_EAB5B85BC07649ED89435D6F2A2ACE83: {“type”:“edge”,“schema”:“dbo”,“table”:“utbG”,“id”:0}
$from_id_BF1E69FB306E4225A58F4DAFAA8FBE94: {“type”:“node”,“schema”:“dbo”,“table”:“utvG”,“id”:49}
$to_id_79504454FC184B7F849B1AB28BCCC670: {“type”:“node”,“schema”:“dbo”,“table”:“utqG”,“id”:21}
b_datetime: “2000-01-01 01:13:36.000”
b_vol: 50