SQLServer图数据库一些优点
上一篇简要介绍了图数据库的一些基本内容(初识SQL Server2017 图数据库(一)),本篇通过对比关系型一些语法来体现图数据库模式的一些优点,比如查询方便,语句易理解等。
在图数据库模型上构建查询的优势:
T-SQL 带给图表查询一些新的语法。在SELECT语句中我们有一些特殊的语句来关联点和边。让我们来演练一些,构建查询语句检索发帖和回复,如下:
- 我们检索每个记录的两个部分,发帖和回复,因此我们需要在FROM子句中引用两次ForumPosts’表,这个地方可以采用一些有意义的别名:
FROM dbo.ForumPosts ReplyPost, dbo.ForumPosts RepliedPost
- 尽管我们能选择任何别名,但是在处理图对象时最好选择有意义的名字。
- 我们需要“posts”之间的关系,而这个关系就是表Reply_to’。语法如下:
FROM dbo.ForumPosts ReplyPost, dbo.Reply_to, dbo.ForumPosts RepliedPost
- 在WHERE 子句中,我们需要关联所有的表,用下面这种MATCH语句来实现关联:
FROM dbo.ForumPosts ReplyPost, dbo.Reply_to, dbo.ForumPosts RepliedPost WHERE MATCH(ReplyPost-(Reply_to)->RepliedPost)
- 这个语法很有意思:“-”破折号表示边的$From_id字段表示关系,然后“->”破折号和大于号用边的$To_id字段表示关系。
- 因为知道那个别名有reply,那个别名有replied post,我们可以构建一个查询字段列表:
FROM dbo.ForumPosts ReplyPost, dbo.Reply_to, dbo.ForumPosts RepliedPost WHERE MATCH(ReplyPost-(Reply_to)->RepliedPost)
- 在关系型模型中相同功能的查询如下:
select RepliedPost.PostId,RepliedPost.PostTitle, ReplyPost.PostId as ReplyId, ReplyPost.PostTitle as ReplyTitle from Forum.ForumPosts ReplyPost, Forum.ForumPosts RepliedPost where ReplyPost.PostId=RepliedPost.ReplyTo
- 这些查询很相似,当然MATCH的语法更容易理解。
- 执行完上面语句查询结果如下:

- 我们加上写这个回复贴人的名字。需要在FROM子句中添加‘ForumMembers’节点和‘Written_By’这个边。语句如下:
FROM dbo.ForumPosts ReplyPost, dbo.Reply_to, dbo.ForumPosts RepliedPost, dbo.ForumMembers RepliedMember, Written_By RepliedWritten_By
- 还要添加MATCH语句的内部关系:
WHERE MATCH(ReplyPost-(Reply_to)->RepliedPost-(RepliedWritten_by)->RepliedMember)
- 这就可以在SELECT列表中添加回帖人的名字,最终的查询如下:
-- Posts 、members 和replies
   SELECT RepliedPost.PostId,RepliedPost.PostTitle,RepliedMember.MemberName,
   ReplyPost.PostId as ReplyId, ReplyPost.PostTitle as ReplyTitle
      FROM dbo.ForumPosts ReplyPost, dbo.Reply_to, dbo.ForumPosts RepliedPost,
        dbo.ForumMembers RepliedMember, Written_By RepliedWritten_By
   WHERE MATCH(ReplyPost-(Reply_to)->RepliedPost-(RepliedWritten_by)->RepliedMember)
- 在关系型模型中的对应查询如下:
SELECT RepliedPost.PostId,RepliedPost.PostTitle,
   ReplyPost.PostId as ReplyId, ReplyPost.PostTitle as ReplyTitle,
   RepliedMember.MemberName
  FROM Forum.ForumPosts ReplyPost, Forum.ForumPosts RepliedPost,
   Forum.ForumMembers RepliedMember
  WHERE ReplyPost.PostId=RepliedPost.ReplyTo
        and RepliedPost.OwnerId=RepliedMember.MemberId
- 结果如下所示:

