初識SQL Server2017 圖數據庫(一)


背景:

  圖數據庫對於表現和遍歷復雜的實體之間關系是很有效果的。而這些在傳統的關系型數據庫中尤其是對於報表而言很難實現。如果把傳統關系型數據庫比做火車的話,那么到現在大數據時代,圖數據庫可比做高鐵。它已成為NoSQL中關注度最高,發展趨勢最明顯的數據庫。伴隨SQL Server 2017的出現,在SQL Server上面有了專門的圖數據庫,那么以往需要其他數據庫或者效率低下地處理這些工作,現在是否可以讓我們容易的實現了那?

  接下來我會用三個篇幅介紹SQLServer 圖數據庫以及它的優缺點。

介紹:

  簡單定義:圖數據庫是NoSQL數據庫的一種類型,它應用圖形理論存儲實體之間的關系信息。圖形數據庫是一種非關系型數據庫,它應用圖形理論存儲實體之間的關系信息。最常見例子就是社會網絡中人與人之間的關系。關系型數據庫用於存儲“關系型”數據的效果並不好,其查詢復雜、緩慢、超出預期,而圖形數據庫的獨特設計恰恰彌補了這個缺陷。

  SQL Server 2017將帶來新的功能之一就是圖數據庫。圖數據庫不像關系型數據庫在一張“圖”內將數據表現為節點,邊和屬性,而是一種抽象的數據類型,通過一組頂點節點、點和邊來表現關系和連接,就像一個纏結的漁網。使我們用簡單的方式來表現和遍歷實體間的關系。圖對象被用來表示復雜的關系。一層就是一個特定的圖,記錄如論壇帖子和回復之間的關系,以及人與人之間的關系。多層有一個根節點(例如,論壇中的帖子和回復),但是多個圖不一定有根節點(例如人們之間的關系)

  本文中,我們一起使用一個論壇數據例子,使用新型的圖模型。也會比較圖和關系型模型的查詢復雜度。

演示環境

  SQL Server 2017 CTP 2.1下載地址: https://www.microsoft.com/en-us/sql-server/sql-server-2017

  使用SSMS 17.0,下載地址: https://docs.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms

創建模型

  下圖是一個關系型實體的模型,以此作為比較:

 

  如果想要比較,可以使用下面的腳本創建,或者直接創建圖模型。但是,需要用SSMS創建一個新的數據庫“GraphExample”。代碼如下:

create database GraphExample
  go
  -- Trying an entire graph model
  use GraphExample
  go
  create schema Forum
  go
  create table Forum.ForumMembers
  (MemberId int not null primary key Identity(1,1),
  MemberName varchar(100))
  go
  create table Forum.ForumPosts
  ([PostID] int not null primary key,
  PostTitle varchar(100),
  PostBody  varchar(100),
  OwnerID int,
  ReplyTo   int)
  go
  Create table Forum.Likes
  (MemberId int,
  PostId int)
  go
  create table Forum.LikeMember
  (MemberId int,
   LikedMemberId int)
   go
  INSERT Forum.ForumMembers values('Mike'),('Carl'),('Paul'),('Christy'),('Jennifer'),('Charlie')
  go
   
  INSERT INTO [Forum].[ForumPosts] 
             (
             [PostID]
             ,[PostTitle]
             ,[PostBody],OwnerID, ReplyTo
                   )
       VALUES
           (4,'Geography','Im Christy from USA',4,null),
             (1,'Intro','Hi There This is Carl',2,null)
  INSERT INTO [Forum].[ForumPosts] 
             (
             [PostID]
             ,[PostTitle]
             ,[PostBody],OwnerID, ReplyTo
                   )
       VALUES
          (8,'Intro','nice to see all here!',1,1),
          (7,'Intro','I''m Mike from Argentina',1,1),
           (6,'Re:Geography','I''m Mike from Argentina',1,4),
          (5,'Re:Geography','I''m Jennifer from Brazil',5,4),
                (3,'Re: Intro','Hey Paul This is Christy',4,2),
                   (2,'Intro','Hello I''m Paul',3,1)
  go
  INSERT Forum.Likes VALUES (1,4),
   (2,7),
   (2,8),
   (2,2),
   (4,5),
   (4,6),
   (1,2),
   (3,7),
   (3,8),
       (5,4)
  go
  Insert Forum.LikeMember VALUES (2,1),
   (2,3),
   (4,1),
   (4,5)

 

