首頁 資料庫 mysql教程 sql查詢語句最佳化

sql查詢語句最佳化

Dec 03, 2016 am 10:18 AM
sql sql查詢

 目錄

1、什麼是執行計畫?執行計劃是依賴什麼資訊。
2、 統一SQL語句的寫法減少解析開銷
3、 減少SQL語句的巢狀
4、 使用「臨時表」暫存中間結果
5、 OLTP系統SQL語句必須採用綁定變數
6、傾斜欄位的綁定變數窺測問題
7、 begin tran的事務要盡量小。
8、 一些SQL查詢語句應加上nolock
9、加nolock後查詢經常發生頁分裂的表,容易產生跳讀或重複讀
10、聚集索引沒有建在表的順序字段上,該表容易發生頁分割
11、使用複合索引提高多個where條件的查詢速度
13、使用like進行模糊查詢時應注意盡量不要使用前%
14、SQL Server 表連接的三種方式
15、Row_number 會導致表掃描,用臨時表分頁比較好

 什麼是執行計畫?執行計劃是依賴什麼資訊。

執行計劃是資料庫根據SQL語句和相關表格的統計資料所做的查詢方案,這個方案是由查詢最佳化器自動分析產生的,例如一條SQL語句如果用來從一個10萬筆記錄的表中查1筆記錄,那查詢優化器會選擇「索引查找」方式,如果該表進行了歸檔,目前只剩下5000筆記錄了,那查詢優化器就會改變方案,採用「全表掃描」方式。

可見,執行計畫並不是固定的,它是「個人化」的。產生一個正確的「執行計畫」有兩點很重要:
SQL語句是否清楚告訴查詢最佳化器它想做什麼?
查詢優化器所得到的資料庫統計資料是否是最新的、正確的?

統一SQL語句的寫法減少解析開銷

對於以下兩句SQL語句,程式設計師認為是相同的,資料庫查詢最佳化器可能認為是不同的。

select * from dual

Select * From dual

其實就是大小寫不同,查詢分析器認為是兩句不同的SQL語句,必須進行兩次解析。產生2個執行計劃。所以身為程式設計師,應該要保證相同的查詢語句在任何地方都一致,多一個空格都不行!

減少SQL語句的巢狀

我常看到,從資料庫捕捉到的一條SQL語句印出來有2張A4紙這麼長。一般來說這麼複雜的語句通常都是有問題的。我拿著這2頁長的SQL語句去請教原作者,結果他說時間太長,他一時也看不懂了。可想而知,連原作者都有可能看糊塗的SQL語句,資料庫也一樣會看糊塗。

一般,將一個Select語句的結果作為子集,然後從該子集中再進行查詢,這種一層嵌套語句還是比較常見的,但是根據經驗,超過3層嵌套,查詢優化器就很容易給出錯誤的執行計劃。因為它被繞暈了。像這種類似人工智慧的東西,終究比人的分辨力要差些,如果人都看暈了,我可以保證資料庫也會暈的。

另外,執行計畫是可以重複使用的,越簡單的SQL語句被重複使用的可能性越高。而複雜的SQL語句只要有一個字元改變就必須重新解析,然後再把這一大堆垃圾塞在記憶體裡。可想而知,資料庫的效率會何等低下。

使用「臨時表」暫存中間結果

 簡化SQL語句的重要方法就是採用臨時表暫存中間結果,但是,臨時表的好處遠不止這些,將臨時結果暫存在臨時表,後面的查詢就在tempdb中了,這可以避免程式中多次掃描主表,也大大減少了程式執行中“共享鎖”阻塞“更新鎖”,減少了阻塞,提高了並發性能。

OLTP系統SQL語句必須採用綁定變數

select * from orderheader where changetime > '2010-10-20 00:00:01'
select * from orderheader where changetime > '2010-09-2012 009-20:20:00: 01'
以上兩句語句,查詢最佳化器認為是不同的SQL語句,需要解析兩次。如果採用綁定變數
select * from orderheader where changetime > @chgtime
@chgtime變數可以傳入任何值,這樣大量的類似查詢可以重複使用該執行計劃了,這可以大大降低資料庫解析SQL語句的負擔。一次解析,多次重複使用,是提高資料庫效率的原則。

 傾斜字段的綁定變數窺測問題

事物都存在兩面性,綁定變數對大多數OLTP處理是適用的,但是也有例外。例如where條件中的欄位是「傾斜欄位」的時候。

