亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > SQL Server > 正文

SQL Server調優系列基礎篇(索引運算總結)

2024-08-31 00:55:04
字體:
來源:轉載
供稿:網友
SQL Server調優系列基礎篇(索引運算總結)

前言

上幾篇文章我們介紹了如何查看查詢計劃、常用運算符的介紹、并行運算的方式,有興趣的可以點擊查看。

本篇將分析在SQL Server中,如何利用先有索引項進行查詢性能優化,通過了解這些索引項的應用方式可以指導我們如何建立索引、調整我們的查詢語句,達到性能優化的目的。

閑言少敘,進入本篇的正題。

技術準備

基于SQL Server2008R2版本,利用微軟的一個更簡潔的案例庫(Northwind)進行解析。

簡介

所謂的索引應用就是在我們日常寫的T-SQL語句中,如何利用現有的索引項,再分析的話就是我們所寫的查詢條件,其實大部分情況也無非以下幾種:

1、等于謂詞:select ...where...column=@parameter

2、比較謂詞:select ...where...column> or < or <> or <= or >= @parameter

3、范圍謂詞:select ...where...column in or not in or between and@parameter

4、邏輯謂詞:select ...where...一個謂詞 or、and 其它謂詞 or、and 更多謂詞....

我們就依次分析上面幾種情況下,如何利用索引進行查詢優化的

一、動態索引查找

所謂的動態索引查找就是SQL Server在執行語句的時候,才格式化查詢條件,然后根據查詢條件的不同自動的去匹配索引項,達到性能提升的目的。

來舉個例子

SET SHOWPLAN_TEXT ONGOSELECT OrderIDFROM OrdersWHERE ShipPostalCode IN (N'05022',N'99362')

因為我們在表Orders的列ShipPostalCode列中建立了非聚集索引列,所以這里查詢的計劃利用了索引查找的方式。這也是需要建立索引的地方。

我們來利用文本的方式來查看該語句的詳細的執行計劃腳本,語句比較長,我用記事本換行,格式化查看

我們知道這張表的該列里存在一個非聚集索引,所以在查詢的時候要盡量使用,如果通過索引掃描的方式消耗就比價大了,所以SQL Server盡量想采取索引查找的方式,其實IN關鍵字和OR關鍵字邏輯是一樣的。

于是上面的查詢條件就轉換成了:

[Northwind].[dbo].[Orders].[ShipPostalCode]=N'05022' OR[Northwind].[dbo].[Orders].[ShipPostalCode]=N'99362'

這樣就可以采用索引查找了,先查找第一個結果,然后再查找第二個,而這個過程在SQL Server中就被稱為:動態索引查找。

是不是有點智能的感覺了....

所以有時候我們寫語句的時候,盡量要使用SQL Server的這點智能了,讓其能自動的查找到索引,提升性能。

有時候偏偏我們寫的語句讓SQL Server的智能消失,舉個例子:

--參數化查詢條件DECLARE @Parameter1 NVARCHAR(20),@Parameter2 NVARCHAR(20)SELECT @Parameter1=N'05022',@Parameter2=N'99362'SELECT OrderIDFROM OrdersWHERE ShipPostalCode IN (@Parameter1,@Parameter2)

我們將這兩個靜態的篩序值改成參數,有時候我們寫的存儲過程灰常喜歡這么做!我們來看這種方式的生成的查詢計劃

本來很簡單的一個非聚集索引查找搞定的執行計劃,我們只是將這兩個數值沒有直接寫入IN關鍵字中,而是利用了兩個變量來代替。

看看上面SQL Server生成的查詢計劃!尼瑪...這都是些啥???還用起來嵌套循環,我就查詢了一個Orders表...你嵌套循環個啥....上面動態索引查找的能力去哪了???