圖模型

  圖模型的計划與關系型模型完全不同。表在圖模型中可能是邊或者節點。我們需要決定哪些表是邊,哪些表是節點。

  圖具有如下特征:

    • 包含節點和邊;
    • 節點上有屬性(鍵值對);
    • 邊有名字和方向,並總是有一個開始節點和一個結束節點;
    • 邊也可以有屬性。

  下圖表現了圖模型:

 

  如圖所示,在模型中節點和邊很容易確定:邏輯模型中的所有實體就是節點,而所有關系就是邊。這里有“Posts”和“Members”兩個實體, ‘Reply To’, ‘Like’‘Written By’三個邊。

注意

  節點和邊不過是帶有特殊字段的表。沒有任何限制禁止我們創建常規的表之間的關系,以便將模型轉化為關系和圖模型的組合。

  例如,‘Written By’ ‘Posts’‘Members’的關系,可以轉化為一個一對多的關系。通過創建一個邊的關系表,我們可以用常規的關系表來表現所謂的圖模型中的表。也就是組合模式了。

  當我們創建一個根節點實體,這個實體接收一個叫做‘$node_id’的計算字段。我們可以使用這個字段作為主鍵,SQL Server 允許計算字段作為主鍵:如果這個主鍵是一個JSON字段,就不適合作為主鍵了。因此我們的節點必須包含兩個鍵:業務鍵,整型字段,以及‘$node_id’ 鍵,包含整型字段自增長的JSON鍵。

  下面為節點實體的腳本:

Use GraphExample

  go

  CREATE TABLE [dbo].[ForumMembers](

         [MemberID] [int] IDENTITY(1,1) NOT NULL,

         [MemberName] [varchar](100) NULL

  )

  AS NODE

  GO

  

  CREATE TABLE [dbo].[ForumPosts](

         [PostID] [int] NULL,

         [PostTitle] [varchar](100) NULL,

         [PostBody] [varchar](1000) NULL

  )

  AS NODE

 

注意

  在創建對象后,在對象瀏覽器中檢查對象。或許此時注意到一個新的文件夾在‘Tables’文件夾里面叫做‘Graph’。同時也注意到自增字段的名字,盡管我們可以用簡稱來引用這些字段,例如$node_id,但是真實的字段名稱包含了GUID。這個簡稱字段其實是一個假的名字,稱之為“偽列”(可以理解為別名),我們能在查詢中使用。

 

  如圖,插入數據到節點表:我們只需要忽略$node_id,寫出插入其他字段的語句即可,語句如下:

INSERT ForumMembers values ('Mike'),('Carl'),('Paul'),('Christy'),('Jennifer'),('Charlie')
  INSERT INTO [dbo].[ForumPosts]

             (

             [PostID]

             ,[PostTitle]

             ,[PostBody]

                   )

       VALUES

          (8,'Intro','nice to see all here!'),

          (7,'Intro','I''m Mike from Argentina'),

           (6,'Re:Geography','I''m Mike from Argentina'),

          (5,'Re:Geography','I''m Jennifer from Brazil'),

           (4,'Geography','Im Christy from USA'),

                (3,'Re: Intro','Hey Paul This is Christy'),

             (1,'Intro','Hi There This is Carl')

                   (2,'Intro','Hello I''m Paul')

使用查詢語句可以看到ForumPosts表的結果。你會發現$node_id字段,是一個JSON字段包含了實體類型和一個自增整型ID,它就是自增長ID。

 

創建邊表

  這個操作很簡單,邊表有屬性,屬性就是表中的常規字段。腳本如下:

Create table dbo.[Written_By]

  as EDGE

  CREATE TABLE [dbo].[Likes]

  AS EDGE

  CREATE TABLE [dbo].[Reply_To]

  AS EDGE

  每個邊表有三個偽列,我們需要處理:

  • $edge_id: 邊記錄的ID
  • $from_id:在邊中記錄的節點ID
  • $to_id:在邊中記錄的其他節點ID

    注意這個定義,最為重要的一點就是:我們需要用一種合乎邏輯的方式定義  $to_id and $from_id 字段對於每條邊意味着什么?你可以觀察之前定義的邊表如何定義的邊,這是一種雙向的合理選擇,使得我們更容易使用和理解。

