Рекурсивный запрос отношений - PullRequest
0 голосов
/ 18 мая 2018

Я хочу реализовать графические таблицы для сопоставления иерархии ролей для моего приложения в Azure SQL.График будет выглядеть как дерево, если его выложить.Поскольку родитель может управлять любой ролью, которая находится под ним в дереве.

Итак, у меня есть таблица узлов ролей и таблица границ управления.

Я знаком с запросами первого уровня и второго уровня отношений, однако мне нужно иметь запрос, где я могу поставить любую роль и получить список всех дочерних элементов, которые подпадают под нее.

Я знаком с подобными вещами в NEO4J, но я не нашел документации о том, как это сделать в Azure SQL.

Как мне выполнить рекурсивный запрос для получениявсе дочерние роли дают определенное имя или идентификатор роли?

1 Ответ

0 голосов
/ 19 мая 2018

Это возможно в SQL Server 2017 и Azure SQL DB, используя новые возможности базы данных графов и новое предложение MATCH для моделирования этого типа отношений.К сожалению, в v1 полиморфизм и транзитивное замыкание изначально не включены, но возможны при использовании рекурсивных запросов.Если вы посмотрите на последний запрос, он сохранит введенный вами параметр в качестве менеджера верхнего уровня и перебирает все остальные.

Пример сценария:

USE tempdb
GO

-- NODES
DROP TABLE IF EXISTS dbo.roles

-- EDGES
DROP TABLE IF EXISTS dbo.canManage
DROP TABLE IF EXISTS dbo.isManagedBy
GO

CREATE TABLE dbo.roles (
    roleId      INT PRIMARY KEY,
    roleName    VARCHAR(20) UNIQUE NOT NULL
    ) AS NODE

CREATE TABLE dbo.canManage AS EDGE;
CREATE TABLE dbo.isManagedBy AS EDGE;
GO


-- Populate node table
INSERT INTO dbo.roles ( roleId, roleName )
VALUES
    ( 1, 'CEO' ),
    ( 2, 'VP 1' ),
    ( 3, 'VP 2' ),
    ( 4, 'Sales Manager 1' ),
    ( 5, 'Sales Manager 2' ),
    ( 6, 'Ops Manager 1' ),
    ( 7, 'Ops Manager 2' ),
    ( 8, 'Sales Lead 1' ),
    ( 9, 'Salesperson 1' ),
    ( 10, 'Salesperson 2' ),
    ( 11, 'Salesperson 3' )
GO


-- Populate edge table
INSERT INTO dbo.canManage ( $from_id, $to_id )
SELECT ceo.$node_id, VPs.$node_id
FROM dbo.roles ceo
    CROSS JOIN dbo.roles VPs
WHERE ceo.roleName = 'CEO'
  AND VPs.roleName Like 'VP%'


-- VP 1 manages Sales Managers
INSERT INTO dbo.canManage ( $from_id, $to_id )
SELECT a.$node_id, b.$node_id
FROM dbo.roles a
    CROSS JOIN dbo.roles b
WHERE a.roleName = 'VP 1'
  AND b.roleName Like 'Sales Manager%'


-- VP 2 manages Ops Managers
INSERT INTO dbo.canManage ( $from_id, $to_id )
SELECT a.$node_id, b.$node_id
FROM dbo.roles a
    CROSS JOIN dbo.roles b
WHERE a.roleName = 'VP 2'
  AND b.roleName Like 'Ops Manager%'


-- Sales Manger 1 manages Sales Leads
INSERT INTO dbo.canManage ( $from_id, $to_id )
SELECT a.$node_id, b.$node_id
FROM dbo.roles a
    CROSS JOIN dbo.roles b
WHERE a.roleName = 'Sales Manager 1'
  AND b.roleName Like 'Sales Lead%'


-- Sales Leads 1 manages all salespersons
INSERT INTO dbo.canManage ( $from_id, $to_id )
SELECT a.$node_id, b.$node_id
FROM dbo.roles a
    CROSS JOIN dbo.roles b
WHERE a.roleName = 'Sales Lead 1'
  AND b.roleName Like 'Salesperson%'


-- Create the inverse edge / relationship
INSERT INTO dbo.isManagedBy ( $from_id, $to_id )
SELECT $to_id, $from_id
FROM dbo.canManage 
GO



-- Now write the graph queries:

-- Manages
SELECT FORMATMESSAGE( '%s manages %s', r1.roleName, r2.roleName ) manages
FROM dbo.roles r1, dbo.canManage canManage, dbo.roles r2
WHERE MATCH ( r1-(canManage)->r2 )


-- Same manager
SELECT FORMATMESSAGE( '%s and %s have the same manager %s', r1.roleName, r3.roleName, r2.roleName )
FROM dbo.roles r1, dbo.isManagedBy m1, dbo.roles r2, dbo.isManagedBy m2, dbo.roles r3
WHERE MATCH ( r1-(m1)->r2<-(m2)-r3 )
AND r1.$node_id < r3.$node_id



-- Recursive
-- walk the tree ... CEO manages everyone ...
;WITH cte AS (
SELECT 1 xlevel, r1.roleName manager, r2.roleName managed
FROM dbo.roles r1, dbo.canManage canManage, dbo.roles r2
WHERE MATCH ( r1-(canManage)->r2 )
AND r1.roleName = 'CEO'

UNION ALL

SELECT c.xlevel + 1, r1.roleName, r2.roleName
FROM cte c, dbo.roles r1, dbo.canManage canManage, dbo.roles r2
WHERE c.managed = r1.roleName
  AND MATCH ( r1-(canManage)->r2 )
)
SELECT *
FROM cte
ORDER BY xlevel, manager, managed



;WITH cte AS (
SELECT 1 xlevel, r1.roleName manager, r2.roleName managed
FROM dbo.roles r1, dbo.canManage canManage, dbo.roles r2
WHERE MATCH ( r1-(canManage)->r2 )
AND r1.roleName = 'CEO'

UNION ALL

SELECT c.xlevel + 1, c.manager, r2.roleName
FROM cte c, dbo.roles r1, dbo.canManage canManage, dbo.roles r2
WHERE c.managed = r1.roleName
  AND MATCH ( r1-(canManage)->r2 )
)
SELECT *
FROM cte
ORDER BY xlevel, manager, managed
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...