「傾斜字段」指該列中的絕大多數的值都是相同的,例如一張人口調查表,其中「民族」這列,90%以上都是漢族。那如果一個SQL語句要查詢30歲的漢族人口有多少,那麼「民族」這列必然要放在where條件裡。這時候如果採用綁定變數@nation會存在很大問題。

試想如果@nation傳入的第一個值是“漢族”,那麼整個執行計劃必然會選擇表掃描。然後,第二個值傳入的是“布依族”,按理說“布依族”佔的比例可能只有萬分之一,應該採用索引查找。但是,由於重用了第一次解析的「漢族」的那個執行計劃,那麼第二次也將採用表格掃描方式。這個問題就是著名的“綁定變數窺測”,建議對於“傾斜欄位”不要採用綁定變數。

begin tran的事務要盡量小

SQL Server中一句SQL語句預設就是一個事務,在該語句執行完成後也是預設commit的。其實,這就是begin tran的一個最小化的形式,好比在每句語句開頭隱含了一個begin tran,結束時隱含了一個commit。
有些情況下,我們需要明確聲明begin tran,例如做「插、刪、改」操作需要同時修改幾個表,要求要嘛幾個表都修改成功,要嘛都不成功。 begin tran 可以起到這樣的作用,它可以把若干SQL語句套在一起執行,最後再一起commit。好處是保證了數據的一致性,但任何事情都不是完美無缺的。 Begin tran付出的代價是在提交之前,所有SQL語句鎖住的資源都不能釋放,直到commit掉。
可見,如果Begin tran套住的SQL語句太多,那資料庫的效能就糟了。在該大事務提交之前,必然會阻塞別的語句,造成block很多。
Begin tran所使用的原則是,在確保資料一致性的前提下,begin tran 套住的SQL語句越少越好!有些情況下可以採用觸發器同步數據,不一定要用begin tran。

一些SQL查詢語句應加上nolock

在SQL語句中加nolock是提高SQL Server並發效能的重要手段,在oracle中並不需要這樣做,因為oracle的結構更為合理,有undo表空間保存“資料前影”,該資料如果在修改中還未commit,那麼你讀到的是它修改之前的副本,該副本放在undo表空間中。這樣,oracle的讀、寫可以做到互不影響,這也是oracle廣受稱讚的地方。 SQL Server 的讀取、寫入是會互相阻塞的,為了提高並發效能,對於某些查詢,可以加上nolock,這樣讀的時候可以允許寫,但缺點是可能讀到未提交的髒資料。使用nolock有3個原則。

(1) 查詢的結果用於「插、刪、改」的不能加上nolock !

(2) 查詢的表格屬於頻繁發生頁分裂的,慎用nolock !

(3) 使用臨時表一樣可以保存“數據前影”,起到類似oracle的undo表空間的功能,

能採用臨時表提高並發性能的,不要用nolock 。

加nolock後查詢經常發生頁分裂的表,容易產生跳讀或重複讀取

加nolock後可以在“插、刪、改”的同時進行查詢,但是由於同時發生“插、刪、改” ,在某些情況下,一旦該資料頁滿了,那麼頁分裂不可避免,而此時nolock的查詢正在發生,例如在第100頁已經讀過的記錄,可能會因為頁分裂而分到第101頁,這有可能使得nolock查詢在讀101頁時重複讀到該條數據,產生「重複讀取」。同理,如果在100頁的資料還沒被讀到就分到99頁去了,那nolock查詢有可能會漏過該記錄,產生「跳讀」。

 上面提到的哥們,在加了nolock後一些操作出現報錯,估計有可能因為nolock查詢產生了重複讀,2條相同的記錄去插入別的表,當然會發生主鍵衝突。

聚集索引沒有建在表的順序欄位上,該表容易發生頁分裂

例如訂單表,有訂單編號orderid,也有客戶編號contactid,那麼聚集索引應該加在哪個欄位上呢?對於該表,訂單編號是順序添加的,如果在orderid上加聚集索引,新增的行都是添加在末尾,這樣不容易經常產生頁分裂。然而,由於大多數查詢都是根據客戶編號來檢查的,因此,將聚集索引加在contactid上才有意義。而contactid對於訂單表而言,並非順序欄位。

例如「張三」的「contactid」是001,那麼「張三」的訂單資訊必須都放在這張表的第一個資料頁上,如果今天「張三」新下了一個訂單,那該訂單資訊不能放在表格的最後一頁,而是第一頁!如果第一頁放滿了呢?很抱歉,表格所有資料都要往後移動為這條記錄騰地方。