以下是我們的合理定義:

Written_By:

$from_id will be the post

$to_id will be the member

Likes:

$from_id will be who likes

$to_id will be who/what is liked

Reply_To:

$from_id will be the reply to the main post

$to_id  will be the main post

這些選擇沒有技術限制,但我們需要在插入新記錄時保留它們,永遠不要混淆關系的每一方的含義。

注意

  除了三個偽列以外,所有的表表都有額外字段,並且全是隱藏字段。我們可以在字段屬性中看到隱藏的定義,並且這些隱藏字段不會出現在查詢結果中。

 

 

插入邊記錄

    插入邊表的語句需要邊的兩端ID,$From_id and $To_id這些字段需要用$node_id的值來填充。例如,對於一個帖子的成員,‘Written_By’包含post 的$node_id 作為$From_id 並且有member的$node_id作為$To_id字段。

下面是插入語句:

Insert into Written_By ($to_id,$from_id) values

   (

   (select $node_id from dbo.ForumMembers where MemberId= 1 ),

   (select $node_id from dbo.ForumPosts where PostID=8 )

   ),

   (

   (select $node_id from dbo.ForumMembers where MemberId=1  ),

   (select $node_id from dbo.ForumPosts where PostID=7 )

   ),

   (

   (select $node_id from dbo.ForumMembers where MemberId= 1 ),

   (select $node_id from dbo.ForumPosts where PostID= 6)

   ),

   (

   (select $node_id from dbo.ForumMembers where MemberId=5  ),

   (select $node_id from dbo.ForumPosts where PostID=5 )

   ),

   (

   (select $node_id from dbo.ForumMembers where MemberId=4  ),

   (select $node_id from dbo.ForumPosts where PostID=4 )

   ),

   (

   (select $node_id from dbo.ForumMembers where MemberId=3  ),

   (select $node_id from dbo.ForumPosts where PostID=3 )

   ),

   (

   (select $node_id from dbo.ForumMembers where MemberId=3  ),

   (select $node_id from dbo.ForumPosts where PostID=1 )

   ),

   (

   (select $node_id from dbo.ForumMembers where MemberId=3  ),

   (select $node_id from dbo.ForumPosts where PostID=2 )

   )

 

注意

  這樣插入是不是感覺很麻煩?未來我們可以使用一個對象框架用以支持圖對象,目前還不支持這個功能。

  插入Reply_To腳本如下:

	INSERT Reply_To ($to_id,$from_id) 
   VALUES
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 4),
         (SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 6)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 1),
         (SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 7)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 1),
         (SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 8)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 1),
         (SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 2)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 4),
         (SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 5)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 2),
(SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 3))

最后,再插入Likes:

 

INSERT Likes ($to_id,$from_id) 
   VALUES
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 4),
         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 1)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 7),
         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 2)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 8),
         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 2)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 2),
         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 2)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 5),
         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 4)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 6),
         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 4)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 2),
         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 1)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 7),
         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 3)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 8),
         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 3)),
   ((SELECT $node_id FROM dbo.ForumPosts WHERE PostID = 4),
         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 5))

 

Likes 邊很好的說明了邊的功能作用。僅僅插入幾個menbers和post表的關系,但是我們可以確定在應用中成員也可能喜歡另一個成員。當然,我們也能用這個邊去關聯這個成員和其他成員的關系。在關系型模型中我們需要兩個表完成這個操作,在圖數據庫我們只需要一個邊。

下面我們在論壇的成員之間插入更多的Like:

INSERT Likes ($to_id,$from_id)

   VALUES

   ((SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 1),

         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 2)),

   ((SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 3),

         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 2)),

   ((SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 1),

         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 4)),

   ((SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 5),

         (SELECT $node_id FROM dbo.ForumMembers WHERE MemberID = 4))

小結

  本篇介紹了圖數據庫的一些簡單定義和理解,概述了SQLServer2017中如何創建圖數據庫的基本步驟和語句。這只是一個初步版本必然有很多缺點,當然也有一些優點,下一篇我將先介紹優點再說一下有哪些不足。

參考文獻:https://www.red-gate.com/simple-talk/sql/t-sql-programming/sql-graph-objects-sql-server-2017

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM