SQLServer图数据库一些优点

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS SQL Server,基础系列 2核4GB
简介: 原文:SQLServer图数据库一些优点    上一篇简要介绍了图数据库的一些基本内容(初识SQL Server2017 图数据库(一)),本篇通过对比关系型一些语法来体现图数据库模式的一些优点,比如查询方便,语句易理解等。
原文: 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

 

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

总结

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

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
3月前
|
SQL 数据库 数据安全/隐私保护
数据库数据恢复——sql server数据库被加密的数据恢复案例
SQL server数据库数据故障: SQL server数据库被加密,无法使用。 数据库MDF、LDF、log日志文件名字被篡改。 数据库备份被加密,文件名字被篡改。
|
10月前
|
SQL 数据库
数据库数据恢复—SQL Server数据库报错“错误823”的数据恢复案例
SQL Server附加数据库出现错误823,附加数据库失败。数据库没有备份,无法通过备份恢复数据库。 SQL Server数据库出现823错误的可能原因有:数据库物理页面损坏、数据库物理页面校验值损坏导致无法识别该页面、断电或者文件系统问题导致页面丢失。
207 13
数据库数据恢复—SQL Server数据库报错“错误823”的数据恢复案例
|
4月前
|
SQL 数据库连接 Linux
数据库编程:在PHP环境下使用SQL Server的方法。
看看你吧,就像一个调皮的小丑鱼在一片广阔的数据库海洋中游弋,一路上吞下大小数据如同海中的珍珠。不管有多少难关,只要记住这个流程,剩下的就只是探索未知的乐趣,沉浸在这个充满挑战的数据库海洋中。
104 16
|
5月前
|
SQL 数据库
数据库数据恢复—SQL Server报错“错误 823”的数据恢复案例
SQL Server数据库附加数据库过程中比较常见的报错是“错误 823”,附加数据库失败。 如果数据库有备份则只需还原备份即可。但是如果没有备份,备份时间太久,或者其他原因导致备份不可用,那么就需要通过专业手段对数据库进行数据恢复。
|
6月前
|
数据库 Windows
SqlServer数据恢复—SqlServer数据库所在分区损坏的数据恢复案例
一块硬盘上存放的SqlServer数据库,windows server操作系统+NTFS文件系统。由于误操作导致分区损坏,需要恢复硬盘里的SqlServer数据库数据。
|
8月前
|
SQL 存储 Linux
从配置源到数据库初始化一步步教你在CentOS 7.9上安装SQL Server 2019
【11月更文挑战第16天】本文介绍了在 CentOS 7.9 上安装 SQL Server 2019 的详细步骤,包括配置系统源、安装 SQL Server 2019 软件包以及数据库初始化,确保 SQL Server 正常运行。
371 4
|
8月前
|
SQL 存储 Linux
从配置源到数据库初始化一步步教你在CentOS 7.9上安装SQL Server 2019
【11月更文挑战第8天】本文介绍了在 CentOS 7.9 上安装 SQL Server 2019 的详细步骤,包括系统准备、配置安装源、安装 SQL Server 软件包、运行安装程序、初始化数据库以及配置远程连接。通过这些步骤,您可以顺利地在 CentOS 系统上部署和使用 SQL Server 2019。
341 1
|
8月前
|
SQL 存储 Linux
从配置源到数据库初始化一步步教你在CentOS 7.9上安装SQL Server 2019
【11月更文挑战第7天】本文介绍了在 CentOS 7.9 上安装 SQL Server 2019 的详细步骤,包括系统要求检查与准备、配置安装源、安装 SQL Server 2019、配置 SQL Server 以及数据库初始化(可选)。通过这些步骤,你可以成功安装并初步配置 SQL Server 2019,进行简单的数据库操作。
282 1
|
9月前
|
存储 数据挖掘 数据库
数据库数据恢复—SQLserver数据库ndf文件大小变为0KB的数据恢复案例
一个运行在存储上的SQLServer数据库,有1000多个文件,大小几十TB。数据库每10天生成一个NDF文件,每个NDF几百GB大小。数据库包含两个LDF文件。 存储损坏,数据库不可用。管理员试图恢复数据库,发现有数个ndf文件大小变为0KB。 虽然NDF文件大小变为0KB,但是NDF文件在磁盘上还可能存在。可以尝试通过扫描&拼接数据库碎片来恢复NDF文件,然后修复数据库。
|
10月前
|
SQL 关系型数据库 MySQL
创建包含MySQL和SQLServer数据库所有字段类型的表的方法
创建一个既包含MySQL又包含SQL Server所有字段类型的表是一个复杂的任务,需要仔细地比较和转换数据类型。通过上述方法,可以在两个数据库系统之间建立起相互兼容的数据结构,为数据迁移和同步提供便利。这一过程不仅要考虑数据类型的直接对应,还要注意特定数据类型在不同系统中的表现差异,确保数据的一致性和完整性。
137 4

热门文章

最新文章