好吧,我們用文本查詢計劃來查看下,這個簡單的語句到底在干些啥...

 |--Nested Loops(Inner Join, OUTER REFERENCES:([ExPR1009], [Expr1010], [Expr1011]))       |--Merge Interval       |    |--Sort(TOP 2, ORDER BY:([Expr1012] DESC, [Expr1013] ASC, [Expr1009] ASC, [Expr1014] DESC))       |         |--Compute Scalar(DEFINE:([Expr1012]=((4)&[Expr1011]) = (4) AND NULL = [Expr1009], [Expr1013]=(4)&[Expr1011], [Expr1014]=(16)&[Expr1011]))       |              |--Concatenation       |                   |--Compute Scalar(DEFINE:([Expr1004]=[@Parameter2], [Expr1005]=[@Parameter2], [Expr1003]=(62)))       |                   |    |--Constant Scan       |                   |--Compute Scalar(DEFINE:([Expr1007]=[@Parameter1], [Expr1008]=[@Parameter1], [Expr1006]=(62)))       |                        |--Constant Scan       |--Index Seek(OBJECT:([Northwind].[dbo].[Orders].[ShipPostalCode]), SEEK:([Northwind].[dbo].[Orders].[ShipPostalCode] > [Expr1009] AND [Northwind].[dbo].[Orders].[ShipPostalCode] < [Expr1010]) ORDERED FORWARD)

挺復雜的是吧,其實我分析了一下腳本,關于為什么會生成這個計劃腳本的原因,是為了解決如下幾個問題:

1、前面我們寫的腳本在IN里面寫的是兩個常量值,并且是不同的值,所以形成了兩個索引值的查找通過OR關鍵字組合,

這種方式貌似沒問題,但是我們將這兩個數值變成了參數,這就引來了新的問題,假如這兩個參數我們輸入的是相等的,那么利用前面的執行計劃就會生成如下

[Northwind].[dbo].[Orders].[ShipPostalCode]=N'05022' OR [Northwind].[dbo].[Orders].[ShipPostalCode]=N'05022'

這樣執行產生的輸出結果就是2條一樣的輸出值!...但是表里面確實只有1條數據...所以這樣輸出結果不正確!

所以變成參數后首先解決的問題就是去重問題,2個一樣的變成1個。

2、上面變成參數,還引入了另外一個問題,加入我們兩個值有一個傳入的為Null值,或者兩個都為Null值,同樣輸出結果面臨著這樣的問題。所以這里還要解決的去Null值的問題。

為了解決上面的問題,我們來粗略的分析一下執行計劃,看SQL Server如何解決這個問題的

簡單點將就是通過掃描變量中的值,然后將內容進行匯總值,然后在進行排序,再將參數中的重復值去掉,這樣獲取的值就是一個正確的值,最后拿這些去重后的參數值參與到嵌套循環中,和表Orders進行索引查找。

但是分析的過程中,有一個問題我也沒看明白,就是最好的經過去重之后的常量匯總值,用來嵌套循環連接的時候,在下面的索引查找的時候的過濾條件變成了 and 查找

我將上面的最后的索引查找條件,整理如下:

|--Index Seek(OBJECT:([Northwind].[dbo].[Orders].[ShipPostalCode]), SEEK: ( [Northwind].[dbo].[Orders].[ShipPostalCode] > [Expr1009] AND [Northwind].[dbo].[Orders].[ShipPostalCode] < [Expr1010]

)ORDERED FORWARD)

這個地方怎么搞的?我也沒弄清楚,還望有看明白童鞋的稍加指導下....

好了,我們繼續

上面的執行計劃中,提到了一個新的運算符:合并間隔(merge interval Operator)

我們來分析下這個運算符的作用,其實在上面我們已經在執行計劃的圖中標示出該運算符的作用了,去掉重復值。

其實關于去重的操作有很多的,比如前面文章中我們提到的各種去重操作。

這里怎么又冒出個合并間隔去重?其實原因很簡單,因為我們在使用這個運算符之前已經對結果進行了排序操作,排序后的結果項重復值是緊緊靠在一起的,所以就引入了合并間隔的方式去處理,這樣性能是最好的。

