• SQLServer图数据库一些优点


        上一篇简要介绍了图数据库的一些基本内容(初识SQL Server2017 图数据库(一)),本篇通过对比关系型一些语法来体现图数据库模式的一些优点,比如查询方便,语句易理解等。

    在图数据库模型上构建查询的优势:

    T-SQL 带给图表查询一些新的语法。在SELECT语句中我们有一些特殊的语句来关联点和边。让我们来演练一些,构建查询语句检索发帖和回复,如下:

    1. 我们检索每个记录的两个部分,发帖和回复,因此我们需要在FROM子句中引用两次ForumPosts’表,这个地方可以采用一些有意义的别名:

        

    FROM dbo.ForumPosts ReplyPost, dbo.ForumPosts RepliedPost
    1. 尽管我们能选择任何别名,但是在处理图对象时最好选择有意义的名字。
    2. 我们需要“posts”之间的关系,而这个关系就是表Reply_to’。语法如下:

        

    FROM dbo.ForumPosts ReplyPost, dbo.Reply_to, dbo.ForumPosts RepliedPost
    1. 在WHERE 子句中,我们需要关联所有的表,用下面这种MATCH语句来实现关联:
    FROM dbo.ForumPosts ReplyPost, dbo.Reply_to, dbo.ForumPosts RepliedPost
    
    WHERE MATCH(ReplyPost-(Reply_to)->RepliedPost)
    1. 这个语法很有意思:“-”破折号表示边的$From_id字段表示关系,然后“->”破折号和大于号用边的$To_id字段表示关系。
    2. 因为知道那个别名有reply,那个别名有replied post,我们可以构建一个查询字段列表:
    FROM dbo.ForumPosts ReplyPost, dbo.Reply_to, dbo.ForumPosts RepliedPost
    
    WHERE MATCH(ReplyPost-(Reply_to)->RepliedPost)
    1. 在关系型模型中相同功能的查询如下:
         
    
    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
    1. 这些查询很相似,当然MATCH的语法更容易理解。
    2. 执行完上面语句查询结果如下:

     

    1. 我们加上写这个回复贴人的名字。需要在FROM子句中添加‘ForumMembers’节点和‘Written_By’这个边。语句如下:
    FROM dbo.ForumPosts ReplyPost, dbo.Reply_to, dbo.ForumPosts RepliedPost, dbo.ForumMembers RepliedMember, Written_By RepliedWritten_By
    1. 还要添加MATCH语句的内部关系:
         
    WHERE MATCH(ReplyPost-(Reply_to)->RepliedPost-(RepliedWritten_by)->RepliedMember)
    1. 这就可以在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)
    1. 在关系型模型中的对应查询如下:
    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
    1. 结果如下所示:

     

    1. 还缺少回复对象的名字。像上面一样增加‘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
    1. 接下来,修改MATCH子句,‘ReplyMember’需要关联‘ReplyPost’,但是如何去处理这个关系而不影响其他关系?需要用不同的方式来实现:
    WHERE MATCH(ReplyMember<-(ReplyWritten_By)-ReplyPost-(Reply_to)->RepliedPost-(RepliedWritten_by)->RepliedMember)
    1. 注意这个符号“<-”与之前的相反方向,但是意义是相同的:一个在边表的$to_id与节点表的关系。
    2. 最终,还需增加写着回复的成员姓名,代码如下:
    -- 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)
    1. 结果集如下:

     

    1. 在关系型查询的对应语句:
    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
    1. 在这个时候,可能在关系型模式里面随着关系的增多读取就会越困难,而在图数据模式中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,
    
               1 as Level, 0 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+1 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,
    
               1 as Level, 0 as ReplyTo
    
         from dbo.ForumPosts RootPosts
    
         where PostId=1  
    
      union all
    
         select $node_id,ReplyPost.PostId, ReplyPost.PostTitle,
    
                Level+1 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=3  -- 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) >1

    回帖个一个帖子多次的成员:

    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(*) >1

    上述两种语句中唯一的不同就是展示结果的聚合。

    总结

      通过上述构建在图数据模式下的查询和关联,对比了常规语句以及在关系模式下的相同查询,不难发现无论是在易读性,逻辑理解上还是在性能上都有很大提高。当然这只是第一个版本,所以难免有很多问题, 下一篇我讲介绍这个版本存在的一部分问题。

  • 相关阅读:
    Javascript位运算符
    自定义控件基础2
    Javascript原型链实现继承
    Javascript如何实现水印效果
    CSS详解position(1)
    Javascript对象冒充实现继承
    Javascript节点类型
    实用技巧chm无法搜索
    Javascript定义类或对象
    深入理解JavaScript系列
  • 原文地址:https://www.cnblogs.com/wenBlog/p/7833971.html
Copyright © 2020-2023  润新知