SQL Server的索引和Oracle的索引是不同的,SQL Server的聚集索引實際上是對資料表按照聚集索引欄位的順序進行了排序,相當於oracle的索引組織表。 SQL Server的聚集索引就是表格本身的一種組織形式,所以它的效率是非常高的。也因為此,插入一筆記錄,它的位置不是隨便放的,而是要按照順序放在該放的資料頁,如果那個資料頁沒有空間了,就引起了頁分裂。所以很顯然,聚集索引沒有建在表的順序欄位上,該表容易發生頁分裂。

曾經碰到過一個情況,一位哥們的某張表重建索引後,插入的效率大幅下降了。估計情況大概是這樣的。該表的聚集索引可能沒有建在表的順序欄位上,該表經常被歸檔,所以該表的資料是以一種稀疏狀態存在的。例如張三下過20張訂單,而最近3個月的訂單只有5張,歸檔策略是保留3個月數據,那麼張三過去的15張訂單已經被歸檔,留下15個空位,可以在insert發生時重新被利用。在這種情況下由於有空位可以利用,就不會發生頁分裂。但是查詢效能會比較低,因為查詢時必須掃描那些沒有資料的空位。

重建聚集索引後情況改變了,因為重建聚集索引就是把表中的資料重新排列一遍,原來的空位沒有了,而頁的填充率又很高,插入資料經常要發生頁分裂,所以效能大幅下降。

對於聚集索引沒有建在順序欄位上的表,是否要給與比較低的頁填充率?是否要避免重建聚集索引?是一個值得考慮的問題!

使用複合索引提高多個where條件的查詢速度

複合索引通常擁有比單一索引更好的選擇性。而且,它是特別針對某個where條件所設立的索引,它已經進行了排序,所以查詢速度比單一索引更快。複合索引的引導欄位必須採用「選擇性高」的欄位。例如有3個欄位:日期,性別,年齡。大家看,應該採用哪個欄位作引導欄位?顯然應該採用「日期」作為引導欄位。日期是3個欄位中選擇性最高的欄位。

這裡有一個例外,如果日期同時也是聚集索引的引導字段,可以不建複合索引,直接走聚集索引,效率也是比較高的。

不要把聚集索引建成“複合索引”,聚集索引越簡單越好,選擇性越高越好!聚集索引包括2個欄位尚可容忍。但是超過2個字段,應該考慮建1個自增字段作為主鍵,聚集索引可以不做主鍵。

使用like進行模糊查詢時應注意盡量不要使用前%

有的時候會需要進行一些模糊查詢例如

 Select * from contact where username like)%yue%' % yue前面使用了“%”,因此查詢必然走全表掃描,除非必要,否則不要在關鍵字前加%,

SQL Server 表連接的三種方式

   (1) Merge Join

   (1) Merge Join

   ) Nested Loop Join 

    (3) Hash Join 

SQL Server 2000只有一種join方式-Nested Loop Join,如果AA結果結果不小,則為每個外表記錄掃描一遍,實際掃過的行數相當於A結果集行數x B結果集行數。所以如果兩個結果集都很大,那Join的結果很糟糕。

SQL Server 2005新增了Merge Join,如果A表和B表的連接字段正好是聚集索引所在字段,那麼表的順序已經排好,只要兩邊拼上去就行了,這種join的開銷相當於A表的結果集行數加上B表的結果集行數,一個是加,一個是乘,可見merge join 的效果比Nested Loop Join好多了。

如果連接的字段上沒有索引,那麼SQL2000的效率是相當低的,而SQL2005提供了Hash join,相當於臨時給A,B表的結果集加上索引,因此SQL2005的效率比SQL2000有很大提高,我認為,這是一個重要的原因。

總結一下,在表格連接時要注意以下幾點:

(1) 連接欄位盡量選擇聚集索引所在的欄位

(2) 仔細考慮where條件,盡量減小A、B表的結果集

(3) 如果很多join的連接欄位都缺少索引,而你還在用SQL2000,乾緊升級吧.

Row_number 會導致表格掃描,用臨時表分頁更好


ROW_Number分頁的測試結果:
使用ROW_Number來分頁:CPU 時間= 317265 毫秒,佔用時間= 423090 毫秒

使用臨時表來分頁:CPU 時間= 1266 毫秒,佔用時間= 6705 毫秒

ROW_Number實作是基於order by的,而排序的影響。

其他

諸如有的寫法會限制使用索引 🎜

從表名中選擇 *,其中 chgdate +7

從表名中選擇 *,其中 chgdate


本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Hibernate 框架中 HQL 和 SQL 的差異是什麼? Hibernate 框架中 HQL 和 SQL 的差異是什麼? Apr 17, 2024 pm 02:57 PM