更重要的是合并間隔這種運算符應用場景不僅僅局限于重復值的去除,更重要的是還應用于重復區間的去除。

來看下面的例子

--參數化查詢條件DECLARE @Parameter1 DATETIME,@Parameter2 DATETIMESELECT @Parameter1='1998-01-01',@Parameter2='1998-01-04'SELECT OrderID FROM ORDERSWHERE OrderDate BETWEEN @Parameter1 AND DATEADD(DAY,6,@Parameter1)OR OrderDate BETWEEN @Parameter2 AND DATEADD(DAY,6,@Parameter2)

我們看看這個生成的查詢計劃項

可以看到,SQL Server為我們生成的查詢計劃,和前面我們寫的語句是一模一樣的,當然我們的語句也沒做多少改動,改動的地方就是查詢條件上。

我們來分析下這個查詢條件:

WHERE OrderDate BETWEEN @Parameter1 AND DATEADD(DAY,6,@Parameter1)OR OrderDate BETWEEN @Parameter2 AND DATEADD(DAY,6,@Parameter2)

很簡單的篩選條件,要獲取訂單日期在1998-01-01開始到1998-01-07內的值或者1998-01-04開始到1998-01-10內的值(不包含開始日期)

這里用的邏輯謂詞為:OR...其實也就等同于我們前面寫的IN

但是我們這里再分析一下,你會發現這兩個時間段是重疊的

這個重復的區間值,如果用到前面的直接索引查找,在這段區間之內的搜索出來的范圍值就是重復的,所以為了避免這種問題,SQL Server又引入了“合并間隔”這個運算符。

其實,經過上面的分析,我們已經分析出這種動態索引查找的優缺點了,有時候我們為了避免這種復雜的執行計劃生成,使用最簡單的方式就是直接傳值進入語句中(當然這里需要重編譯),當然大部分的情況我們寫的程序都是只定義的參數,然后進行的運算??赡軒淼穆闊┚褪巧厦娴膯栴},當然有時候參數多了,為了合并間隔所應用的排序就消耗的內存就會增長。怎么使用,根據場景自己酌情分析。

、索引聯合

所謂的索引聯合,就是根據就是根據篩選條件的不同,拆分成不同的條件,去匹配不同的索引項。

舉個例子

SELECT OrderID FROM ORDERSWHERE OrderDate BETWEEN '1998-01-01' AND '1998-01-07'OR ShippedDate BETWEEN '1998-01-01' AND '1998-01-07'

這段代碼是查詢出訂單中的訂單日期在1998年1月1日到1998年1月7日的或者發貨日期同樣在1998年1月1日到1998年1月7日的。

邏輯很簡單,我們知道在這種表里面這兩個字段都有索引項。所以這個查詢在SQL Server中就有了兩個選擇:

1、一次性的來個索引掃描根據匹配結果項輸出,這樣簡單有效,但是如果訂單表數據量比較大的話,性能就會很差,因為大部分數據就根本不是我們想要的,還要浪費時間去掃描。

2、就是通過兩列的索引字段直接查找獲取這部分數據,這樣可以直接減少數據表的掃描量,但是帶來的問題就是,如果分開掃描,有一部分數據就是重復的:那些同時在1998年1月1日到1998年1月7日的訂單,發貨日期也在這段時間內,因為兩個掃描項都包含,所以再輸出的時候需要將這部分重復數據去掉。

我們來看SQL Server如何選擇

看來SQL Server經過評估選擇了第2中方法。但是上面的方法也不盡完美,采用去重操作耗費了64%的資源。

其實,上面的方法,我們根據生成的查詢計劃可以變通的使用以下邏輯,其效果和上面的語句是一樣的,并且生成的查詢計劃也一樣

SELECT OrderID FROM ORDERSWHERE OrderDate BETWEEN '1998-01-01' AND '1998-01-07'UNION SELECT OrderID FROM ORDERSWHERE  ShippedDate BETWEEN '1998-01-01' AND '1998-01-07'

