T-SQL查詢進階--深刻淺出視圖

簡介  

 

   視圖能夠看做定義在SQL Server上的虛擬表.視圖正如其名字的含義同樣,是另外一種查看數據的入口.常規視圖自己並不存儲實際的數據,而僅僅存儲一個Select語句和所涉及表的metadata.html

    視圖簡單的理解以下:sql

    5

    經過視圖,客戶端再也不須要知道底層table的表結構及其之間的關係。視圖提供了一個統一訪問數據的接口。數據庫

 

爲何要使用視圖(View)

   從而咱們不難發現,使用視圖將會獲得以下好處:緩存

 

  •    視圖隱藏了底層的表結構,簡化了數據訪問操做
  •    由於隱藏了底層的表結構,因此大大增強了安全性,用戶只能看到視圖提供的數據
  •    使用視圖,方便了權限管理,讓用戶對視圖有權限而不是對底層表有權限進一步增強了安全性
  •    視圖提供了一個用戶訪問的接口,當底層表改變後,改變視圖的語句來進行適應,使已經創建在這個視圖上客戶端程序不受影響

 

視圖(View)的分類

    視圖在SQL中能夠分爲三類安全

  1.      普通視圖(Regular View)
  2.      索引視圖(Indexed View)
  3.      分割視圖(Partitioned View)

    下面從這幾種視圖類型來談視圖服務器

 

普通視圖(Rugular View)

   普通視圖由一個Select語句所定義,視圖僅僅包含其定義和被引用表的metadata.並不實際存儲數據。MSDN中建立視圖的模版以下:分佈式

CREATE VIEW [ schema_name . ] view_name [ (column [ ,...n ] ) ] 
[ WITH <view_attribute> [ ,...n ] ] AS select_statement 
[ WITH CHECK OPTION ] [ ; ]

<view_attribute> ::= 
{
    [ ENCRYPTION ]
    [ SCHEMABINDING ]
    [ VIEW_METADATA ]     }

   參數仍是比較少的,如今解釋一下上面的參數:函數

   ENCRYPTION:視圖是加密的,若是選上這個選項,則沒法修改.建立視圖的時候須要將腳本保存,不然不再能修改了性能

   SCHEMABINDING:和底層引用到的表進行定義綁定。這個選項選上的話,則視圖所引用到的表不能隨便更改構架(好比列的數據類型),若是須要更改底層表構架,則先drop或者alter在底層表之上綁定的視圖.加密

   VIEW_METADATA:這個是個頗有意思的選項.正如這個選項的名稱所指示,若是不選擇,返回給客戶端的metadata是View所引用表的metadata,若是選擇了這個選項,則返回View的metadata.再通俗點解釋,VIEW_METADATA可讓視圖看起來貌似表同樣。View的每個列的定義等直接告訴客戶端,而不是所引用底層表列的定義。

   WITH Check Option:這個選項用於更新數據作限制,下面會在經過視圖更新數據一節解釋.

 

   固然了,建立視圖除了須要符合上面的語法規則以外,還有一些規則須要遵照:

  •    在View中,除非有TOP關鍵字,不然不能用Order By子句(若是你獨斷獨行要用Order by,這裏有個hack是使用Top 100 percent…..)
  •    View在每一個Schema中命名必須獨一無二
  •    View嵌套不能超過32層(其實實際工做中誰嵌套超過兩層就要被打PP了-.-)
  •    Compute,compute by,INTO關鍵字不容許出如今View中
  •    View不能創建在臨時表上
  •    View不能對全文索引進行查詢

 

   創建View一個簡單的例子:

CREATE VIEW v_Test_View1AS SELECT TOP 100 * FROM HumanResources.Employee

 

   視圖創建完成後,就能夠像訪問表同樣訪問視圖了:

SELECT * FROM v_Test_View1

 

在Management studio中,我建立視圖的時候更喜歡用這樣一種方法,將會便捷不少:

   6

 

7

