背景: 圖資料庫對於表現和遍歷複雜的實體之間關係是很有效果的。而這些在傳統的關係型資料庫中尤其是對於報表而言很難實現。如果把傳統關係型資料庫比做火車的話,那麼到現在大數據時代,圖資料庫可比做高鐵。它已成為NoSQL中關註度最高,發展趨勢最明顯的資料庫。伴隨SQL Server 2017的出現,在SQ ...
背景:
圖資料庫對於表現和遍歷複雜的實體之間關係是很有效果的。而這些在傳統的關係型資料庫中尤其是對於報表而言很難實現。如果把傳統關係型資料庫比做火車的話,那麼到現在大數據時代,圖資料庫可比做高鐵。它已成為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