我們再來看一個索引聯合的例子

SELECT OrderID FROM ORDERSWHERE OrderDate = '1998-01-01' OR ShippedDate = '1998-01-01'

我們將上面的Between and不等式篩選條件改成等式篩選條件,我們來看一下這樣形成的執行計劃

基本相同的語句,只是我們改變了不同的查詢條件,但是生成的查詢計劃還是變化蠻大的,有幾點不同之處:

1、前面的用between...and 的篩選條件,通過索引查找返回的值進行組合是用的串聯的方式,所謂的串聯就是兩個數據集拼湊在一起就行,無所謂順序連接什么的。

2、前面的用between...and 的篩選條件,通過串聯拼湊的結果集去重的方式,是排序去重(Sort Distinct)...并且耗費了大量的資源。這里采用了流聚合來干這個事,基本不消耗

我們來分析以下產生著兩點不同的原因有哪些:

首先、這里改變了篩選條件為等式連接,所通過索引查找所產生的結果項是排序的,并且按照我們所要查詢的OrderID列排序,因此在兩個數據集進行匯總的時候,正適合合并連接的條件!需要提前排序。所以這里最優的方式就是采用合并連接!

那么前面我們用between...and 的篩選條件通過索引查找獲取的結果項也是排序的,但是這里它沒有按照OrderID排序,它是按照OrderDate或者ShippedDate列排序的,而我們的結果是要OrderID列,所以這里的排序是沒用的......所以SQL Server只能選擇一個串聯操作,將結果匯聚到一起,然后在排序了......我希望這里我已經講明白了...

其次、關于去重操作,毫無疑問采用流聚合(Aggregate)這種方式最好,消耗內存少,速度又快...但是前提是要提前排序...前面選用的排序去重(Sort Distinct)純屬無奈之舉...

總結下:我們在寫語句的時候能確定為等式連接,最好采用等式連接。還有就是如果能確定輸出條件的最好能寫入,避免多余的書簽查找,還有萬惡的SELEECT *....

如果寫了萬惡的SELECT *...那么你所寫的語句基本上就可以和非聚集索引查找告別了....頂多就是聚集索引掃描或者RID查找...

