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

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

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

在关系型查询的对应语句:
FROM Forum.ForumPosts ReplyPost,Forum.ForumMembers RepliedMember,Forum.ForumMembers ReplyMember
WHERE ReplyPost.PostId =<span style="color: #000000">RepliedPost.ReplyTo
and RepliedPost.OwnerId</span>=<span style="color: #000000">RepliedMember.MemberId
and ReplyPost.OwnerId</span>=ReplyMember.MemberId</pre>
<ol start="21">
在这个时候,可能在关系型模式里面随着关系的增多读取就会越困难,而在图数据模式中MATCH子句相对就容易很多。让我们看一下在图数据模式中一些有趣又有用的地方。
统计每篇帖子的回复数
</span><span style="color: #0000ff">as</span><span style="color: #000000"> TotalReplies
FROM dbo.ForumPosts ReplyPost,Reply_To,dbo.ForumPosts RepliedPost
WHERE MATCH(ReplyPost -(Reply_To)->RepliedPost)
<p align="left">在这个语句中我们统计了每一篇回复的数量,但是仅仅在一个层面中,并不是在整个回复的树结构里面。
<p align="left">根贴(主贴)的列表
<p align="left">我们通过下面不使用MATCH的语句得到所有的根贴:
<div class="cnblogs_code">
FROM dbo.ForumPosts Post1
WHERE $node_id not <span style="color: #0000ff">in (<span style="color: #0000ff">select $from_id <span style="color: #0000ff">from dbo.Reply_To
<p align="left">MATCH语法只是允许我们关联三个或者更多的实体(比如两个节点和一个关系)。当我们只想关联其中两个的时候,只需要一个常规的连接或者子查询。如上面的语句一样。
<p align="left">在结果中添加‘Level’字段
<p align="left">添加一个‘Level’字段,显示树结构。在T-SQL中有一个简单的语法,叫做CTE实现递归。但是有一个问题,不能使用MATCH语法在一个派生表上,此时可以使用CTE。如果有必要,可以在CTE中使用MATCH,但是反之就不行了,有这样的限制。下面展示一下使用常规的关系仅仅使用CTE来迭代,代码如下:
<div class="cnblogs_code">
with root ( <span style="color: #0000ff">select $node_id <span style="color: #0000ff">as <span style="color: #000000"> node_id,RootPosts.PostId,RootPosts.PostTitle,<span style="color: #800080">1 <span style="color: #0000ff">as Level,<span style="color: #800080">0 <span style="color: #0000ff">as<span style="color: #000000"> ReplyTo
</span><span style="color: #0000ff">from</span><span style="color: #000000"> dbo.ForumPosts RootPosts
</span><span style="color: #0000ff">where</span> $node_id not <span style="color: #0000ff">in</span> (<span style="color: #0000ff">select</span> $from_id <span style="color: #0000ff">from</span><span style="color: #000000"> dbo.reply_to)
union all
</span><span style="color: #0000ff">select</span><span style="color: #000000"> $node_id,ReplyPost.PostId,ReplyPost.PostTitle,Level</span>+<span style="color: #800080">1</span> <span style="color: #0000ff">as</span> [Level],root.PostId <span style="color: #0000ff">as</span><span style="color: #000000"> ReplyTo
</span><span style="color: #0000ff">from</span><span style="color: #000000"> dbo.ForumPosts ReplyPost,reply_to,root
</span><span style="color: #0000ff">where</span> ReplyPost.$node_id=<span style="color: #000000">reply_to.$from_id
and root.node_id</span>=<span style="color: #000000">reply_to.$to_id
)
<span style="color: #0000ff">select<span style="color: #000000"> PostId,PostTitle,Level,ReplyTo
<span style="color: #0000ff">from root
<p align="left">?
<p align="left">检索一个帖子中的所有回复
<p align="left">使用CTE递归语法,我们可以用一种树结构检索一个帖子的所有回复。如果使用常规的语法不能在检索帖子1的时候检索贴子3,因为3是对2的回复,而2是对1的回复。使用CTE.当查询帖子1的所有回复时能检索贴子3。代码如下:
<div class="cnblogs_code">
with root ( <span style="color: #0000ff">select $node_id <span style="color: #0000ff">as<span style="color: #000000"> node_id,<span style="color: #800080">0 <span style="color: #0000ff">as<span style="color: #000000"> ReplyTo
</span><span style="color: #0000ff">from</span><span style="color: #000000"> dbo.ForumPosts RootPosts
</span><span style="color: #0000ff">where</span> PostId=<span style="color: #800080">1</span><span style="color: #000000">
union all
</span><span style="color: #0000ff">select</span><span style="color: #000000"> $node_id,ReplyTo
<span style="color: #0000ff">from root
<p align="left">我们也可以反过来做,在树状结构中按顺序检索所有父贴。由于CTE不支持OUTER join,所以要在外部添加,代码如下:
<div class="cnblogs_code">
with root ( <span style="color: #0000ff">select LeafPost.$node_id <span style="color: #0000ff">as<span style="color: #000000"> node_id,LeafPost.PostId,LeafPost.PostTitle
</span><span style="color: #0000ff">from</span><span style="color: #000000"> dbo.ForumPosts LeafPost
</span><span style="color: #0000ff">where</span> LeafPost.PostId=<span style="color: #800080">3</span> --<span style="color: #000000"> Single post
union all
</span><span style="color: #0000ff">select</span> RepliedPost.$node_id <span style="color: #0000ff">as</span><span style="color: #000000"> node_id,RepliedPost.PostId,RepliedPost.PostTitle
</span><span style="color: #0000ff">from</span><span style="color: #000000"> dbo.ForumPosts RepliedPost,Reply_to,root
</span><span style="color: #0000ff">where</span> root.node_id=<span style="color: #000000">Reply_to.$from_id
and Reply_to.$to_id</span>=<span style="color: #000000">RepliedPost.$node_id
)
<span style="color: #0000ff">select<span style="color: #000000"> root.PostId,root.PostTitle,RepliedPost.PostId ParentPostId
<span style="color: #0000ff">from<span style="color: #000000"> root
left join reply_to
on root.node_id</span>=<span style="color: #000000">reply_to.$from_id
left join dbo.ForumPosts RepliedPost
on reply_to.$to_id</span>=RepliedPost.$node_id</pre>
检索一个用户所有帖子
--SELECT distinct RepliedPost.PostID,RepliedPost.PostBody
FROM dbo.ForumPosts ReplyPost,dbo.ForumMembers Members,Written_By
WHERE MATCH(Members<-(Written_By)-ReplyPost-(Reply_To)-><span style="color: #000000">RepliedPost)
and Members.MemberName=<span style="color: #800000">'<span style="color: #800000">Peter<span style="color: #800000">'
--<span style="color: #000000"> Peter发的所有帖子
SELECT ReplyPost.PostID,ReplyPost.PostBody,RepliedPost.PostId ReplyTo
FROM dbo.ForumPosts ReplyPost,Written_By
WHERE MATCH(Members<-(Written_By)-ReplyPost-(Reply_To)-><span style="color: #000000">RepliedPost)
and Members.MemberName=<span style="color: #800000">'<span style="color: #800000">Peter<span style="color: #800000">'
<p align="left">或许你注意到上面两个查询的不同,就是在展示字段上是否使用DISTINCT。这个去重是因为Peter回复同一个帖子可以超过一次。
<p align="left">在模型中检索Likes(点赞)
<p align="left">这个查询是有意思的:‘Likes’边是成员和发帖表的关系。每一个关系都是唯一的,并不受其他关系影响。代码如下:
<div class="cnblogs_code">
--SELECT Post.PostID,Post.PostTitle,Member.MemberName
FROM dbo.ForumPosts Post,Likes,dbo.ForumMembers Member
WHERE MATCH(Member -(Likes)-><span style="color: #000000">Post)
--<span style="color: #000000"> 点赞的人或者被人点赞
SELECT Member.MemberId,Member.MemberName LikeMember,LikedMember.MemberName LikedMemberName
FROM dbo.ForumMembers Member,dbo.ForumMembers LikedMember
WHERE MATCH(Member-(Likes)->LikedMember)
<p align="left">还可以很容易地聚合信息,以获得每个帖子或每个成员的总的Likes。
<div class="cnblogs_code">
--<span style="color: #0000ff">select<span style="color: #000000"> Post.PostId,count(*<span style="color: #000000">) totalLikes
<span style="color: #0000ff">from<span style="color: #000000"> dbo.ForumPosts Post,dbo.ForumMembers Members
<span style="color: #0000ff">where Match(Members-(Likes)-><span style="color: #000000">Post)
group by PostId,PostTitle
--每个成员总的点赞数<span style="color: #0000ff">select<span style="color: #000000"> LikedMembers.MemberId,LikedMembers.MemberName,count(*<span style="color: #000000">) totalLikes
<span style="color: #0000ff">from<span style="color: #000000"> dbo.ForumMembers Members,dbo.ForumMembers LikedMembers
<span style="color: #0000ff">where Match(Members-(Likes)-><span style="color: #000000">LikedMembers)
group by LikedMembers.MemberId,LikedMembers.MemberName
<p align="left">用户点赞并且回复帖子
<p align="left">我们也可以创建一些更有趣的查询,例如,查找这些点赞并回复的人,如下:
<div class="cnblogs_code">
FROM dbo.ForumPosts LikedPost,dbo.ForumPosts ReplyPost,dbo.ForumMembers Member,Written_By
WHERE MATCH(Member -(Likes)->LikedPost<-(Reply_To)-ReplyPost-(Written_By)->Member)
<p align="left">注意,对于‘Member’节点使用了两次在同一个MATCH表达式中。这形成了一种过滤:点赞并且有回复的成员,需要在‘LikedPost’和‘ReplyPost’中都有记录才可以。
<p align="left">那么在关系型模式中代码如下:
<div class="cnblogs_code">
<span style="color: #0000ff">from<span style="color: #000000"> Forum.Likes Likes,Forum.ForumPosts Posts,Forum.ForumMembers Members
<span style="color: #0000ff">where Likes.MemberId=<span style="color: #000000">Posts.OwnerId
and Posts.ReplyTo=<span style="color: #000000">Likes.PostId
and Members.MemberId=Likes.MemberId
<p align="left">看起来这种写法更难理解和读懂。
<p align="left">回帖给多个帖子的成员
<div class="cnblogs_code">
FROM dbo.ForumPosts ReplyPost,Written_By,dbo.ForumMembers Members
WHERE MATCH(Members <-(Written_By)-ReplyPost-(Reply_To)-><span style="color: #000000">RepliedPost)
GROUP BY MemberId,Members.MemberName
Having Count(RepliedPost.PostId) ><span style="color: #800080">1
<p align="left">回帖个一个帖子多次的成员:
<div class="cnblogs_code">
*) FROM dbo.ForumPosts ReplyPost,dbo.ForumMembers Members
WHERE MATCH(Members <-(Written_By)-ReplyPost-(Reply_To)-><span style="color: #000000">RepliedPost)
GROUP BY MemberId,MemberName,RepliedPost.PostId
Having count( *) ><span style="color: #800080">1
<p align="left">上述两种语句中唯一的不同就是展示结果的聚合。
<h3 align="left">总结
通过上述构建在图数据模式下的查询和关联,对比了常规语句以及在关系模式下的相同查询,不难发现无论是在易读性,逻辑理解上还是在性能上都有很大提高。当然这只是第一个版本,所以难免有很多问题, 下一篇我讲介绍这个版本存在的一部分问题。 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|