SQLServer 表連接種類


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

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

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次匹配。

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

 

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)子句定義。

合並聯接本身的速度很快,但如果需要排序操作,選擇合並聯接就會非常費時,然而,數據量很大且能夠從現有B樹索引中獲得預排序的所需數據,則合並聯接通常最快

的可用聯接算法。

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.完整計划優化

 


免責聲明!

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



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