Каждые несколько месяцев мы проводим встречу по реляционному графику в офисе в Нео-Лондоне, где рассказываем, как перенести ваши данные из реляционной базы данных в граф.
Мы используем набор данных Northwind, который часто поставляется как демонстрационный набор данных в реляционных базах данных, и придумываем некоторые запросы, которые кажутся графическими по своей природе.
Мой любимый запрос — это вопрос о том, как организованы сотрудники и кто перед кем отчитывается. Я подумал, что было бы довольно интересно посмотреть, как это будет выглядеть в Postgres SQL, просто для удовольствия.
Мы начнем с получения списка сотрудников и лица, которому они сообщают:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
SELECT e."EmployeeID", e."ReportsTo"FROM employees AS eWHERE e."ReportsTo" IS NOT NULL; EmployeeID | ReportsTo------------+----------- 1 | 2 3 | 2 4 | 2 5 | 2 6 | 5 7 | 5 8 | 2 9 | 5(8 ROWS) |
В Cypher мы бы сделали это:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
MATCH (e:Employee)<-[:REPORTS_TO]-(sub)RETURN sub.EmployeeID, e.EmployeeID +-------------------------------+| sub.EmployeeID | e.EmployeeID |+-------------------------------+| "4" | "2" || "5" | "2" || "1" | "2" || "3" | "2" || "8" | "2" || "9" | "5" || "6" | "5" || "7" | "5" |+-------------------------------+8 rows |
Теперь давайте найдем большого босса, который никому не подчиняется. Сначала в SQL:
|
1
2
3
4
5
6
7
8
|
SELECT e."EmployeeID" AS bigBossFROM employees AS eWHERE e."ReportsTo" IS NULL bigboss--------- 2(1 ROW) |
А теперь шифр
|
01
02
03
04
05
06
07
08
09
10
|
MATCH (e:Employee)WHERE NOT (e)-[:REPORTS_TO]->()RETURN e.EmployeeID AS bigBoss +---------+| bigBoss |+---------+| "2" |+---------+1 row |
Нам по-прежнему не нужно ничего объединять, поэтому запрос пока не так интересен. Давайте добавим еще несколько свойств из записи менеджера, поэтому нам нужно самостоятельно присоединиться к таблице сотрудников:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
SELECT e."FirstName", e."LastName", e."Title", manager."FirstName", manager."LastName", manager."Title"FROM employees AS eJOIN employees AS manager ON e."ReportsTo" = manager."EmployeeID"WHERE e."ReportsTo" IS NOT NULL FirstName | LastName | Title | FirstName | LastName | Title-----------+-----------+--------------------------+----------+-----+ Nancy | Davolio | Sales Representative | Andrew | Fuller | Vice President, Sales Janet | Leverling | Sales Representative | Andrew | Fuller | Vice President, Sales Margaret | Peacock | Sales Representative | Andrew | Fuller | Vice President, Sales Steven | Buchanan | Sales Manager | Andrew | Fuller | Vice President, Sales Michael | Suyama | Sales Representative | Steven | Buchanan | Sales Manager Robert | King | Sales Representative | Steven | Buchanan | Sales Manager Laura | Callahan | Inside Sales Coordinator | Andrew | Fuller | Vice President, Sales Anne | Dodsworth | Sales Representative | Steven | Buchanan | Sales Manager(8 ROWS) |
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
MATCH (e:Employee)<-[:REPORTS_TO]-(sub)RETURN sub.FirstName, sub.LastName, sub.Title, e.FirstName, e.LastName, e.Title +--------------------------------------------------------------+| sub.FirstName | sub.LastName | sub.Title | e.FirstName | e.LastName | e.Title |+--------------------------------------------------------------+| "Margaret" | "Peacock" | "Sales Representative" | "Andrew" | "Fuller" | "Vice President, Sales" || "Steven" | "Buchanan" | "Sales Manager" | "Andrew" | "Fuller" | "Vice President, Sales" || "Nancy" | "Davolio" | "Sales Representative" | "Andrew" | "Fuller" | "Vice President, Sales" || "Janet" | "Leverling" | "Sales Representative" | "Andrew" | "Fuller" | "Vice President, Sales" || "Laura" | "Callahan" | "Inside Sales Coordinator" | "Andrew" | "Fuller" | "Vice President, Sales" || "Anne" | "Dodsworth" | "Sales Representative" | "Steven" | "Buchanan" | "Sales Manager" || "Michael" | "Suyama" | "Sales Representative" | "Steven" | "Buchanan" | "Sales Manager" || "Robert" | "King" | "Sales Representative" | "Steven" | "Buchanan" | "Sales Manager" |+-------------------------------------------------------------+8 rows |
Теперь посмотрим, сколько прямых отчетов имеет каждый менеджер:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
SELECT manager."EmployeeID" AS manager, COUNT(e."EmployeeID") AS reportsFROM employees AS managerLEFT JOIN employees AS e ON e."ReportsTo" = manager."EmployeeID"GROUP BY managerORDER BY reports DESC; manager | reports---------+--------- 2 | 5 5 | 3 1 | 0 3 | 0 4 | 0 9 | 0 6 | 0 7 | 0 8 | 0(9 ROWS) |
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
MATCH (e:Employee)OPTIONAL MATCH (e)<-[rel:REPORTS_TO]-(report)RETURN e.EmployeeID AS employee, COUNT(rel) AS reports +--------------------+| employee | reports |+--------------------+| "2" | 5 || "5" | 3 || "8" | 0 || "7" | 0 || "1" | 0 || "4" | 0 || "6" | 0 || "9" | 0 || "3" | 0 |+--------------------+9 rows |
Вещи начинают становиться все интереснее, если мы находим существующие транзитивные отношения отчетности. Я не эксперт в Postgres, но один из способов добиться этого — написать рекурсивный запрос WITH следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
WITH RECURSIVE recursive_employees("EmployeeID", "ReportsTo") AS ( SELECT e."EmployeeID", e."ReportsTo" FROM employees e UNION ALL SELECT e."EmployeeID", e."ReportsTo" FROM employees e, recursive_employees re WHERE e."EmployeeID" = re."ReportsTo")SELECT re."ReportsTo", COUNT(*) AS COUNTFROM recursive_employees AS reWHERE re."ReportsTo" IS NOT NULLGROUP BY re."ReportsTo"; ReportsTo | COUNT-----------+------- 2 | 8 5 | 3(2 ROWS) |
Если есть более простой способ, дайте мне знать в комментариях.
В cypher нам нужно добавить только один символ, ‘*’, после отношения ‘REPORTS_TO’, чтобы заставить его повторяться как можно дальше. Мы также удалим «ФАКУЛЬТАТИВНОЕ МАТЧЕ», чтобы мы возвращали только тех людей, о которых им сообщают:
|
01
02
03
04
05
06
07
08
09
10
|
MATCH (e:Employee)<-[rel:REPORTS_TO*]-(report)RETURN e.EmployeeID AS employee, COUNT(rel) AS reports +--------------------+| employee | reports |+--------------------+| "2" | 8 || "5" | 3 |+--------------------+2 rows |
Теперь мне нужно найти несколько реляционных наборов данных с более сложными запросами, с которыми можно поиграться. Если у вас есть идеи, дайте мне знать.
| Ссылка: | Northwind: поиск прямых / транзитивных отчетов в SQL и Cypher Neo4j от нашего партнера по JCG Марка Нидхэма в блоге Марка Нидхэма . |