聽到大牛們說執行計划,總是很惶恐,是對知識的缺乏的惶恐,所以必須得學習執行計划,以減少對這一塊知識的惶恐,下面是對執行計划的第一講-理解執行計划。
本系列【T-SQL】主要是針對T-SQL的總結。
SQL基礎
【T-SQL基礎】06.透視、逆透視、分組集
【T-SQL基礎】07.數據修改
【T-SQL基礎】08.30分鍾全面解析-SQL事務+隔離級別+阻塞+死鎖(推薦 +165)
【T-SQL基礎】09.可編程對象
SQL進階
【SQL進階】01.好用的SQL TVP~~獨家贈送[增-刪-改-查]的例子
【SQL進階】02.理解SQL查詢的底層原理(推薦 +5)
SQL性能調優
持續更新......歡迎關注我!
一、為什么需要執行計划?
(1)幫助分析
當我們想要去分析SQL語句存在很慢時,需要有一個分析工具幫助我們分析SQL語句中哪些地方存在性能問題,而這個分析工具就是執行計划,看懂執行計划就能知道哪些地方有性能問題,然后結合自己已有的SQL知識分析為什么這些地方有性能問題,進而嘗試提出解決方案,並測試自己的方案是否能提高性能,以及方案是否合理。
(2)獲取其他信息
a.哪些索引被用在查詢中
b.數據是怎樣關聯起來的
c.數據是怎樣檢索的
d.為什么SQL Server沒有使用這些索引
e.SQL語句的執行順序
二、什么是執行計划?
SQL語句執行之前,需要有一個執行的方案,而這個方案是由查詢優化器(查詢分析器)產生的,並且是高效的、開銷最小的方案,這就是執行計划。不知道查詢優化器的可以看我寫之前寫的一篇博客:
三、如何顯示執行計划?
執行計划有三種格式:圖形化執行計划,文本化執行計划,XML格式的執行計划。
(1)圖形化執行計划
優點:可視性好。
a.估計的執行計划
可以通過鼠標點擊圖標顯示估計的執行計划或者通過快捷鍵Ctrl+L顯示估計執行計划。預估執行計划不會真正執行,只是預估出來的執行計划。
b.實際的執行計划
單擊實際的執行計划圖標,該圖標處於選中狀態,然后執行SQL語句,將會顯示實際執行的執行計划。
(2)文本化執行計划
用獨立的行來代表每一個迭代器。使用豎線(符號“|”)來代表查詢樹中迭代器之間的父子關系。數據都是從子迭代器流向父迭代器。
優點:和圖形計划比較,文本執行計划更容易保存、處理、搜索和比較。
--顯示完整的預估執行計划信息 SET SHOWPLAN_TEXT ON GO
--顯示預估執行計划的有限信息,可以用osql.exe等工具分析 SET SHOWPLAN_ALL ON GO
--顯示完整的實際執行計划信息 SET STATISTICS PROFILE ON GO
總結:
(3)XML執行計划
優點:三種執行計划中最詳細的。圖形執行計划可以保存為擴展名為.sqlplan的XML格式的計划文件,打開此文件將會以圖形化的執行計划展示。
--顯示預估執行計划 SET SHOWPLAN_XML ON GO
--顯示實際計划的XML格式數據 SET STATISTICS XML ON GO
總結
四、如何分析執行計划?
下面分析三種情況的執行計划:
1.堆表
2.聚集索引
3.非聚集索引
結構 | 掃描 | 查找 | 書簽查找 |
堆表 | 表掃描 | 沒有這種情況 | RID 查找 |
聚集索引 | 聚集索引掃描 | 聚集索引查找 | 沒有這種情況 |
非聚集索引 | 如果用到了索引,則是索引掃描 | 索引查找 | Key 查找 |
關於表掃描的那些事:
- 沒有索引的表稱作堆表,查找匹配行用的是表掃描。
- 如果出現表掃描操作,則證明這個表上一定沒有聚集索引。
關於索引查找的那些事:
假設[列1]上有一個單列索引,可以使用這個索引查找下面這些謂詞:
1.[列1] = 1.23
2.[列1] > 1.23
3.[列1] BETWEEN 1 AND 100
4.[列1] LIKE 'abc%'
5.[列1] IN (1,3,7,10)
不能使用這個索引對下列這些謂詞進行查找:
1.ABS[列1] = 1
2.[列1] + 10 > 12
3.[列1] LIKE '%abc'
關於非聚集索引的那些事:
- 如果只有非聚集索引時,非聚集索引不包含查詢列時,則SQL查詢優化器選擇非聚集索引掃描。
- 只有非聚集索引時,非聚集索引不包含過濾條件列時,則選擇表掃描。
- 非聚集索引具有獨立於數據行的結構。 非聚集索引包含非聚集索引鍵值,並且每個鍵值項都有指向包含該鍵值的數據行的指針。
- 從非聚集索引中的索引行指向數據行的指針稱為行定位器。 行定位器的結構取決於數據頁是存儲在堆中還是聚集表中。 對於堆,行定位器是指向行的指針。 對於聚集表,行定位器是聚集索引鍵。
- 您可以向非聚集索引的葉級添加非鍵列(包含列)以跳過現有的索引鍵限制(900 字節和 16 鍵列),並執行完整范圍內的索引查詢。
關於聚集索引的那些事:
1. 如果表上有聚集索引,則掃描稱作聚集索引掃描,查找稱作聚集索引查找;
2. 聚集索引掃描和表掃描的性能沒多大差異;
3.聚集索引根據數據行的鍵值在表或視圖中排序和存儲這些數據行。
4.索引定義中包含聚集索引列。
5.每個表只能有一個聚集索引,因為數據行本身只能按一個順序排序。
6.只有當表包含聚集索引時,表中的數據行才按排序順序存儲。 如果表具有聚集索引,則該表稱為聚集表。如果表沒有聚集索引,則其數據行存儲在一個稱為堆的無序結構中。
7.加了聚集索引不一定能提高性能,有些情況下,性能可能不如表掃描;
8.聚集索引就是表本身。表有多少行和多少列,聚集索引就有多少行和和多少列。
9.單表查詢中,過濾條件中有聚集索引列,且能用這個索引查找過濾條件中的謂詞,則是聚集索引查找,過濾條件中沒有聚集索引列則是聚集索引掃描。
(1)沒有索引的情況
創建myOrder表
USE [Test] GO CREATE TABLE [dbo].[myOrder]( [id] [int] NOT NULL, [customer] [nvarchar](100) NOT NULL ) ON [PRIMARY] GO
myOrder只有兩列id,customer,這兩列上面都沒有索引。
SELECT [id] FROM [Test].[dbo].[myOrder] WHERE [customer] = 'ddd'
下面是執行計划:
customer列上面沒有索引,SQL Server需要讀取myOrder表的每一行來判斷customer='ddd',如果結果為true,則返回此行。
查詢的示例圖如下,customer=ddd 存在三條記錄。
注意:
1.掃描及查找是SQL Server用來從表和索引中讀取數據的迭代器;
2.掃描用來處理整個表或索引的全部分支;
3.查找是在謂詞基礎上有效返回索引中一個或多個范圍中的行。
(2)有非聚集索引的情況
在id上創建非聚集索引
USE [Test] GO --刪除索引dbo.myOrder.ID_NON_INDEX DROP INDEX dbo.myOrder.ID_NON_INDEX CREATE NONCLUSTERED INDEX [ID_NON_INDEX] ON [dbo].[myOrder] ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] GO
1.查詢條件的列上沒有非聚集索引,查詢列上沒有非聚集索引->表掃描
--id列上有索引,customer列上沒有索引,查詢條件中用的是customer='ddd'進行過濾。 --由於customer列上沒有索引,所以需要進行表掃描來找到符合customer='ddd'的行。 SELECT [id] FROM [Test].[dbo].[myOrder] WHERE [customer] = 'ddd' SELECT [customer] FROM [Test].[dbo].[myOrder] WHERE [customer] = 'ddd' SELECT [id],[customer] FROM [Test].[dbo].[myOrder] WHERE [customer] = 'ddd' SELECT [id] FROM [Test].[dbo].[myOrder] WHERE [id] = 2 AND [customer] ='ddd' SELECT [id],[customer] FROM [Test].[dbo].[myOrder] WHERE [id] = 2 AND [customer] ='ddd'
2.查詢條件的列上有非聚集索引,查詢列上沒有非聚集索引->表掃描
--id列上有索引,customer列上沒有索引,查詢條件中用的是id=2進行過濾。 --SELECT查詢需要返回customer列,由於customer列上沒有索引,且索引[ID_NON_INDEX]不包含customer列, --即使用非聚集索引掃描找到了符合過濾條件id=2的索引分支,但是只能在該索引分支上面拿到id列的值,因為該索引分支只包含了id列,其他列的值拿不到。 --所以還是需要進行表掃描來找到符合條件的行,然后獲取該行的customer列的值。 --這里有個疑問:為什么找到索引分支后,不能繼續找到對應的行,然后拿到這行的customer列??
SELECT [customer] FROM [Test].[dbo].[myOrder] WHERE [id] = 2 SELECT [id],[customer] FROM [Test].[dbo].[myOrder] WHERE [id] = 2
3.查詢條件的列上有非聚集索引,查詢列上有非聚集索引->索引查找
--id列上有索引,customer列上沒有索引,查詢條件中用的是id=2進行過濾。 --SELECT查詢需要返回id列,使用非聚集索引掃描找到了符合過濾條件id=2的索引分支,在找到的索引分支上拿到id列的值。 SELECT [id] FROM [Test].[dbo].[myOrder] WHERE [id] = 2
(3)有聚集索引的情況
1.查詢條件的列上沒有聚集索引->聚集索引掃描
-- --id列上有聚集索引,customer列上沒有索引,查詢條件中用的是customer='ddd'進行過濾。 --由於customer列上沒有索引,所以需要進行掃描來找到符合customer='ddd'的行。 --只要有聚集索引,則掃描就是聚集索引掃描。聚集索引和表掃描的性能基本上一樣。 SELECT [id] FROM [Test].[dbo].[myOrder] WHERE [customer] = 'ddd' SELECT [customer] FROM [Test].[dbo].[myOrder] WHERE [customer] = 'ddd' SELECT [id],[customer] FROM [Test].[dbo].[myOrder] WHERE [customer] = 'ddd'
2.查詢條件的列上有聚集索引->聚集索引查找
--id列上有索引,customer列上沒有索引,查詢條件中用的是id=2進行過濾。 --用聚集索引查找到了id=2的行,由於表中的行就是按照id列來排序的,所以找到了這一行, --也就能找到這一行的所有列,所以能夠拿到customer列。所以是聚集索引掃描。 SELECT [customer] FROM [Test].[dbo].[myOrder] WHERE [id] = 2 SELECT [id] FROM [Test].[dbo].[myOrder] WHERE [id] = 2 SELECT [id],[customer] FROM [Test].[dbo].[myOrder] WHERE [id] = 2
3.查詢條件中,有一列有聚集索引,另一列沒有聚集索引->聚集索引查找
--id列上有索引,customer列上沒有索引,查詢條件中用的是[id] = 2,[customer] ='ddd'進行過濾。 --當過濾條件中有一個可以根據聚集索引來查找時,先用聚集索引來找到匹配的行([id] = 2),然后再在過濾出來的行中篩選處符合[customer] ='ddd'的行。 --所以是聚集索引查找。 --疑問:為什么第二步篩選操作在執行計划圖中沒有體現??這個地方我想到的是拿到id=2的匹配行后,直接舍棄掉不符合條件[customer] ='ddd'的行,這個舍棄動作就沒有直接體現出來。 SELECT [id] FROM [Test].[dbo].[myOrder] WHERE [id] = 2 AND [customer] ='ddd' SELECT [id],[customer] FROM [Test].[dbo].[myOrder] WHERE [id] = 2 AND [customer] ='ddd' SELECT [id],[customer] FROM [Test].[dbo].[myOrder] WHERE [customer] ='3333' AND [id] = 2
->>【T-SQL】系列文章全文目錄(2017-06-26更新)
作 者: Jackson0714
出 處:http://www.cnblogs.com/jackson0714/
關於作者:專注於微軟平台的項目開發。如有問題或建議,請多多賜教!
版權聲明:本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信我
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角【推薦】一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!