索引視圖(Indexed View)

    在談到索引視圖以前,我忽然想起之前看過的一個漫畫.話說我們高端產品買不起,可是省吃儉用攢點錢買個IPhone裝裝高端總仍是能夠的吧:

 

      1

 

    其實索引視圖也很相似,在普通的視圖的基礎上,爲視圖創建惟一彙集索引,這時這個視圖就變成了索引視圖.套用上面漫畫的公式:視圖+彙集索引=索引視圖

   索引視圖能夠看做是一個和表(Table)等效的對象!

    SQL Server中的索引視圖和Oracle中的Materialized View是一個概念.想要理解索引視圖,必須先理解彙集索引。彙集索引簡單來講理解成主鍵,數據庫中中的數據按照主鍵的順序物理存儲在表中,就像新華字典,默認是按照ABCD….這樣的方式進行內容設置。ABCD….就至關於主鍵.這樣就避免了整表掃描從而提升了性能.所以一個表中只能有一個彙集索引。

    對於索引視圖也是,爲一個視圖加上了彙集索引後。視圖就不只僅再是select語句和表的metadata了,索引視圖會將數據物理存在數據庫中,索引視圖所存的數據和索引視圖中所涉及的底層表保持同步。

    理解了索引視圖的原理以後,咱們能夠看出,索引視圖對於OLAP這種大量數據分析和查詢來講,性能將會獲得大幅提高。尤爲是索引視圖中有聚合函數,涉及大量高成本的JOIN,由於聚合函數計算的結果物理存入索引視圖,因此當面對大量數據使用到了索引視圖以後,並沒必要要每次都進行聚合運算,這無疑會大大提高性能.

    而同時,每次索引視圖所涉及的表進行Update,Insert,Delete操做以後,SQL Server都須要標識出改變的行,讓索引視圖進行數據同步.因此OLTP這類增刪改不少的業務,數據庫須要作大量的同步操做,這會下降性能。

    談完了索引視圖的基本原理和好處與壞處以後,來看看在SQL Server中的實現:

    在SQL Server中實現索引視圖是一件很是,簡單的事,只須要在現有的視圖上加上惟一彙集索引.但SQL Server對於索引視圖的限制卻使不少DBA對其並不青睞:

    好比:

  • 索引視圖涉及的基本表必須ANSI_NULLS設置爲ON
  • 索引視圖必須設置ANSI_NULLS和QUOTED_INDETIFIER爲ON
  • 索引視圖只能引用基本表
  • SCHEMABINDING必須設置
  • 定義索引視圖時必須使用Schema.ViewName這樣的全名
  • 索引視圖中不能有子查詢
  • avg,max,min,stdev,stdevp,var,varp這些聚合函數不能用

     ………………

   

      還有更多…就不一一列舉了,有興趣的請自行Google之.

      下面我來經過一個例子來講明索引視圖:

      假設在adventureWorks數據庫中,咱們有一個查詢:

SELECT p.Name,s.OrderQtyFROM Production.Product p
 inner join Sales.SalesOrderDetail sON p.ProductID=s.ProductID

    這個查詢的執行計劃:

    8before

    這時,我創建視圖並在這個視圖上創建惟一彙集索引:

--創建視圖 CREATE VIEW v_Test_IndexedViewWITH SCHEMABINDINGAS SELECT p.Name,s.OrderQty,s.SalesOrderDetailIDFROM Production.Product p
 inner join Sales.SalesOrderDetail sON p.ProductID=s.ProductIDGO --在視圖上創建索引 CREATE UNIQUE CLUSTERED INDEX indexedview_test1ON v_Test_IndexedView(SalesOrderDetailID)

 

   接下來,套用劉謙的臺詞:見證奇蹟的時刻到了,咱們再次執行以前的查詢:

   8after

 

    從上面這個例子中,能夠體會到索引視圖的強大威力,即便你的查詢語句中不包含這個索引視圖,查詢分析器會自動選擇這個視圖,從而大大的提升了性能.固然,這麼強力的性能,只有在SQL SERVER企業版和開發版纔有哦(雖然我見過不少SQL Server的開發人員讓公司掏着Enterprise版的錢,用着Express版的功能……)

分割視圖(Partitioned View)

 

    分割視圖其實從微觀實現方式來講,整個視圖所返回的數據由幾個平行表(既是幾個表有相同的表結構,也就是列和數據類型,但存儲的行集合不一樣)進行UNION鏈接(對於UNION鏈接若是不瞭解,請看我以前的博文)所得到的數據集.

    分割視圖整體上能夠分爲兩種:

    1.本地分割視圖(Local Partitioned View)

    2.分佈式分割視圖(Distributed Partitioned View)

  

    由於本地分割視圖僅僅是爲了和SQL Server 2005以前的版本的一種向後兼容,因此這裏僅僅對分佈式分割視圖進行說明.

    分佈式分割視圖實際上是將由幾個由不一樣數據源或是相同數據源得到的平行數據集進行鏈接所得到的,一個簡單的概念圖以下:

 

    2

   

   上面的視圖所得到的數據分別來自三個不一樣數據源的表,每個表中只包含四行數據,最終組成了這個分割視圖.

    使用分佈式分割視圖最大的好處就是提高性能.好比上面的例子中,我僅僅想取得ContactID爲8這位員工的信息,若是經過分佈式視圖獲取的話,SQL Server能夠很是智能的僅僅掃描包含ContactID爲8的表2,從而避免了整表掃描。這大大減小了IO操做,從而提高了性能.

    這裏要注意的是,分佈式分割視圖所涉及的表之間的主鍵不能重複,好比上面的表A ContactID是1-4,則表B的ContactID不能是2-8這個樣子.

    還有一點要注意的是,必定要爲分佈式分割索引的主鍵加Check約束,從而讓SQL Server的查詢分析器知道該去掃描哪一個表,下面來看個例子.

 

    在微軟示例數據庫AdventureWorks數據庫,我經過ContactID從前100行和100-200行的數據分別存入兩個表,Employee100和Employee200,代碼以下:

