SQLServer 錶鏈接種類

SQLServer 有3種物理鏈接:Nested Loop(嵌套循環)、Merge Join(合併聯接)、Hash Join(哈希聯接)。算法

T-SQL中的inner/left/right/full join等在進行優化的過程當中會轉換成上面3種物理鏈接。sql

1.Nested Loop(嵌套循環)
SELECT e.BusinessEntityID FROM HumanResources.Employee AS e 
   INNER JOIN Sales.SalesPerson AS s 
   ON e.BusinessEntityID =s.BusinessEntityID

 

處於上方的輸入叫作外部輸入,處於下方的輸入叫作內部輸入,SalesPerson爲外部輸入,Employee爲內部輸入緩存

外部循環逐行處理外部輸入表,內部循環會針對每一個外部行在內部輸入表中進行搜索,已找出匹配行函數

經過彙集索引查找,返回17行數據,也就是說外部輸入要進行17次匹配。oop

當外部鏈接很小,而且內部鏈接在鏈接列上有索引時,優化器會傾向使用這種算法。性能

 

2.Merge Join(合併鏈接)優化

SELECT s.Name FROM sales.store AS s
   JOIN sales.Customer AS c
    ON s.BusinessEntityID =c.CustomerID
	WHERE c.TerritoryID=6

 合併連接也分爲外部輸入和內部輸入,外部輸入和內部輸入只會執行一次。合併聯接要求兩個輸入都在合併列上排序,而合併列由聯接謂詞的等效(on)子句定義。3d

合併聯接自己的速度很快,但若是須要排序操做,選擇合併聯接就會很是費時,然而,數據量很大且可以從現有B樹索引中得到預排序的所需數據,則合併聯接一般最快code

的可用聯接算法。對象

3.Hash Join(哈希聯接)

SELECT pv.ProductID,v.BusinessEntityID,v.Name
	 FROM Purchasing.ProductVendor pv 
	JOIN Purchasing.Vendor v ON (pv.BusinessEntityID=v.BusinessEntityID)
	WHERE pv.StandardPrice >$10

 

哈希聯接有兩種輸入:生成輸入和探測輸入。查詢優化器使用兩個輸入中較小的那個做爲生成輸入

用於多種設置匹配操做:內部聯接、左外部聯接、右外部聯接、和徹底外部聯接,左半聯接、和右半聯接,交集、並集和差別。某種變形能夠進行重複刪除和分組。

 

二:數據訪問操做:

1.掃描操做將針對整個結構來進行,多是一個堆表、一個彙集索引、和一個非彙集索引。

2.查找操做是從索引中查找所需的數據,不須要掃描整個結構,只發生在彙集索引和非彙集索引上。

堆表:表掃描、不存在查找

彙集索引:彙集索引掃描、彙集索引查找

非彙集索引:索引掃描、索引查找

1.掃描(堆表)

SELECT * FROM dbo.DatabaseLog

 

2.彙集索引掃描

SELECT * FROM Person.Address

 

3.非彙集索引掃描(索引掃描)

SELECT AddressID,City,StateProvinceID FROM Person.Address

 

想知道數據是否有排序,能夠看加框部分,它爲true證實數據已經排序

二:查找(堆表上不存在查找操做,因此查找操做特指索引的操做)

查找操做不須要掃描整個索引,它是經過B-Tree結構來快速定位所需的數據

彙集索引查找

SELECT AddressID,City,StateProvinceID FROM Person.Address WHERE AddressID=12037

 

非彙集索引掃描:

SELECT AddressID,StateProvinceID FROM Person.Address WHERE StateProvinceID=32

 

3.書籤(鍵)查找

一個非彙集索引被優化器選爲訪問數據的索引,可是這個索引不能覆蓋全部的列,致使非彙集索引必須藉助彙集索引鍵或者堆上的RID來定位其餘數據

SELECT AddressID,City,StateProvinceID,ModifiedDate FROM Person.Address WHERE StateProvinceID=32

 

鍵值查找操做符的tooltips:

 鍵查找(Key Lookup屬於書籤查找的一種)因爲AddressID不包含在非彙集索引中,查詢又須要用到它,因此須要經過彙集索引來獲取相關的數據。

若是表上沒有相關的彙集索引,會出現RID查找,RID Lookup和Key Lookup統稱爲書籤查找。

不能覆蓋查詢的非彙集索引,因此建立了一個非彙集索引在堆表上,以便進行查詢

--建立非彙集索引

CREATE INDEX IX_Object ON dbo.DatabaseLog(Object)

  --RID

SELECT * FROM dbo.DatabaseLog WHERE Object='City'

 

三:聚合操做:

有兩種操做實現聚合:流聚合(Stream Aggregate)和哈希聚合(Hash Aggregate)

1.排序和哈希

2.流聚合

指使用了聚合函數,但沒有Group By子句並返回一個單一值的查詢。

若是帶有聚合函數,但沒有Group By子句,叫作標量聚合。標量聚合由流聚合操做來返回一個數據。

SELECT AVG(ListPrice) FROM Production.Product

 

   --帶有Group By的聚合

SELECT ProductLine,COUNT(*) FROM Production.Product GROUP BY  ProductLine

 

3.Hash聚合

