共計 5130 個字符,預計需要花費 13 分鐘才能閱讀完成。
今天就跟大家聊聊有關如何分析 SQL Server 中的 SQL 語句優化與效率問題,可能很多人都不太了解,為了讓大家更加了解,丸趣 TV 小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
很多人不知道 SQL 語句在 SQL SERVER 中是如何執行的,他們擔心自己所寫的 SQL 語句會被 SQL SERVER 誤解。比如:
select * from table1 where name= zhangsan and tID 10000
和執行:
select * from table1 where tID 10000 and name= zhangsan
一些人不知道以上兩條語句的執行效率是否一樣,因為如果簡單的從語句先后上看,這兩個語句的確是不一樣,如果 tID 是一個聚合索引,那么后一句僅僅從表的 10000 條以后的記錄中查找就行了;而前一句則要先從全表中查找看有幾個 name= zhangsan 的,而后再根據限制條件條件 tID 10000 來提出查詢結果。
事實上,這樣的擔心是不必要的。SQL SERVER 中有一個“查詢分析優化器”,它可以計算出 where 子句中的搜索條件并確定哪個索引能縮小表掃描的搜索空間,也就是說,它能實現自動優化。
雖然查詢優化器可以根據 where 子句自動的進行查詢優化,但大家仍然有必要了解一下“查詢優化器”的工作原理,如非這樣,有時查詢優化器就會不按照您的本意進行快速查詢。
在查詢分析階段,查詢優化器查看查詢的每個階段并決定限制需要掃描的數據量是否有用。如果一個階段可以被用作一個掃描參數(SARG),那么就稱之為可優化的,并且可以利用索引快速獲得所需數據。
SARG 的定義:用于限制搜索的一個操作,因為它通常是指一個特定的匹配,一個值得范圍內的匹配或者兩個以上條件的 AND 連接。
形式如下:
列名 操作符 常數 或 變量
或
常數 或 變量 操作符列名列名可以出現在操作符的一邊,而常數或變量出現在操作符的另一邊。如:
Name= 張三
價格 5000
5000 價格
Name= 張三 and 價格 5000
如果一個表達式不能滿足 SARG 的形式,那它就無法限制搜索的范圍了,也就是 SQL SERVER 必須對每一行都判斷它是否滿足 WHERE 子句中的所有條件。所以一個索引對于不滿足 SARG 形式的表達式來說是無用的。
介紹完 SARG 后,我們來總結一下使用 SARG 以及在實踐中遇到的和某些資料上結論不同的經驗:
1、Like 語句是否屬于 SARG 取決于所使用的通配符的類型
– 如:name like 張 % –,這就屬于 SARG
– 而:name like % 張 –, 就不屬于 SARG。
原因是通配符 % 在字符串的開通使得索引無法使用。
2、or 會引起全表掃描
Name= 張三 and 價格 5000 符號 SARG,而:Name= 張三 or 價格 5000 則不符合 SARG。使用 or 會引起全表掃描。
3、非操作符、函數引起的不滿足 SARG 形式的語句
不滿足 SARG 形式的語句最典型的情況就是包括非操作符的語句,如:NOT、!=、、!、!、NOT EXISTS、NOT IN、NOT LIKE 等,另外還有函數。
下面就是幾個不滿足 SARG 形式的例子:
ABS(價格) 5000
Name like % 三
– 有些表達式,如:
WHERE 價格 *2 5000
–SQL SERVER 也會認為是 SARG,SQL SERVER 會將此式轉化為:WHERE 價格 2500/2
但我們不推薦這樣使用,因為有時 SQL SERVER 不能保證這種轉化與原始表達式是完全等價的。
4、IN 的作用相當與 OR
語句:
Select * from table1 where tid in (2,3)
– 和
Select * from table1 where tid=2 or tid=3
是一樣的,都會引起全表掃描,如果 tid 上有索引,其索引也會失效。
5、盡量少用 NOT
6、exists 和 in 的執行效率是一樣的
很多資料上都顯示說,exists 要比 in 的執行效率要高,同時應盡可能的用 not exists 來代替 not in。但事實上,我試驗了一下,發現二者無論是前面帶不帶 not,二者之間的執行效率都是一樣的。因為涉及子查詢,我們試驗這次用 SQL SERVER 自帶的 pubs 數據庫。運行前我們可以把 SQL SERVER 的 statistics I/ O 狀態打開:
(1)select title,price from titles where title_id in (select title_id from sales where qty 30)
該句的執行結果為:
表 sales。掃描計數 18,邏輯讀 56 次,物理讀 0 次,預讀 0 次。表 titles。掃描計數 1,邏輯讀 2 次,物理讀 0 次,預讀 0 次。
(2)select title,price from titles where exists (select * from sales where sales.title_id=titles.title_id and qty 30)
第二句的執行結果為:
表 sales。掃描計數 18,邏輯讀 56 次,物理讀 0 次,預讀 0 次。表 titles。掃描計數 1,邏輯讀 2 次,物理讀 0 次,預讀 0 次。
我們從此可以看到用 exists 和用 in 的執行效率是一樣的。
7、用函數 charindex() 和前面加通配符 % 的 LIKE 執行效率一樣
前面,我們談到,如果在 LIKE 前面加上通配符 %,那么將會引起全表掃描,所以其執行效率是低下的。但有的資料介紹說,用函數 charindex() 來代替 LIKE 速度會有大的提升,經我試驗,發現這種說明也是錯誤的:
select gid,title,fariqi,reader from tgongwen where charindex(刑偵支隊 ,reader) 0 and fariqi 2004-5-5
用時:7 秒,另外:掃描計數 4,邏輯讀 7155 次,物理讀 0 次,預讀 0 次。
select gid,title,fariqi,reader from tgongwen where reader like % + 刑偵支隊 + % and fariqi 2004-5-5
用時:7 秒,另外:掃描計數 4,邏輯讀 7155 次,物理讀 0 次,預讀 0 次。
8、union 并不絕對比 or 的執行效率高
我們前面已經談到了在 where 子句中使用 or 會引起全表掃描,一般的,我所見過的資料都是推薦這里用 union 來代替 or。事實證明,這種說法對于大部分都是適用的。
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi= 2004-9-16 or gid 9990000
用時:68 秒。掃描計數 1,邏輯讀 404008 次,物理讀 283 次,預讀 392163 次。
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi= 2004-9-16 unionselect gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid 9990000
用時:9 秒。掃描計數 8,邏輯讀 67489 次,物理讀 216 次,預讀 7499 次。
看來,用 union 在通常情況下比用 or 的效率要高的多。
但經過試驗,筆者發現如果 or 兩邊的查詢列是一樣的話,那么用 union 則反倒和用 or 的執行速度差很多,雖然這里 union 掃描的是索引,而 or 掃描的是全表。
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi= 2004-9-16 or fariqi= 2004-2-5
用時:6423 毫秒。掃描計數 2,邏輯讀 14726 次,物理讀 1 次,預讀 7176 次。
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi= 2004-9-16 unionselect gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi= 2004-2-5
用時:11640 毫秒。掃描計數 8,邏輯讀 14806 次,物理讀 108 次,預讀 1144 次。
9、字段提取要按照“需多少、提多少”的原則,避免“select *”
我們來做一個試驗:
select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc 用時:4673 毫秒 select top 10000 gid,fariqi,title from tgongwen order by gid desc 用時:1376 毫秒 select top 10000 gid,fariqi from tgongwen order by gid desc 用時:80 毫秒
由此看來,我們每少提取一個字段,數據的提取速度就會有相應的提升。提升的速度還要看您舍棄的字段的大小來判斷。
10、count(*) 不比 count(字段) 慢
某些資料上說:用 * 會統計所有列,顯然要比一個世界的列名效率低。這種說法其實是沒有根據的。我們來看:
select count(*) from Tgongwen 用時:1500 毫秒
select count(gid) from Tgongwen 用時:1483 毫秒
select count(fariqi) from Tgongwen 用時:3140 毫秒
select count(title) from Tgongwen 用時:52050 毫秒
從以上可以看出,如果用 count(*) 和用 count(主鍵) 的速度是相當的,而 count(*) 卻比其他任何除主鍵以外的字段匯總速度要快,而且字段越長,匯總的速度就越慢。我想,如果用 count(*),SQL SERVER 可能會自動查找最小字段來匯總的。當然,如果您直接寫 count(主鍵) 將會來的更直接些。
11、order by 按聚集索引列排序效率最高
我們來看:(gid 是主鍵,fariqi 是聚合索引列):
select top 10000 gid,fariqi,reader,title from tgongwen
用時:196 毫秒。掃描計數 1,邏輯讀 289 次,物理讀 1 次,預讀 1527 次。
select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc
用時:4720 毫秒。掃描計數 1,邏輯讀 41956 次,物理讀 0 次,預讀 1287 次。
select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc 用時:4736 毫秒。掃描計數 1,邏輯讀 55350 次,物理讀 10 次,預讀 775 次。
select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc 用時:173 毫秒。掃描計數 1,邏輯讀 290 次,物理讀 0 次,預讀 0 次。
select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc 用時:156 毫秒。掃描計數 1,邏輯讀 289 次,物理讀 0 次,預讀 0 次。
從以上我們可以看出,不排序的速度以及邏輯讀次數都是和“order by 聚集索引列”的速度是相當的,但這些都比“order by 非聚集索引列”的查詢速度是快得多的。
同時,按照某個字段進行排序的時候,無論是正序還是倒序,速度是基本相當的。
12、高效的 TOP
事實上,在查詢和提取超大容量的數據集時,影響數據庫響應時間的最大因素不是數據查找,而是物理的 I / 0 操作。如:
select top 10 * from (select top 10000 gid,fariqi,title from tgongwenwhere neibuyonghu= 辦公室 order by gid desc) as aorder by gid asc
這條語句,從理論上講,整條語句的執行時間應該比子句的執行時間長,但事實相反。因為,子句執行后返回的是 10000 條記錄,而整條語句僅返回 10 條語句,所以影響數據庫響應時間最大的因素是物理 I / O 操作。而限制物理 I / O 操作此處的最有效方法之一就是使用 TOP 關鍵詞了。TOP 關鍵詞是 SQL SERVER 中經過系統優化過的一個用來提取前幾條或前幾個百分比數據的詞。經筆者在實踐中的應用,發現 TOP 確實很好用,效率也很高。但這個詞在另外一個大型數據庫 ORACLE 中卻沒有,這不能說不是一個遺憾,雖然在 ORACLE 中可以用其他方法(如:rownumber)來解決。
看完上述內容,你們對如何分析 SQL Server 中的 SQL 語句優化與效率問題有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注丸趣 TV 行業資訊頻道,感謝大家的支持。