HQL和SQL在Hibernate框架中進行比較:HQL(1.物件導向語法,2.資料庫無關的查詢,3.類型安全),而SQL直接操作資料庫(1.與資料庫無關的標準,2.可執行複雜查詢和資料操作)。

Oracle SQL中除法運算的用法 Oracle SQL中除法運算的用法 Mar 10, 2024 pm 03:06 PM

《OracleSQL中除法運算的用法》在OracleSQL中,除法運算是常見的數學運算之一。在資料查詢和處理過程中,除法運算可以幫助我們計算欄位之間的比例或得出特定數值的邏輯關係。本文將介紹OracleSQL中除法運算的用法,並提供具體的程式碼範例。一、OracleSQL中除法運算的兩種方式在OracleSQL中,除法運算可以用兩種不同的方式來進行

Oracle與DB2的SQL語法比較與區別 Oracle與DB2的SQL語法比較與區別 Mar 11, 2024 pm 12:09 PM

Oracle和DB2是兩個常用的關聯式資料庫管理系統,它們都有自己獨特的SQL語法和特性。本文將針對Oracle和DB2的SQL語法進行比較與區別,並提供具體的程式碼範例。資料庫連接在Oracle中,使用以下語句連接資料庫:CONNECTusername/password@database而在DB2中,連接資料庫的語句如下:CONNECTTOdataba

詳解MyBatis動態SQL標籤中的Set標籤功能 詳解MyBatis動態SQL標籤中的Set標籤功能 Feb 26, 2024 pm 07:48 PM

MyBatis動態SQL標籤解讀:Set標籤用法詳解MyBatis是一個優秀的持久層框架,它提供了豐富的動態SQL標籤,可以靈活地建構資料庫操作語句。其中,Set標籤是用來產生UPDATE語句中SET子句的標籤,在更新作業中非常常用。本文將詳細解讀MyBatis中Set標籤的用法,以及透過具體的程式碼範例來示範其功能。什麼是Set標籤Set標籤用於MyBati

SQL中的identity屬性是什麼意思? SQL中的identity屬性是什麼意思? Feb 19, 2024 am 11:24 AM

SQL中的Identity是什麼,需要具體程式碼範例在SQL中,Identity是一種用於產生自增數字的特殊資料類型,它常用於唯一識別表中的每一行資料。 Identity欄位通常與主鍵列搭配使用,可確保每筆記錄都有獨一無二的識別碼。本文將詳細介紹Identity的使用方式以及一些實際的程式碼範例。 Identity的基本使用方式在建立表格時,可以使用Identit

SQL出現5120錯誤怎麼解決 SQL出現5120錯誤怎麼解決 Mar 06, 2024 pm 04:33 PM

解決方法:1、檢查登入使用者是否具有足夠的權限來存取或操作該資料庫,確保該使用者俱有正確的權限;2、檢查SQL Server服務的帳戶是否具有存取指定檔案或資料夾的權限,確保該帳戶具有足夠的權限來讀取和寫入該文件或資料夾;3、檢查指定的資料庫文件是否已被其他進程打開或鎖定,嘗試關閉或釋放該文件,並重新運行查詢;4、嘗試以管理員身份運行Management Studio等等。

如何使用SQL語句在MySQL中進行資料聚合和統計? 如何使用SQL語句在MySQL中進行資料聚合和統計? Dec 17, 2023 am 08:41 AM

如何使用SQL語句在MySQL中進行資料聚合和統計?在進行資料分析和統計時,資料聚合和統計是非常重要的步驟。 MySQL作為一個功能強大的關聯式資料庫管理系統,提供了豐富的聚合和統計函數,可以很方便地進行資料聚合和統計操作。本文將介紹使用SQL語句在MySQL中進行資料聚合和統計的方法,並提供具體的程式碼範例。一、使用COUNT函數進行計數COUNT函數是最常用

資料庫技術大比拼:Oracle和SQL的差別有哪些? 資料庫技術大比拼:Oracle和SQL的差別有哪些? Mar 09, 2024 am 08:30 AM

資料庫技術大比拼:Oracle和SQL的差別有哪些?在資料庫領域中,Oracle和SQLServer是兩種備受推崇的關聯式資料庫管理系統。儘管它們都屬於關係型資料庫的範疇,但兩者之間存在著許多不同之處。在本文中,我們將深入探討Oracle和SQLServer之間的區別,以及它們在實際應用中的特徵和優勢。首先,Oracle和SQLServer在語法方面存

See all articles