在執行計劃中以Hash Match做爲物理操做符,當優化器發現一個沒有排序的大表須要聚合,預估只有少許的組時,就會選擇Hash聚合

SELECT TerritoryID,COUNT(*) FROM sales.SalesOrderHeader GROUP BY TerritoryID

 

哈希聚合是針對未排序的數據,一旦加上索引,是能夠轉換成流聚合的。

CREATE INDEX IX_ContactID ON Sales.SalesOrderHeader(TerritoryID)
   SELECT TerritoryID,COUNT(*) FROM  Sales.SalesOrderHeader GROUP BY TerritoryID

 

 

四:檢查統計對象

能夠經過sys.stats目錄視圖來查看某個對象的統計信息

SELECT * FROM sys.stats  WHERE object_id=OBJECT_ID('Sales.SalesOrderDetail')

 也能夠用DBCC SHOW_STATISRICS命令來顯示某列上的統計信息

DBCC SHOW_STATISTICS('Sales.SalesOrderDetail',UnitPrice)

 

只須要用如下這個列:

SELECT * FROM sales.SalesOrderDetail WHERE UnitPrice=35

 再次執行:

檢查一個非彙集索引上的統計信息,只關注密度信息部分:

DBCC SHOW_STATISTICS('Sales.SalesOrderDetail',IX_SalesOrderDetail_ProductID)

手工計算密度矢量:

SELECT COUNT (DISTINCT ProductID) ,1.0/COUNT (DISTINCT ProductID) FROM Sales.SalesOrderDetail

 

執行如下語句,看看執行計劃中是否真的使用了266行預估行數

SELECT ProductID,COUNT(1) FROM sales.SalesOrderDetail GROUP BY ProductID

 

使用如下語句查看對象的統計信息狀況:

SELECT name,auto_created,STATS_DATE(object_id,stats_id) AS update_date
    FROM sys.stats WHERE object_id=OBJECT_ID(N'表名')

重建索引:

ALTER INDEX ix_ProductID ON dbo.SalesOrderDetail  REBUILD

 計算列:

SELECT * FROM sales.SalesOrderDetail WHERE OrderQty * UnitPrice>10000

 

建立一個計算列:

ALTER TABLE sales.SalesOrderDetail ADD cc AS OrderQty * UnitPrice

 從新運行上面的語句,預估行數和實際函數相對較接近:

 

刪除上述列:

ALTER TABLE sales.SalesOrderDetail DROP COLUMN cc

 4.過濾索引上的統計信息

這種統計信息不是全表建立的,而是針對一個表的子集建立的,當建立過濾索引時,會自動建立對應的統計信息。

SELECT * FROM Person.Address WHERE City='Los Angeles'

SELECT * FROM Person.Address WHERE StateProvinceID=9

 

兩個查詢的預估行數和實際行數是同樣的。

組合Where條件以後的執行計劃:

SELECT * FROM person.Address WHERE City='Los Angeles' AND StateProvinceID=9

 

SQL Server 首先把兩個查詢的結果集行數相乘,而後 除以表中的總行數。

提升預估的準確性,能夠建立一個統計信息

CREATE STATISTICS California ON Person.Address(City) WHERE StateProvinceID=9

 清空緩存,執行查詢語句:

DBCC FREEPROCCACHE
	GO
    SELECT * FROM person.Address WHERE City='Los Angeles' AND StateProvinceID=9

 

只返回頭信息的方法查詢:

DBCC SHOW_STATISTICS('Person.Address',California)
	WITH stat_header

 

也能夠用下面的語句進行查詢:

SELECT * FROM sys.stats WHERE filter_definition IS NOT NULL

 

六: 預估數據錯誤:

錯誤預估行數會致使優化器不合適的執行計劃影響性能,能夠經過檢查執行計劃發現。

SET STATISTICS PROFILE  ON
    GO
    SELECT * FROM Sales.SalesOrderDetail
	WHERE OrderQty * UnitPrice >10000
	GO
     SET STATISTICS PROFILE OFF

 七:優化器工做過程

工做過程:簡化、簡單計劃優化和完整計劃優化

1.簡化,在這個階段,查詢會被重寫,一些邏輯寫法會被重寫成優化器能讀懂的內容

(一):子查詢會被轉換成join

(二):多餘的inner/outer join會被移除

(三):Where條件中的篩選部分會被處理

SELECT pp.ProductID--,ppc.Name FROM Production.Product pp
	 INNER JOIN production.ProductSubcategory pps ON pps.ProductSubcategoryID=pp.ProductSubcategoryID
	 INNER JOIN Production.ProductCategory  ppc ON ppc.ProductCategoryID =pps.ProductCategoryID

 去掉註釋和不去註釋分別執行,查看執行計劃,註釋了一個字段,會少一個表關聯。

禁用外鍵:

ALTER TABLE Production.ProductSubcategory NOCHECK CONSTRAINT FK_ProductSubcategory_ProductCategory_ProductCategoryID

 

由於外鍵的做用消失,因此須要引入第三個表

恢復外鍵

ALTER TABLE Production.ProductSubcategory WITH CHECK CHECK CONSTRAINT FK_ProductSubcategory_ProductCategory_ProductCategoryID

 2.簡單計劃優化

3.完整計劃優化

相關文章
相關標籤/搜索