--create Employee100 SELECT TOP 100 * INTO Employee100FROM HumanResources.Employee ORDER BY EmployeeID--create Employee200 SELECT *  INTO Employee200FROM 
(SELECT TOP 100 *FROM HumanResources.Employee WHERE EmployeeID NOT IN (SELECT TOP 100 EmployeeID FROM HumanResources.Employee ORDER BY EmployeeID)ORDER BY HumanResources.Employee.EmployeeID)AS e

 

   這時來創建分佈式分割視圖:

CREATE VIEW v_part_view_testAS SELECT * FROM Employee100UNION SELECT * FROM Employee200

 

   這時咱們對這個索引進行查詢操做:

 

SELECT * FROM v_part_view_testWHERE EmployeeID=105

 

  下面是執行計劃:

  3

  經過上圖能夠看出,經過這種分割的方式,執行計劃僅僅是掃描Employee200,從而避免了掃描全部數據,這無疑提高了性能.

  因此,當你將不一樣的數據表之間放到不一樣的服務器或是使用RAID5磁盤陣列時,分佈式分割視圖則進一步會提高查詢性能.

 

  使用分佈式分割視圖可以在全部狀況下都提高性能嗎?那妥妥的不可能.使用這種方式若是面對的查詢包含了聚合函數,尤爲是聚合函數中還包含distinct。或是不加where條件進行排序.那絕對是性能的殺手。由於聚合函數須要掃描分佈式分割視圖中全部的表,而後進行UNION操做後再進行運算.

 

經過視圖(View)更新數據

   經過視圖更新數據是我所不推薦的.由於視圖並不能接受參數.我更推薦使用存儲過程來實現.

   使用View更新數據和更新Table中數據的方式徹底同樣(前面說過,View能夠看做是一個虛擬表,若是是索引視圖則是具體的一張表)

   經過視圖來更新數據須要注意如下幾點

   1.視圖中From子句以後至少有一個用戶表

   2.View的查詢不管涉及多少張表,一次只能更新其中一個表的數據

   3.對於表達式計算出來的列,常量列,聚合函數算出來的列沒法更新

   4.Group By,Having,Distinct關鍵字不能影響到的列不能更新

 

   這裏說一下建立View有一個WITH Check Option選項,若是選擇這個選項,則經過View所更新的數據必須符合View中where子句所限定的條件,好比:

   我建立一個View:

   4

 

視圖(View)中的幾個小技巧

    1.經過視圖名稱查到視圖的定義

SELECT * FROM sys.sql_modulesWHERE object_id=OBJECT_ID('視圖名稱')

 

   2.前面說過,普通視圖僅僅存儲的是select語句和所引用表的metadata,當底層表數據改變時,有時候視圖中表的metadata並無及時同步,能夠經過以下代碼進行手動同步

EXEC sp_refreshview 視圖名稱

 

 

視圖(View)的最佳實踐

    這是我我的一些經驗,歡迎補充

  •    必定要將View中的Select語句性能調到最優(貌似是廢話,不過真理都是廢話…)
  •    View最好不要嵌套,若是非要嵌套,最多隻嵌套一層
  •    能用存儲過程和自定義函數替代View的,儘可能不要使用View,存儲過程會緩存執行計劃,性能更優,限制更少
  •    在分割視圖上,不要使用聚合函數,尤爲是聚合函數還包含了Distinct
  •    在視圖內,若是Where子句能加在視圖內,不要加在視圖外(由於調用視圖會返回全部行,而後再篩選,性能殺手,若是你還加上了order by…..)

 

總結

    文中對視圖的三種類型進行了詳解.每種視圖都有各自的使用範圍,使用得當會將性能提高一個檔次,而使用不當反而會拖累性能.

    我想起一句名言:「everything has price,always trade-off」…..

相關文章
相關標籤/搜索