SQL Server2017新增了一个新功能叫做图形数据库。图形指的拓扑图形,是一些Node表和Edge表的合集,Node对应关系数据库中的实体,比如一个人、一个岗位等,Edge表指示Node之前的关系,比如张三在经理岗位。图形表比较适合用来表示这种实体与实体之间有明显关联关系的情况,比如学生和课程,学生是Node表,选课记录是Edge表。下面以比较常用的角色-任务-操作三层权限管理模型说明图形表的使用。
在SSMS中,可以看到数据库的“表”节点下面有“图形表”这个节点:
创建图形表的语句与普通表大体相同,只是在语句最后加上AS NODE或AS EDGE,表示创建的是Node表还是Edge表。创建用户、角色、任务、操作四张Node表,并插入一些测试数据:
Create Table Roles
(RoleName nvarchar(20) primary key,RoleDesc nvarchar(255)
)
As Node
GoCreate Table Tasks
(TaskName nvarchar(20) primary key,TaskDesc nvarchar(255)
)
As Node
GoCreate Table Opers
(OperName nvarchar(20) primary key,OperDesc nvarchar(255)
)
As Node
GoCreate Table Users
(UserID nvarchar(20) primary key,UserName nvarchar(255)
)
As Node
Go
insert into Roles Values('R001','经理')
insert into Roles Values('R002','库管员')
insert into Roles Values('R003','出纳')
GoInsert into Users Values('U001','张三')
Insert into Users Values('U002','李四')
Insert into Users Values('U003','王五')
GoInsert into Opers Values('C001','操作1')
Insert into Opers Values('C002','操作2')
Insert into Opers Values('C003','操作3')
GoInsert into Tasks Values('T001','任务1')
Insert into Tasks Values('T002','任务2')
Insert into Tasks Values('T003','任务3')
Go
每一个Node表都有一个伪列$node_id,这个列是SQL Server自动添加且自动填充的。$node_id的值是一段json。
可以看到实际上的$node_id字段名后面还有一串16进制数字,查询的时候,使用$node_id作为字段名是可以查询出来的:
反而如果用这个字段的全称$node_id_E42A169EC3FA4F84B5E932FD8B877822,是会报错:Invalid pseudocolumn "$node_id_E42A169EC3FA4F84B5E932FD8B877822".
在SSMS中查看表结构:
还有一个graph_id列,这个列是数据库内部使用,对我们没有用。
再创建表示用户-角色对应关系、角色-任务对应关系、任务-操作对应关系的三张Edge表,并插入数据:
Create Table UserRole As Edge
GoCreate Table RoleTask As Edge
GoCreate Table TaskOper As Edge
GoInsert into UserRole Values((select $node_id from Users where UserID='U001'),(select $node_id from Roles where RoleName='R001'))
Insert into UserRole Values((select $node_id from Users where UserID='U002'),(select $node_id from Roles where RoleName='R002'))
Insert into UserRole Values((select $node_id from Users where UserID='U003'),(select $node_id from Roles where RoleName='R003'))
GoInsert into RoleTask Values((select $node_id from Roles where RoleName='R001'),(select $node_id from Tasks where TaskName='T001'))
Insert into RoleTask Values((select $node_id from Roles where RoleName='R002'),(select $node_id from Tasks where TaskName='T002'))
Insert into RoleTask Values((select $node_id from Roles where RoleName='R003'),(select $node_id from Tasks where TaskName='T003'))
GoInsert into TaskOper Values((select $node_id from Tasks where TaskName='T001'),(select $node_id from Opers where OperName='C001'))
Insert into TaskOper Values((select $node_id from Tasks where TaskName='T001'),(select $node_id from Opers where OperName='C001'))
Insert into TaskOper Values((select $node_id from Tasks where TaskName='T001'),(select $node_id from Opers where OperName='C001'))
Go
上面创建表并没有写包含任何字段,因为Edge表包含三个默认列:$edge_id、$from_id、$to_id,分别表示记录的id值、第一个Node记录的id、第二个Node记录的id。这样,就表示了两个Node之前有了关联关系。同样的,$edge_id也是自动填充。
查询角色R001所有的任务:
Select Tasks.TaskName,Tasks.TaskDesc
From Roles,RoleTask,Tasks
Where Match(Roles-(RoleTask)->Tasks)
And Roles.RoleName='R001'
Match是图形数据库查询的特有语句,使用Node-(Edge)->Node或Ndoe<-(Edge)-Node来表示Node与Node之间有某种关联。具体语法可以参考https://docs.microsoft.com/en-us/sql/t-sql/queries/match-sql-graph。
查询某个用户的所有操作权限:
Select Opers.OperName,Opers.OperDesc
From Users,UserRole,Roles,RoleTask,Tasks,TaskOper,Opers
Where Match(Users-(UserRole)->Roles-(RoleTask)->Tasks-(TaskOper)->Opers)
And Users.UserID='U001'
如果用关系型表的方法,就得用类似如下的方法查询:
select Opers.* from Opers join TaskOper on TaskOper.OperName=Opers.OperName
join RoleTask on RoleTask.TaskName=TaskOper.TaskName
join UserRole on UserRole.RoleName=RoleTask.RoleName
join Users on Users.UserID=UserRole.UserID
where User.UserID='U001'
可以看到图像表的查询写法更简单一些。