MSSQL Graph DB¶
Table is nothing, relation does matter
Disclaimer¶
Post language: Ukrainian
Трохи теорії¶
Компанія Microsoft зробила ще один крок для об’єднання різних технологій і методологій роботи з даними під одним дахом. Додавання обробки JSON дало можливість працювати з даними в стилі NOSQL Document DB. Добавання роботи з графовою моделлю даних дасть можливість працювати в стилі NOSQL Graph DB. Звичайно, що під капотом залишився той самий движок реляційних баз даних, на який навішали новий функціонал.
Світ не розділений на реляційні моделі, графові моделі, документи – все це існує інтегроване одне з другим. Зазвичай в реальна модель даних виглядає по різному з різних кутів зору. Відповно зручно мати універсальний комбайн, який дозволить працювати з такою «мішаною» моделлю. Звичайно, вузькоспеціалізовані моделі даних задачі будуть вимагати вузькоспеціалізованих інструментів. Але для того, що називається «золотою серединою» універсальний комбайн стане в пригоді. Прикладом може бути той самий PostgreSQL з підтримкою JSON – реляційні дані в таблицях, те що не має строгої моделі даних – в JSON.
Той No SQL, який є Graph DB ніколи не був на хвилі хайпу і не старався витіснити реляційні бази даних з насиджених місць. Реляційні і графові бази даних мають одну спільну рису – і та і та системи побудовані на основі математичних теорій – теорії множин і теорії графів. У решті аспектів роботи з даними вони різні. Реляційні бази даних працюють з сутностями, їхніми властивостями. Вони добре підходять для управління відностно статичними наборами даних з одноріднми зв’язками між ними. Графові бази даних оперують зв’язками між сутностями. Вони взяли на себе вирішення задач що стосувалися складності структури (моделі) даних, стали альтернативою, коли звязки між об’єктами є такими самими важливими як і самі об’єкти. Сьогодні бізнес для досягнення успіху більше орієнтується на зв’язки між даними, а не тільки на самі дані. А це територія графових баз. Відповідно графова і реляційна модель доповнюють одна іншу, дозволяють аналізувати дані в різних ракурсах.
Структура графових баз даних проста. Є два типи об’єктів – вершини (або ноди), які представляють різні об’єкти, та ребра які репрезентують зв’язки між цими вершинами. Графова база даних надає можливості аналізувати зв’язки. Зв’язки між сутностями містять значний контекст, який втрачається при нормалізації даних в реляційних базах. З іншої сторони графові бази даних мають більш інтуїтивну модель даних. В реляційні моделі ми оперуєм множинами записів і менший акцент робиться на зв’язки між записами в межах множини. В класичному “NOSQL” модель спрощена по максимуму (ключ або документ) для досягнення кращого масштабування і швидкодії. Фактично графові бази даних добре працюють з складними моделями, дозволяють отримувати знання з зв’язків між даними. Сама теорія графів має практичні застосування в медицині, фізиці, соціології і, звичайно, в комп’ютерних науках. Типи задач, де використовуються графові бази даних: виявлення шахрайства, GDPR та інші типи регулювання, ІТ – управління мережевими даними і звичайно соціальні мережі та системи рекомендацій.
В архітектурі графових баз даних виділяються два основних підходи. Один з них - це RDF (Resource Description Framework) розроблений в кінці 90-х років для аналізу web ресурсів і зв’язків між ними. RDF формально оперує об’єктами, котрі концептуально є трійками “сутність: атрибут: значення”. Для навігації використовується декларативна мова (SQL подібна) SPARQL, розроблена консорціумом W3C. Інший підхід – labeled property graph (проблема перекладу українською). Його розробила група шведських інженерів для потреб власного проекту. Тут вершини і ребра можуть мати додаткові властивості. Прикладом реалізації цього підходу є графова база даних Neo4J. Мовою навігації є декларативна мова Cypher (CQL).
CQL використовує схожий на SQL синтакс. Послідовність опцій в запиті є більш природньою, ніж в SQL – команда для виводу результатів знаходиться в кінці виразу (декларації).
Підтримка графових баз даних була добавлена у версії MS SQL Server 2017 року, нові команди добавлені в 2019. Для роботи з графовими даними в MS SQL Server реалізовано підхід labeled property graph. Що було додано: нові типи таблиць (NODE, EDGE (+ обмеження)), ключові слова MATCH (для підтримки пошуку за шаблонами та навігації по графу), FOR PATH, функція SHORTEST PATH, розширення агрегатних функцій WITHING GROUP (GRAPH PATH) Було внесено зміни в системні каталоги, додані нові системні функції. Документація є на офіційному сайті компанії Майкрософт.
Приклад¶
Розглянемо варіант соціальної мережі
Вершини – випускники, університети, міста. Ребра: Випускники дружать, колись вчилися, живуть у містах. Університети знаходяться у містіах. Схематично запити до соціальної мережі прості – показати коло друзів (безпосередніх, друзів друзів (2 рівень)...) і знайти найкоротший шлях між друзями (якщо він існує). Повний скрипт для створення структури і заливки даних знаходиться в додатку. Тут проаналізуємо окремі команди і нові ключові слова.
Створення таблиці вершин:
Нове ключове слово – AS NODE
визначає тип таблиці. Коли таблиця створена, то крім явно заданих колонок вона містить і набір системних. Дивимося структуру таблиці і дані.
SELECT object_name(c.object_id) AS table_name
, c.name AS column_name
, c.graph_type_desc AS graph_type_desc
FROM sys.columns c
WHERE c.object_id = OBJECT_ID('[sn].[alumnus]')
-- table_name column_name graph_type_desc
-- ---------- ----------------------------------------- ---------------
-- alumnus graph_id_21C6D6D17C4648039ED00A332D88D152 GRAPH_ID
-- alumnus $node_id_4BE90463CC054FAD9926854B90F3D5CB GRAPH_ID_COMPUTED
-- alumnus id NULL
-- alumnus name NULL
SELECT $node_id, id, name FROM [sn].[alumnus]
-- $node_id_4BE90463CC054FAD9926854B90F3D5CB id name
-- ------------------------------------------------------ -- ----
-- {"type":"node","schema":"sn","table":"alumnus","id":0} 1 Vera
-- {"type":"node","schema":"sn","table":"alumnus","id":1} 2 Patrizio
-- {"type":"node","schema":"sn","table":"alumnus","id":2} 3 Sergio
-- {"type":"node","schema":"sn","table":"alumnus","id":3} 4 Caprice
-- {"type":"node","schema":"sn","table":"alumnus","id":4} 5 Bellissa
Як ми бачимо, є дві службових колонки, обидві автогенеровані, одна з них доступна на читання $node_id
. Структура колонки $node_id
– JSON. Що цікаво, ідентифікатри в таблиці стартують з 1, а в зненерованому JSON-I – з 0. Колонка Graph_id
недоступна, але у кожній таблиці по ній створюється унікальний некластерний індекс.
Створення таблиці ребер. Якщо ребро має властивості (так як «вчилися у», то табличка буде створюватися з колонками, якщо немає – то у ній будуть присутні тільки службові колонки)
CREATE TABLE sn.graduatedfrom
( rating INTEGER
, graduatedyear INTEGER
)
AS EDGE;
CREATE TABLE sn.friendof AS EDGE;
Ключове слово AS EDGE
визначає тип таблиці – ребро. Маємо нові типи графових колонок. Працювати будем з колонками $edge_id
, $from_id
, $to_id
. На відміну від традиційних таблиць є можливість створити табличку без колонок. Структура таблиці (ребро містить користувацькі властивості – рейтинг, рік закінчення):
table_name column_name graph_type_desc
------------- -------------------------------------------- ---------------
graduatedfrom graph_id_3CDE2433553947C9979FB537014C37DD GRAPH_ID
graduatedfrom $edge_id_3199B59CF4264209B3C33C2A9A6D3993 GRAPH_ID_COMPUTED
graduatedfrom from_obj_id_377B54A257834F19AB3D376E6EC38C41 GRAPH_FROM_OBJ_ID
graduatedfrom from_id_A4EC331A614D4B87810AA535509CDB30 GRAPH_FROM_ID
graduatedfrom $from_id_96F31C58E41848349732356ABBBFA1C4 GRAPH_FROM_ID_COMPUTED
graduatedfrom to_obj_id_0E6E76FE72CC4194A4C7A41A7F538B6E GRAPH_TO_OBJ_ID
graduatedfrom to_id_13C58060E6DF416CBE08E05A9652FACE GRAPH_TO_ID
graduatedfrom $to_id_48D2A88E1AA94EFABD3B68B08C10612A GRAPH_TO_ID_COMPUTED
graduatedfrom rating NULL
graduatedfrom graduatedyear NULL
Зразок даних.
-- $edge_id_3199B59CF4264209B3C33C2A9A6D3993 $from_id_96F31C58E41848349732356ABBBFA1C4 $to_id_48D2A88E1AA94EFABD3B68B08C10612A rating graduatedyear
-- {"type":"edge","schema":"sn","table":"graduatedfrom","id":0} {"type":"node","schema":"sn","table":"alumnus","id":0} {"type":"node","schema":"sn", table":"university","id":0} 9 2010
-- {"type":"edge","schema":"sn","table":"graduatedfrom","id":1} {"type":"node","schema":"sn","table":"alumnus","id":1} {"type":"node","schema":"sn","table":"university","id":0} 8 2010
-- {"type":"edge","schema":"sn","table":"graduatedfrom","id":2} {"type":"node","schema":"sn","table":"alumnus","id":2} {"type":"node","schema":"sn","table":"university","id":1} 8 2011
Структура таблиці у випадку, коли немає додаткових властивостей.
table_name column_name graph_type_desc
---------- -------------------------------------------- ---------------
friendof graph_id_2DD2FDDB742A43419F03B53351B0EEA1 GRAPH_ID
friendof $edge_id_BA3A385B57654E1B845ACD1BFCB3DD1D GRAPH_ID_COMPUTED
friendof from_obj_id_E0CB7FFB93124606AEFED8296507FA47 GRAPH_FROM_OBJ_ID
friendof from_id_38010D8B5F854BD5BAF35B1DBC451878 GRAPH_FROM_ID
friendof $from_id_719A9DBB01774C16BCB1C55E665AAADF GRAPH_FROM_ID_COMPUTED
friendof to_obj_id_4D67821CE5D349638B6DC911A0C68894 GRAPH_TO_OBJ_ID
friendof to_id_F48CDB2969B24A8FA0522BEECFDEA073 GRAPH_TO_ID
friendof $to_id_73917B0BD5F84EB2B810BE72714B4731 GRAPH_TO_ID_COMPUTED
Зразок даних.
-- $edge_id_BA3A385B57654E1B845ACD1BFCB3DD1D $from_id_719A9DBB01774C16BCB1C55E665AAADF $to_id_73917B0BD5F84EB2B810BE72714B4731
-- ------------------------------------------------------- ------------------------------------------------------ ------------------------------------------------------
-- {"type":"edge","schema":"sn","table":"friendof","id":0} {"type":"node","schema":"sn","table":"alumnus","id":0} {"type":"node","schema":"sn","table":"alumnus","id":1}
-- {"type":"edge","schema":"sn","table":"friendof","id":1} {"type":"node","schema":"sn","table":"alumnus","id":1} {"type":"node","schema":"sn","table":"alumnus","id":2}
-- {"type":"edge","schema":"sn","table":"friendof","id":2} {"type":"node","schema":"sn","table":"alumnus","id":2} {"type":"node","schema":"sn","table":"alumnus","id":0}
Для таблиць ребер є можливість створювати додаткові обмеження – аналог зовнішніх ключів.
ALTER TABLE sn.graduatedfrom ADD CONSTRAINT ec_graduatedfrom CONNECTION (sn.alumnus TO sn.university);
ALTER TABLE sn.friendof ADD CONSTRAINT ec_friendof CONNECTION (sn.alumnus TO sn.alumnus);
Обмеження показують з яких табличок будуть братися вершини і напрямок. Заливка даних в таблиці. Вершини заливаються просто, як звичайні дані. Службові колонки генеруються автоматично. Для заливки ребер необхідно вичитувати ідентифікатори вершин.
INSERT
INTO sn.friendof
( $from_id
, $to_id
)
VALUES
( (SELECT $node_id FROM sn.alumnus WHERE id = 2)
, (SELECT $node_id FROM sn.alumnus WHERE id = 3)
)
;
Обмеження на таблицях вершин не дозволять вставити туди невалідні дані. Спробуєм виконати такий запит:
Результатом буде помилка
Msg 515, Level 16, State 2, Line 26
Cannot insert the value NULL into column 'from_obj_id_E0CB7FFB93124606AEFED8296507FA47', table 'GraphDB_demo.sn.friendof'; column does not allow nulls. INSERT fails.
The statement has been terminated.
Таблички створені, дані залиті, можна пробувати писати запити. Починаєм з найпростіших.
Запит 1: Знайти Університет, який закінчила Vera:
SELECT university.name
FROM sn.alumnus AS alumnus
, sn.graduatedfrom AS graduatedfrom
, sn.university AS university
WHERE MATCH (alumnus - (graduatedfrom) -> university)
AND alumnus.name = 'Vera'
;
-- name
-- University of Bologna
FROM
є список вершин і ребер, котрі їх з’єднують, в WHERE
– умова фільтрування і шлях, по якому будемо проходити по графу - MATCH
. Тут спосіб думання має перемкнутися від оперування множинами до траверсу (переходу між вершинами) графа. Зазвичай в запитах такого типу є задана одна або кілька вершин, ребра і напрямок проходження.
Запит 2: знайти університети які закінчили друзі Donatello:
SELECT university.name AS university_name
, alumnus2.name AS friends_name
FROM sn.alumnus alumnus1
, sn.alumnus alumnus2
, sn.graduatedfrom AS graduatedfrom
, sn.friendof AS friendof
, sn.university AS university
WHERE MATCH( alumnus1 - (friendof) -> alumnus2 - (graduatedfrom) -> university)
AND alumnus1.name = 'Donatello'
;
-- university_name friends_name
-- --------------- ------------
-- University of Milan Emilia
-- University of Padua Massimo
Якщо нам потрібно знайти університети котрі закінчили друзі друзів Donatello,
то ми додаєм ще одну вершину і ще одне ребро (послідовність таблиць в FROM
не має значення). І відкоригувати шлях (опція MATCH
)
SELECT university.name AS university_name
, alumnus3.name AS friends_name
FROM sn.alumnus alumnus1
, sn.alumnus alumnus2
, sn.alumnus alumnus3
, sn.graduatedfrom AS graduatedfrom
, sn.friendof AS friendof
, sn.friendof AS friendof1
, sn.university AS university
WHERE MATCH( alumnus1 - (friendof) -> alumnus2 - (friendof1) -> alumnus3 - (graduatedfrom) -> university)
AND alumnus1.name = 'Donatello'
;
-- university_name friends_name
-- --------------- ------------
-- University of Naples Gian
-- University of Padua Carina
Тепер спробуєм додати декілька шляхів переходів в опцію MATCH
.
Запит 3: знайти всіх випускників, котрі вчилися в тому самому місті де живуть.
SELECT alumnus.name AS person
, city.name AS city_name
FROM sn.alumnus alumnus
, sn.graduatedfrom AS graduatedfrom
, sn.university AS university
, sn.livesin AS livesin
, sn.locatedin AS locatedin
, sn.city AS city
WHERE MATCH ( alumnus - (graduatedfrom) -> university - (locatedin) -> city
AND alumnus - (livesin) -> city
)
;
-- person city_name
-- ------ ---------
-- Vera Bologna
-- Sistine Perugia
-- Aleksandra Perugia
Тут ми шукаємо пари вершин персона – місто використовуючи перехід по різних шляхах. Перший : персона –> живе в -> місто; другий : персона -> закінчила –> університет –> розташований -> місто. І вибираємо ті пари, які співпадають.
Виконаємо два запити, що шукають друзів 3 і 5 кола для Donatello.
З цих запитів видно, що FROM
працює аналогічно INNER JOIN
-у.
Друзі третього кола не потраплять у список друзів 5 кола.
А список друзів 6 кола поверне порожній результат (через те, що чиїхось друзів не внесли в табличку).
Запит 4 і 5: знайти всіх друзів 3 і 5 кола для Donatello
SELECT alumnus1.name AS name1
, alumnus2.name AS name2
, alumnus3.name AS name3
, alumnus4.name AS name4
FROM sn.alumnus alumnus1
, sn.friendof fo
, sn.friendof fo2
, sn.friendof fo3
, sn.alumnus alumnus2
, sn.alumnus alumnus3
, sn.alumnus alumnus4
WHERE MATCH (alumnus1-(fo)->alumnus2-(fo2)-> alumnus3-(fo3) -> alumnus4)
AND alumnus1.name = 'Donatello'
;
-- name1 name2 name3 name4
-- ----- ----- ----- -----
-- Donatello Emilia Gian Stefano
-- Donatello Massimo Carina Sistine
-- Donatello Massimo Carina Stefano
SELECT alumnus1.name AS name1
, alumnus2.name AS name2
, alumnus3.name AS name3
, alumnus4.name AS name4
, alumnus5.name AS name5
, alumnus6.name AS name6
FROM sn.alumnus alumnus1
, sn.friendof fo
, sn.friendof fo2
, sn.friendof fo3
, sn.friendof fo4
, sn.friendof fo5
, sn.alumnus alumnus2
, sn.alumnus alumnus3
, sn.alumnus alumnus4
, sn.alumnus alumnus5
, sn.alumnus alumnus6
WHERE MATCH (alumnus1-(fo)->alumnus2-(fo2)-> alumnus3-(fo3) -> alumnus4 -(fo4) -> alumnus5 -(fo5) -> alumnus6)
AND alumnus1.name = 'Donatello'
;
-- name1 name2 name3 name4 name5 name6
-- ----- ----- ----- ----- ----- -----
-- Donatello Massimo Carina Sistine Aleksandra Stefano
Ще раз щодо наступного кола друзів – в якийсь момент ми отримаєм зациклення. Vera дружить з Patrizio, котрий дружить з Sergio, а той дружить з Vera. Коло замикається і у цьому випадку ми отримаєм друзів будь-якого кола (до запиту можна докидати нові пари вершина – ребро і він буде повертати результат).
Запит 6: приклад зациклення
SELECT alumnus1.name as name1
, alumnus2.name as name2
, alumnus3.name as name3
, alumnus4.name as name4
, alumnus5.name as name5
, alumnus6.name as name6
, alumnus7.name as name7
FROM sn.alumnus alumnus1
, sn.friendof fo
, sn.friendof fo2
, sn.friendof fo3
, sn.friendof fo4
, sn.friendof fo5
, sn.friendof fo6
, sn.alumnus alumnus2
, sn.alumnus alumnus3
, sn.alumnus alumnus4
, sn.alumnus alumnus5
, sn.alumnus alumnus6
, sn.alumnus alumnus7
WHERE MATCH (alumnus1-(fo)->alumnus2-(fo2)-> alumnus3-(fo3) -> alumnus4 -(fo4) -> alumnus5 -(fo5) -> alumnus6 -(fo6) -> alumnus7)
AND alumnus1.name IN('Vera', 'Patrizio', 'Sergio')
;
-- name1 name2 name3 name4 name5 name6 name7
-- ----- ----- ----- ----- ----- ----- -----
-- Patrizio Sergio Vera Patrizio Sergio Vera Patrizio
-- Sergio Vera Patrizio Sergio Vera Patrizio Sergio
-- Vera Patrizio Sergio Vera Patrizio Sergio Vera
І останній тип запитів – знайти найкоротший шлях між двома вершинами. Для прикладу шукаємо найкоротший шлях між Donatello i Stefano. З попередніх запитів ми бачимо що Stefano є як в 3 колі друзів Donatello, так і в 5. Шукаєм найкоротший шлях.
Запит 7: знайти найкоротший шлях між Donatello i Stefano.
SELECT q.personname
, q.friends
, q.levels
FROM
(
SELECT alumnus1.name AS personname
, STRING_AGG(alumnus2.name, '->') WITHIN GROUP (GRAPH PATH) AS friends
, LAST_VALUE(alumnus2.name) WITHIN GROUP (GRAPH PATH) AS lastnode
, COUNT(alumnus2.name) WITHIN GROUP (GRAPH PATH) AS levels
FROM sn.alumnus alumnus1
, sn.friendof FOR PATH fo
, sn.alumnus FOR PATH alumnus2
WHERE MATCH(SHORTEST_PATH(alumnus1(-(fo)->alumnus2)+))
AND alumnus1.name = 'Donatello'
) q
WHERE q.lastnode = 'Stefano'
;
-- personname friends levels
-- ---------- ------- ------
-- Donatello Emilia->Gian->Stefano 3
Результат збігається з очікуванням.
В запиті ми бачимо підтримку агрегатних функцій: опції GRAPH PATH
/FOR PATH
і функцію SHORTEST_PATH
, котра використовується тільки всередині MATCH
.
«+» показує, чи ми проходимо весь граф, чи зупиняємся на якомусь рівні.
Внутрішній запит знаходить найкоротший шлях від вказаної вершини до всіх решта. Зовнішній вже відфільтровує результат.
З прикладами все.
Під капотом використовується той самий движок баз даних без додаткових компонент в плані запиту. Компанія Microsoft відома тим, що покращення на рівні движка бази даних вносяться в наступних версіях після виходу нового функціоналу. Так як було з віконними функціями і пакетною обробкою записів для даних, що зберігаються по рядках (batch processing for row store). Існують певні обмеження для нових типів таблиць – вони не можуть бути тимчасовими, темпоральними таблицями, табличними змінними чи типами. Неможливо в одному запиті звертатися до графових об’єктів в різних базах даних (виконувати cross-database query). Плюсом для роботи є те, що використовується той самий SQL. Підхід компанії до розвитку свого флагманського продукту – інтеграція під одним дахом різних структур даних і підходів до обробки цих даних. Такий аналог кухонного комбайну. З таким підходом і SQL збагачується новими можливостями, які з часом з категорії розширень можуть перейти в стандарт.
Додаток¶
SQL Script для створення таблиць і заливки даних. База graphdb_demo
повинна бути створена пеерд запуском скріпта.
Викноувати на 2019 версії MS SQL Server (моя робоча версія - Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64) Sep 24 2019 13:48:23 Copyright (C) 2019 Microsoft Corporation Developer Edition (64-bit) on Windows 10 Enterprise 10.0
USE [graphdb_demo]
GO
if not exists (select * from sys.schemas where name = 'sn')
execute('create schema sn;');
go
-- Create NODE tables
CREATE TABLE sn.alumnus
( id INTEGER PRIMARY KEY
, name VARCHAR(100)
)
AS NODE;
CREATE TABLE sn.university
( id INTEGER PRIMARY KEY
, name VARCHAR(100) UNIQUE
)
AS NODE;
CREATE TABLE sn.city
( id INTEGER PRIMARY KEY
, name VARCHAR(100)
, country VARCHAR(100)
)
AS NODE;
-- Create EDGE tables.
CREATE TABLE sn.graduatedfrom
( rating INTEGER
, graduatedyear INTEGER
)
AS EDGE;
ALTER TABLE sn.graduatedfrom ADD CONSTRAINT ec_graduatedfrom
CONNECTION (sn.alumnus TO sn.university);
CREATE TABLE sn.friendof AS EDGE;
ALTER TABLE sn.friendof ADD CONSTRAINT ec_friendof
CONNECTION (sn.alumnus TO sn.alumnus);
CREATE TABLE sn.livesin AS EDGE;
ALTER TABLE sn.livesin ADD CONSTRAINT ec_livesin
CONNECTION (sn.alumnus TO sn.city);
CREATE TABLE sn.locatedin AS EDGE;
ALTER TABLE sn.locatedin ADD CONSTRAINT ec_locatedin
CONNECTION (sn.university TO sn.city);
GO
INSERT INTO sn.alumnus
(id, name)
VALUES
(1,'Vera')
,(2,'Patrizio' )
,(3,'Sergio')
,(4,'Caprice')
,(5,'Bellissa')
,(6,'Donatello')
,(7,'Emilia')
,(8,'Gian')
,(9,'Stefano')
,(10,'Massimo')
,(11,'Carina')
,(12,'Sistine')
,(13,'Aleksandra')
;
GO
INSERT INTO sn.university
(id, name)
VALUES
(1,'University of Bologna')
,(2,'University of Florence')
,(3,'University of Milan')
,(4,'University of Naples')
,(5,'University of Padua')
,(6,'University of Perugia')
GO
INSERT INTO sn.city
(id, name, country)
VALUES
(1,'Bologna','Italy')
,(2,'Florence','Italy')
,(3,'Milan','Italy')
,(4,'Naples','Italy')
,(5,'Padua','Italy')
,(6,'Perugia','Italy')
,(7,'Rome','Italy')
,(8,'Salerno','Italy')
,(9,'Siena','Italy')
,(10,'Turin','Italy')
,(11,'Catania','Italy')
,(12,'Modena','Italy')
,(13,'Cagliari','Italy')
GO
-- Insert data into the friendof edge.
INSERT INTO sn.friendof
VALUES
( (SELECT $node_id FROM sn.alumnus WHERE id = 1)
, (SELECT $node_id FROM sn.alumnus WHERE id = 2)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 2)
, (SELECT $node_id FROM sn.alumnus WHERE id = 3)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 3)
, (SELECT $node_id FROM sn.alumnus WHERE id = 1)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 4)
, (SELECT $node_id FROM sn.alumnus WHERE id = 2)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 5)
, (SELECT $node_id FROM sn.alumnus WHERE id = 4)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 6)
, (SELECT $node_id FROM sn.alumnus WHERE id = 7)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 7)
, (SELECT $node_id FROM sn.alumnus WHERE id = 8)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 8)
, (SELECT $node_id FROM sn.alumnus WHERE id = 9)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 6)
, (SELECT $node_id FROM sn.alumnus WHERE id = 10)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 10)
, (SELECT $node_id FROM sn.alumnus WHERE id = 11)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 11)
, (SELECT $node_id FROM sn.alumnus WHERE id = 12)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 12)
, (SELECT $node_id FROM sn.alumnus WHERE id = 13)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 13)
, (SELECT $node_id FROM sn.alumnus WHERE id = 9)
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 11)
, (SELECT $node_id FROM sn.alumnus WHERE id = 9)
);
GO
INSERT INTO sn.graduatedfrom
($from_id, $to_id, rating, graduatedyear )
VALUES
( (SELECT $node_id FROM sn.alumnus WHERE id = 1)
, (SELECT $node_id FROM sn.university WHERE id = 1)
, 9
, 2010
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 2)
, (SELECT $node_id FROM sn.university WHERE id = 1)
, 8
, 2010
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 3)
, (SELECT $node_id FROM sn.university WHERE id = 2)
, 8
, 2011
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 4)
, (SELECT $node_id FROM sn.university WHERE id = 2)
, 8
, 2009
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 5)
, (SELECT $node_id FROM sn.university WHERE id = 3)
, 8
, 2006
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 6)
, (SELECT $node_id FROM sn.university WHERE id = 3)
, 7
, 2009
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 7)
, (SELECT $node_id FROM sn.university WHERE id = 3)
, 8
, 2013
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 8)
, (SELECT $node_id FROM sn.university WHERE id = 4)
, 9
, 2012
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 9)
, (SELECT $node_id FROM sn.university WHERE id = 5)
, 8
, 2005
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 10)
, (SELECT $node_id FROM sn.university WHERE id = 5)
, 7
, 2014
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 11)
, (SELECT $node_id FROM sn.university WHERE id = 5)
, 8
, 2003
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 12)
, (SELECT $node_id FROM sn.university WHERE id = 6)
, 8
, 2003
),
( (SELECT $node_id FROM sn.alumnus WHERE id = 13)
, (SELECT $node_id FROM sn.university WHERE id = 6)
, 7
, 2018
);
GO
INSERT INTO sn.locatedin
($from_id, $to_id)
VALUES
((SELECT $node_id FROM sn.university WHERE id = 1)
,(SELECT $node_id FROM sn.city WHERE id = 1)
),
((SELECT $node_id FROM sn.university WHERE id = 2)
,(SELECT $node_id FROM sn.city WHERE id = 2)
),
((SELECT $node_id FROM sn.university WHERE id = 3)
,(SELECT $node_id FROM sn.city WHERE id = 3)
),
((SELECT $node_id FROM sn.university WHERE id = 4)
,(SELECT $node_id FROM sn.city WHERE id = 4)
),
((SELECT $node_id FROM sn.university WHERE id = 5)
,(SELECT $node_id FROM sn.city WHERE id = 5)
),
((SELECT $node_id FROM sn.university WHERE id = 6)
,(SELECT $node_id FROM sn.city WHERE id = 6)
);
GO
INSERT INTO sn.livesin
($from_id, $to_id)
VALUES
((SELECT $node_id FROM sn.alumnus WHERE id = 1)
,(SELECT $node_id FROM sn.city WHERE id = 1)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 2)
,(SELECT $node_id FROM sn.city WHERE id = 2)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 3)
,(SELECT $node_id FROM sn.city WHERE id = 3)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 4)
,(SELECT $node_id FROM sn.city WHERE id = 4)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 5)
,(SELECT $node_id FROM sn.city WHERE id = 5)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 6)
,(SELECT $node_id FROM sn.city WHERE id = 6)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 7)
,(SELECT $node_id FROM sn.city WHERE id = 7)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 8)
,(SELECT $node_id FROM sn.city WHERE id = 8)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 9)
,(SELECT $node_id FROM sn.city WHERE id = 9)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 10)
,(SELECT $node_id FROM sn.city WHERE id = 10)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 13)
,(SELECT $node_id FROM sn.city WHERE id = 6)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 11)
,(SELECT $node_id FROM sn.city WHERE id = 11)
),
((SELECT $node_id FROM sn.alumnus WHERE id = 12)
,(SELECT $node_id FROM sn.city WHERE id = 6)
);
GO