瞅瞅以下語句

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲欧美精品中文字幕在线| 日韩a**中文字幕| 亚洲欧洲黄色网| 最新中文字幕亚洲| 一区二区国产精品视频| 久久久久久成人精品| 欧美成人全部免费| 97超级碰碰人国产在线观看| 黄色精品在线看| 在线一区二区日韩| 久久这里有精品| 川上优av一区二区线观看| 久久在精品线影院精品国产| 欧美激情性做爰免费视频| 亚洲精品中文字幕有码专区| 国产91精品黑色丝袜高跟鞋| 亚洲欧美在线一区二区| 尤物yw午夜国产精品视频| 久久成人精品电影| 欧美性做爰毛片| 日韩国产精品一区| 亚洲图中文字幕| 精品一区二区三区三区| 日韩精品在线视频美女| 2019中文字幕在线| 亚洲欧美国产制服动漫| 成人国产精品av| 国产精品电影网站| 国产深夜精品福利| 国外成人在线播放| 久久久999精品视频| 日韩欧美在线免费| 国模私拍一区二区三区| 97超级碰碰人国产在线观看| 欧美美最猛性xxxxxx| 中文国产成人精品久久一| 久久精品国亚洲| 亚洲天堂网在线观看| 91精品久久久久| 欧美亚洲免费电影| 欧美丰满老妇厨房牲生活| 美女国内精品自产拍在线播放| 在线观看久久av| 992tv在线成人免费观看| 深夜福利91大全| 九九久久综合网站| 国产精品高潮呻吟久久av野狼| 欧美性xxxx在线播放| 国产精品99一区| 精品一区二区三区四区| 国产精品综合不卡av| 久久久精品在线观看| 欧美日韩国产激情| 91在线观看免费高清完整版在线观看| 45www国产精品网站| 高清欧美电影在线| 91在线视频九色| 国产视频丨精品|在线观看| 国产在线观看精品一区二区三区| 丁香五六月婷婷久久激情| 疯狂欧美牲乱大交777| 亚洲欧美日韩天堂| 成人免费视频在线观看超级碰| 欧美夜福利tv在线| 国产裸体写真av一区二区| 久久国产精品久久国产精品| 国产精品欧美激情| 91精品国产91久久久久久| 中文字幕亚洲欧美一区二区三区| 亚洲欧美日韩中文视频| zzijzzij亚洲日本成熟少妇| 欧美精品18videos性欧美| 亚洲一区二区三区成人在线视频精品| 狠狠综合久久av一区二区小说| 国产精品日日做人人爱| 久久久久久久久久婷婷| 日韩一区二区在线视频| 欧美午夜精品久久久久久人妖| 人人做人人澡人人爽欧美| 欧美中文字幕视频| 91精品视频一区| 精品少妇一区二区30p| 欧美激情免费在线| 欧美在线一区二区三区四| 国产精品视频在线观看| 亚洲第一二三四五区| 欧美午夜精品伦理| 国产精品美女视频网站| 亚洲最新视频在线| 日本韩国欧美精品大片卡二| 成人久久18免费网站图片| 国产中文日韩欧美| 亚洲国产精品久久久久| 国产精品欧美久久久| 久久视频国产精品免费视频在线| 亚洲缚视频在线观看| 日韩在线观看你懂的| 欧洲一区二区视频| 精品五月天久久| 欧美日韩亚洲精品一区二区三区| 亚洲欧美日韩第一区| 欧美激情xxxxx| 欧美自拍视频在线| 久久亚洲成人精品| 91黑丝在线观看| 精品女同一区二区三区在线播放| 欧美另类在线播放| 欧美性xxxxx极品| 欧美激情a∨在线视频播放| 国产国语videosex另类| 亚洲国产精品推荐| 日韩美女激情视频| 亚洲精品网站在线播放gif| 国产精品久久久久久久久男| 国产91亚洲精品| 国产69精品99久久久久久宅男| 91情侣偷在线精品国产| 日韩网站免费观看高清| 亚洲色图欧美制服丝袜另类第一页| 亚洲欧美日韩精品久久奇米色影视| 亚洲石原莉奈一区二区在线观看| 国产女人18毛片水18精品| 日本亚洲欧洲色| 国产精品电影在线观看| 日韩精品极品在线观看| 欧美成人午夜激情在线| 亚洲精品久久久一区二区三区| 欧美精品www在线观看| 欧美亚洲国产视频| 国产精品v日韩精品| 国产一区二区香蕉| 日本一区二区在线免费播放| 中文字幕亚洲欧美日韩2019| 国产亚洲精品久久久久动| 亚洲无亚洲人成网站77777| 亚洲男人的天堂网站| 欧美性猛交丰臀xxxxx网站| 欧美日韩综合视频| 成人国产精品色哟哟| 欧美激情高清视频| 色老头一区二区三区在线观看| 欧美精品做受xxx性少妇| 久久精品久久久久久| 精品国产一区av| 日韩毛片中文字幕| 欧美极品少妇xxxxⅹ免费视频| 亚洲欧美中文字幕| 欧美精品电影在线| 91久久在线观看| 91av网站在线播放| 隔壁老王国产在线精品| 亚洲成年网站在线观看| 91国自产精品中文字幕亚洲| 美女av一区二区三区| 中文字幕亚洲一区在线观看| 日韩一区二区精品视频| 91精品国产综合久久香蕉922| 色老头一区二区三区在线观看| 亚洲一区免费网站| 一区二区三区久久精品| 午夜欧美大片免费观看| 国产97免费视| 久久国产精品久久久久| 国产精品高潮呻吟久久av野狼|