- 还缺少回复对象的名字。像上面一样增加‘ForumMembers’ 和 ‘Written_By’在FROM子句中:
From dbo.ForumPosts ReplyPost, dbo.Reply_to, dbo.ForumPosts RepliedPost, dbo.ForumMembers RepliedMember, Written_By RepliedWritten_By, dbo.ForumMembers ReplyMember, Written_By ReplyWritten_By
- 接下来,修改MATCH子句,‘ReplyMember’需要关联‘ReplyPost’,但是如何去处理这个关系而不影响其他关系?需要用不同的方式来实现:
WHERE MATCH(ReplyMember<-(ReplyWritten_By)-ReplyPost-(Reply_to)->RepliedPost-(RepliedWritten_by)->RepliedMember)
- 注意这个符号“<-”与之前的相反方向,但是意义是相同的:一个在边表的$to_id与节点表的关系。
- 最终,还需增加写着回复的成员姓名,代码如下:
-- Posts and members and their replies and members
   SELECT RepliedPost.PostId, RepliedPost.PostTitle,RepliedMember.MemberName,
   ReplyPost.PostId as ReplyId, ReplyPost.PostTitle as ReplyTitle,
   ReplyMember.MemberName [ReplyMemberName]
      FROM dbo.ForumPosts ReplyPost, dbo.Reply_to, dbo.ForumPosts RepliedPost,
        dbo.ForumMembers RepliedMember, Written_By RepliedWritten_By,
        dbo.ForumMembers ReplyMember, Written_By ReplyWritten_By
      WHERE MATCH(ReplyMember<-(ReplyWritten_By)-ReplyPost-(Reply_to)->RepliedPost-(RepliedWritten_by)->RepliedMember)
- 结果集如下:

- 在关系型查询的对应语句:
SELECT RepliedPost.PostId,RepliedPost.PostTitle,
         RepliedMember.MemberName, ReplyPost.PostId as ReplyId,
      ReplyPost.PostTitle as ReplyTitle, ReplyMember.MemberName
  FROM Forum.ForumPosts ReplyPost, Forum.ForumPosts RepliedPost,
       Forum.ForumMembers RepliedMember, Forum.ForumMembers ReplyMember
  WHERE ReplyPost.PostId=RepliedPost.ReplyTo
        and RepliedPost.OwnerId=RepliedMember.MemberId
        and ReplyPost.OwnerId=ReplyMember.MemberId
- 在这个时候,可能在关系型模式里面随着关系的增多读取就会越困难,而在图数据模式中MATCH子句相对就容易很多。让我们看一下在图数据模式中一些有趣又有用的地方。
统计每篇帖子的回复数
  SELECT distinct RepliedPost.PostID,RepliedPost.PostTitle,
          RepliedPost.PostBody,
          count(ReplyPost.PostID) over(partition by RepliedPost.PostID)
              as TotalReplies
   FROM dbo.ForumPosts ReplyPost, Reply_To, dbo.ForumPosts RepliedPost
   WHERE MATCH(ReplyPost-(Reply_To)->RepliedPost)
在这个语句中我们统计了每一篇回复的数量,但是仅仅在一个层面中,并不是在整个回复的树结构里面。
根贴(主贴)的列表
我们通过下面不使用MATCH的语句得到所有的根贴:
SELECT Post1.PostId,Post1.PostTitle FROM dbo.ForumPosts Post1 WHERE $node_id not in (select $from_id from dbo.Reply_To
MATCH语法只是允许我们关联三个或者更多的实体(比如两个节点和一个关系)。当我们只想关联其中两个的时候,只需要一个常规的连接或者子查询。如上面的语句一样。
在结果中添加‘Level’字段
添加一个‘Level’字段,显示树结构。在T-SQL中有一个简单的语法,叫做CTE实现递归。但是有一个问题,不能使用MATCH语法在一个派生表上,此时可以使用CTE。如果有必要,可以在CTE中使用MATCH,但是反之就不行了,有这样的限制。下面展示一下使用常规的关系仅仅使用CTE来迭代,代码如下:
 with root as
  ( select $node_id as node_id,RootPosts.PostId,
           RootPosts.PostTitle,
            as Level,  as ReplyTo
     from dbo.ForumPosts RootPosts
     where $node_id not in (select $from_id from dbo.reply_to)
  union all
     select $node_id,ReplyPost.PostId, ReplyPost.PostTitle,
            Level+ as [Level], root.PostId as ReplyTo
     from dbo.ForumPosts ReplyPost, reply_to, root
     where ReplyPost.$node_id=reply_to.$from_id
           and root.node_id=reply_to.$to_id
  )
  select PostId,PostTitle, Level, ReplyTo
  from root
检索一个帖子中的所有回复
使用CTE递归语法,我们可以用一种树结构检索一个帖子的所有回复。如果使用常规的语法不能在检索帖子1的时候检索贴子3,因为3是对2的回复,而2是对1的回复。使用CTE.当查询帖子1的所有回复时能检索贴子3。代码如下:
 with root as
  ( select $node_id as node_id,RootPosts.PostId,RootPosts.PostTitle,
            as Level,  as ReplyTo
     from dbo.ForumPosts RootPosts
     where PostId=  
  union all
     select $node_id,ReplyPost.PostId, ReplyPost.PostTitle,
            Level+ as [Level],root.PostId as ReplyTo
     from dbo.ForumPosts ReplyPost, reply_to, root
     where ReplyPost.$node_id=reply_to.$from_id
           and root.node_id=reply_to.$to_id
  )
  select PostId,PostTitle, Level, ReplyTo
  from root
我们也可以反过来做,在树状结构中按顺序检索所有父贴。由于CTE不支持OUTER join,所以要在外部添加,代码如下:
with root as
  ( select LeafPost.$node_id as node_id,LeafPost.PostId,
           LeafPost.PostTitle
     from dbo.ForumPosts LeafPost
     where LeafPost.PostId=  -- Single post
  union all
     select RepliedPost.$node_id as node_id,RepliedPost.PostId,
            RepliedPost.PostTitle
     from dbo.ForumPosts RepliedPost, Reply_to, root
     where root.node_id=Reply_to.$from_id
           and Reply_to.$to_id=RepliedPost.$node_id
  )
  select root.PostId,root.PostTitle,
         RepliedPost.PostId ParentPostId
  from root
  left join reply_to
       on root.node_id=reply_to.$from_id
  left join dbo.ForumPosts RepliedPost
       on reply_to.$to_id=RepliedPost.$node_id
检索一个用户所有帖子
查询一个用所有的信息,与帖子不同,这不需要树,要简单不少:
--  Peter回复的所有帖子
   SELECT distinct RepliedPost.PostID,RepliedPost.PostTitle,
           RepliedPost.PostBody
   FROM dbo.ForumPosts ReplyPost, Reply_To, dbo.ForumPosts RepliedPost,
        dbo.ForumMembers Members,Written_By
   WHERE MATCH(Members<-(Written_By)-ReplyPost-(Reply_To)->RepliedPost)
   and Members.MemberName='Peter'
  -- Peter发的所有帖子
   SELECT ReplyPost.PostID,ReplyPost.PostTitle,ReplyPost.PostBody,
          RepliedPost.PostId ReplyTo
   FROM dbo.ForumPosts ReplyPost, Reply_To, dbo.ForumPosts RepliedPost,
        dbo.ForumMembers Members,Written_By
   WHERE MATCH(Members<-(Written_By)-ReplyPost-(Reply_To)->RepliedPost)
   and Members.MemberName='Peter'
或许你注意到上面两个查询的不同,就是在展示字段上是否使用DISTINCT。这个去重是因为Peter回复同一个帖子可以超过一次。
在模型中检索Likes(点赞)
这个查询是有意思的:‘Likes’边是成员和发帖表的关系。每一个关系都是唯一的,并不受其他关系影响。代码如下:
--点赞的帖子或者被别人点赞的帖子。
  SELECT Post.PostID,Post.PostTitle,Member.MemberName
  FROM   dbo.ForumPosts Post, Likes,
         dbo.ForumMembers Member
  WHERE MATCH(Member-(Likes)->Post)
  -- 点赞的人或者被人点赞
  SELECT Member.MemberId,Member.MemberName LikeMember,
         LikedMember.MemberName LikedMemberName
  FROM dbo.ForumMembers Member, Likes, dbo.ForumMembers LikedMember
  WHERE MATCH(Member-(Likes)->LikedMember)
还可以很容易地聚合信息,以获得每个帖子或每个成员的总的Likes。
--每个帖子总的likes
  select Post.PostId,Post.PostTitle,
         count(*) totalLikes
  from dbo.ForumPosts Post,Likes,
       dbo.ForumMembers Members
  where Match(Members-(Likes)->Post)
  group by PostId,PostTitle
  --每个成员总的点赞数select LikedMembers.MemberId,LikedMembers.MemberName,
         count(*) totalLikes
  from dbo.ForumMembers Members,Likes,
       dbo.ForumMembers LikedMembers
  where Match(Members-(Likes)->LikedMembers)
  group by LikedMembers.MemberId,
           LikedMembers.MemberName
用户点赞并且回复帖子
我们也可以创建一些更有趣的查询,例如,查找这些点赞并回复的人,如下:
  SELECT Member.MemberName,Member.Memberid,
         LikedPost.PostId,LikedPost.PostTitle,
         ReplyPost.PostTitle ReplyTitle
  FROM dbo.ForumPosts LikedPost, Reply_To, dbo.ForumPosts ReplyPost,
       Likes, dbo.ForumMembers Member, Written_By
  WHERE MATCH(Member-(Likes)->LikedPost<-(Reply_To)-ReplyPost-(Written_By)->Member)
注意,对于‘Member’节点使用了两次在同一个MATCH表达式中。这形成了一种过滤:点赞并且有回复的成员,需要在‘LikedPost’和‘ReplyPost’中都有记录才可以。
那么在关系型模式中代码如下:
select Likes.MemberId,Members.MemberName
  from Forum.Likes Likes, Forum.ForumPosts Posts,
       Forum.ForumMembers Members
  where Likes.MemberId=Posts.OwnerId
   and Posts.ReplyTo=Likes.PostId
   and Members.MemberId=Likes.MemberId
看起来这种写法更难理解和读懂。
回帖给多个帖子的成员
SELECT Members.MemberId, Members.MemberName,
         Count(distinct RepliedPost.PostId) as Total
  FROM   dbo.ForumPosts ReplyPost, Reply_To, dbo.ForumPosts RepliedPost,
         Written_By,dbo.ForumMembers Members
  WHERE  MATCH(Members<-(Written_By)-ReplyPost-(Reply_To)->RepliedPost)
  GROUP BY MemberId, Members.MemberName
  Having Count(RepliedPost.PostId) >
回帖个一个帖子多次的成员:
SELECT Members.MemberId, Members.MemberName,
         RepliedPost.PostId RepliedId,count(*) as TotalReplies
  FROM   dbo.ForumPosts ReplyPost, Reply_To, dbo.ForumPosts RepliedPost,
       Written_By,dbo.ForumMembers Members
  WHERE MATCH(Members<-(Written_By)-ReplyPost-(Reply_To)->RepliedPost)
  GROUP BY MemberId,MemberName,RepliedPost.PostId
  Having count(*) >
上述两种语句中唯一的不同就是展示结果的聚合。
总结
通过上述构建在图数据模式下的查询和关联,对比了常规语句以及在关系模式下的相同查询,不难发现无论是在易读性,逻辑理解上还是在性能上都有很大提高。当然这只是第一个版本,所以难免有很多问题, 下一篇我讲介绍这个版本存在的一部分问题。
SQLServer图数据库一些优点的更多相关文章
- 初识SQL Server2017 图数据库(一)
		背景: 图数据库对于表现和遍历复杂的实体之间关系是很有效果的.而这些在传统的关系型数据库中尤其是对于报表而言很难实现.如果把传统关系型数据库比做火车的话,那么到现在大数据时代,图数据库可比做高铁.它已 ... 
- SQLServer 2005 数据库定阅复制实现双机热备(主要是sharepoint 内容数据库)
		原文:SQLServer 2005 数据库定阅复制实现双机热备(主要是sharepoint 内容数据库) 场景 公司最近的sharepoint的数据库服务器老是出问题,并且在一旦出现问题,就导致无法正 ... 
- JanusGraph : 图和图数据库的简介
		JanusGraph:图数据库系统简介 图(graph)是<数据结构>课中第一次接触到的一个概念,它是一种用来描述现实世界中个体和个体之间网络关系的数据结构. 为了在计算机中存储图,< ... 
- Neo4j图数据库从入门到精通
		目录 第一章:介绍 Neo4j是什么 Neo4j的特点 Neo4j的优点 第二章:安装 1.环境 2.下载 3.开启远程访问 4.启动 第三章:CQL 1.CQL简介 2.Neo4j CQL命令/条款 ... 
- Neo4j图数据库从入门到精通(转)
		add by zhj: 转载时,目录没整理好,还会跳转到原文 其实RDB也可以存储多对多的关系,使用的是中间表,GDB使用的是边,RDB中的实体存储在数据表,而GDB存储在节点.两者使用的底层技术不同 ... 
- NEO4J 图数据库使用APOC数据导入
		Neo4j 数据导入 一.安装与部署 直接在官网下载安装包安装,解压即可. 二.下载相应的jar包 apoc 包下载链接: https://github.com/neo4j-contrib/ne ... 
- 【图数据库】史上超全面的Neo4j使用指南
		转自:https://cloud.tencent.com/developer/article/1336299 在这篇文章中: 第一章:介绍 Neo4j是什么 Neo4j的特点 Neo4j的优点 第二章 ... 
- 图数据库 Nebula Graph TTL 特性
		导读 身处在现在这个大数据时代,我们处理的数据量需以 TB.PB, 甚至 EB 来计算,怎么处理庞大的数据集是从事数据库领域人员的共同问题.解决这个问题的核心在于,数据库中存储的数据是否都是有效的.有 ... 
- 图数据库 Nebula Graph 是什么
		图数据库(英语:Graph Database)是一个使用图结构进行语义查询的数据库.该系统的关键概念是图,形式上是点 (Node 或者 Vertex) 和边 (Edge 或者 Relationship ... 
随机推荐
- shell脚本示例:计算毫秒级、微秒级时间差
			bash&shell系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.html 有时候需要计算命令的执行时间长度,可以使用time命令,虽然t ... 
- C语言学习之assert
			C语言学习之assert assert (编程术语) 编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式.断言表示为一些布尔表达式,程序员相信 ... 
- SpringBoot系列——EnableScheduling,对定时器的支持
			前言 定时器功能在项目里面往往会用到,比如定时发送邮件.定时释放数据库资源:这里记录一下springboot对定时器的支持的简单实例 cron表达式 开始之前要先介绍一下cron表达式,这里当一下百度 ... 
- 【转载】ASP.NET工具类:文件夹目录Directory操作工具类
			在ASP.NET开发网站的过程中,有时候会涉及到文件夹相关操作,如判断文件夹目录是否存在.删除文件夹目录.创建文件.删除文件.复制文件夹等等.这一批有关文件目录的操作可以通过Directory类.Fi ... 
- 重构——与设计模式的恋情
			慢慢的,我发现,我想和<重构>加深感情不那么容易,于是我就想办法,重构有个好闺蜜<设计模式>,他们青梅竹马两小无猜,行为习性喜好都差不多,要让重构爱上我,我或许可以和设计模式多 ... 
- WPF window 子窗口反馈效果(抖动/阴影渐变)
			当设置了owner的子窗口显示后,点击子窗口外部,需要一种反馈机制(反馈动画). 实现: 1.触发源 每次点击子窗口外部,即母窗口时,事件捕捉如下 HwndSource hwndSource = Pr ... 
- 从零开始学安全(十)●TCP/IP协议栈
			局域网靠mac 地址通信 
- JSJ——主数据类型和引用
			变量有两种:primitive主数据类型和引用. Java注重类型.它不会让你做出把长颈鹿类型变量装进兔子类型变量中这种诡异又危险的举动——如果有人对长颈鹿调用“跳跃”这个方法会发生什么悲剧?并且它也 ... 
- http协议、web服务器、并发服务器(下)
			Web静态服务器-5-非堵塞模式 单进程非堵塞模型 import socket import time def main(): tcp_socket_server = socket.socket(so ... 
- phpStorm ctrl+左键无法找到类
			场景 在使用phpstrom时,通过commd+鼠标左键的方式找不到该类 报如下异常: Cannot load settings from file ‘/*/.idea/xdp_stat.iml': ... 
