
sql語(yǔ)句優(yōu)化的13中方法
1,什么是“執(zhí)行計(jì)劃”?
執(zhí)行計(jì)劃是數(shù)據(jù)庫(kù)根據(jù)SQL語(yǔ)句和相關(guān)表的統(tǒng)計(jì)信息作出的一個(gè)查詢(xún)方案,這個(gè)方案是由查詢(xún)優(yōu)化器自動(dòng)分析產(chǎn)生的,比如一條SQL語(yǔ)句如果用來(lái)從一個(gè) 10萬(wàn)條記錄的表中查1條記錄,那查詢(xún)優(yōu)化器會(huì)選擇“索引查找”方式,如果該表進(jìn)行了歸檔,當(dāng)前只剩下5000條記錄了,那查詢(xún)優(yōu)化器就會(huì)改變方案,采用 “全表掃描”方式。
可見(jiàn),執(zhí)行計(jì)劃并不是固定的,它是“帶有相當(dāng)個(gè)性的”。如何產(chǎn)生一個(gè)正確的執(zhí)行計(jì)劃呢?
(1) SQL語(yǔ)句是否清晰地告訴查詢(xún)優(yōu)化器它想干什么?
(2) 查詢(xún)優(yōu)化器得到的數(shù)據(jù)庫(kù)統(tǒng)計(jì)信息是否是最新的、正確的?
2、 如何寫(xiě)出統(tǒng)一SQL語(yǔ)句
對(duì)于以下兩句SQL語(yǔ)句,很多人人認(rèn)為是相同的,但是,數(shù)據(jù)庫(kù)查詢(xún)優(yōu)化器認(rèn)為是不同的。
select * from dual
select * From dual
雖然只是大小寫(xiě)不同,查詢(xún)分析器就認(rèn)為是兩句不同的SQL語(yǔ)句,必須進(jìn)行兩次解析。生成2個(gè)執(zhí)行計(jì)劃。所以作為程序員,應(yīng)該保證相同的查詢(xún)語(yǔ)句在任何地方都一致,多一個(gè)空格都不行!
3、 不要把SQL語(yǔ)句寫(xiě)得太長(zhǎng),太過(guò)冗余
一般,將一個(gè)Select語(yǔ)句的結(jié)果作為子集,然后從該子集中再進(jìn)行查詢(xún),這種一層嵌套語(yǔ)句還是比較常見(jiàn)的,但是根據(jù)經(jīng)驗(yàn),超過(guò)3層嵌套,查詢(xún)優(yōu)化器就很容易給出錯(cuò)誤的執(zhí)行計(jì)劃。因?yàn)樗焕@暈了。像這種類(lèi)似人工智能的東西,終究比人的分辨力要差些,如果人都看暈了,我可以保證數(shù)據(jù)庫(kù)也會(huì)暈的。
另外,執(zhí)行計(jì)劃是可以被重用的,越簡(jiǎn)單的SQL語(yǔ)句被重用的可能性越高。而復(fù)雜的SQL語(yǔ)句只要有一個(gè)字符發(fā)生變化就必須重新解析,然后再把這一大堆垃圾塞在內(nèi)存里??上攵?,數(shù)據(jù)庫(kù)的效率會(huì)何等低下。
4、考慮使用“臨時(shí)表”暫存中間結(jié)果
簡(jiǎn)化SQL語(yǔ)句的重要方法就是采用臨時(shí)表暫存中間結(jié)果,但是,臨時(shí)表的好處遠(yuǎn)遠(yuǎn)不止這些,將臨時(shí)結(jié)果暫存在臨時(shí)表,后面的查詢(xún)就在tempdb中了,這可以避免程序中多次掃描主表,也大大減少了程序執(zhí)行中“共享鎖”阻塞“更新鎖”,減少了阻塞,提高了并發(fā)性能。
5、 OLTP系統(tǒng)SQL語(yǔ)句必須采用綁定變量
select * from orderheader where changetime >’2010-10-20 00:00:01′
select * from orderheader where changetime >’2010-09-22 00:00:01′
以上兩句語(yǔ)句,查詢(xún)優(yōu)化器認(rèn)為是不同的SQL語(yǔ)句,需要解析兩次。如果采用綁定變量
select*from orderheader where changetime >@chgtime
@chgtime變量可以傳入任何值,這樣大量的類(lèi)似查詢(xún)可以重用該執(zhí)行計(jì)劃了,可以大大降低數(shù)據(jù)庫(kù)解析SQL語(yǔ)句的負(fù)擔(dān)。一次解析,多次重用,是提高數(shù)據(jù)庫(kù)效率的原則。
6、 綁定變量窺測(cè)
事物都存在兩面性,綁定變量對(duì)大多數(shù)OLTP處理是適用的,但是也有例外。比如在where條件中的字段是“傾斜字段”的時(shí)候。
“傾斜字段”指該列中的絕大多數(shù)的值都是相同的,比如一張人口調(diào)查表,其中“民族”這列,90%以上都是漢族。那么如果一個(gè)SQL語(yǔ)句要查詢(xún)30歲的漢族人口有多少,那“民族”這列必然要被放在where條件中。這個(gè)時(shí)候如果采用綁定變量@nation會(huì)存在很大問(wèn)題。
試想如果@nation傳入的第一個(gè)值是“漢族”,那整個(gè)執(zhí)行計(jì)劃必然會(huì)選擇表掃描。然后,第二個(gè)值傳入的是“布依族”,按理說(shuō)“布依族”占的比例可能只有萬(wàn)分之一,應(yīng)該采用索引查找。但是,由于重用了第一次解析的“漢族”的那個(gè)執(zhí)行計(jì)劃,那么第二次也將采用表掃描方式。這個(gè)問(wèn)題就是著名的“綁定變量窺測(cè)”,建議對(duì)于“傾斜字段”不要采用綁定變量。
7、 只在必要的情況下才使用begin tran
SQL Server中一句SQL語(yǔ)句默認(rèn)就是一個(gè)事務(wù),在該語(yǔ)句執(zhí)行完成后也是默認(rèn)commit的。其實(shí),這就是begin tran的一個(gè)最小化的形式,好比在每句語(yǔ)句開(kāi)頭隱含了一個(gè)begin tran,結(jié)束時(shí)隱含了一個(gè)commit。
有些情況下,我們需要顯式聲明begin tran,比如做“插、刪、改”操作需要同時(shí)修改幾個(gè)表,要求要么幾個(gè)表都修改成功,要么都不成功。begin tran 可以起到這樣的作用,它可以把若干SQL語(yǔ)句套在一起執(zhí)行,最后再一起commit。 好處是保證了數(shù)據(jù)的一致性,但任何事情都不是完美無(wú)缺的。Begin tran付出的代價(jià)是在提交之前,所有SQL語(yǔ)句鎖住的資源都不能釋放,直到commit掉。
可見(jiàn),如果Begin tran套住的SQL語(yǔ)句太多,那數(shù)據(jù)庫(kù)的性能就糟糕了。在該大事務(wù)提交之前,必然會(huì)阻塞別的語(yǔ)句,造成block很多。
Begin tran使用的原則是,在保證數(shù)據(jù)一致性的前提下,begin tran 套住的SQL語(yǔ)句越少越好!有些情況下可以采用觸發(fā)器同步數(shù)據(jù),不一定要用begin tran。
8、 部分SQL查詢(xún)語(yǔ)句加上nolock
在SQL語(yǔ)句中加nolock是提高SQL Server并發(fā)性能的重要手段,在oracle中并不需要這樣做,因?yàn)閛racle的結(jié)構(gòu)更為合理,有undo表空間保存“數(shù)據(jù)前影”,該數(shù)據(jù)如果在修改中還未commit,那么你讀到的是它修改之前的副本,該副本放在undo表空間中。這樣,oracle的讀、寫(xiě)可以做到互不影響,這也是oracle 廣受稱(chēng)贊的地方。SQL Server 的讀、寫(xiě)是會(huì)相互阻塞的,為了提高并發(fā)性能,對(duì)于一些查詢(xún),可以加上nolock,這樣讀的時(shí)候可以允許寫(xiě),但缺點(diǎn)是可能讀到未提交的臟數(shù)據(jù)。使用 nolock有3條原則。
查詢(xún)的結(jié)果用于“插、刪、改”的不能加nolock !
查詢(xún)的表屬于頻繁發(fā)生頁(yè)分裂的,慎用nolock !
使用臨時(shí)表一樣可以保存“數(shù)據(jù)前影”,起到類(lèi)似oracle的undo表空間的功能,能采用臨時(shí)表提高并發(fā)性能的,不要用nolock 。
9、 聚集索引沒(méi)有建在表的順序字段上,該表容易發(fā)生頁(yè)分裂
比如訂單表,有訂單編號(hào)orderid,也有客戶(hù)編號(hào)contactid,那么聚集索引應(yīng)該加在哪個(gè)字段上呢?對(duì)于該表,訂單編號(hào)是順序添加的,如果在orderid上加聚集索引,新增的行都是添加在末尾,這樣不容易經(jīng)常產(chǎn)生頁(yè)分裂。然而,由于大多數(shù)查詢(xún)都是根據(jù)客戶(hù)編號(hào)來(lái)查的,因此,將聚集索引加在contactid上才有意義。而contactid對(duì)于訂單表而言,并非順序字段。
比如“張三”的“contactid”是001,那么“張三”的訂單信息必須都放在這張表的第一個(gè)數(shù)據(jù)頁(yè)上,如果今天“張三”新下了一個(gè)訂單,那該訂單信息不能放在表的最后一頁(yè),而是第一頁(yè)!如果第一頁(yè)放滿(mǎn)了呢?很抱歉,該表所有數(shù)據(jù)都要往后移動(dòng)為這條記錄騰地方。
SQL Server的索引和Oracle的索引是不同的,SQL Server的聚集索引實(shí)際上是對(duì)表按照聚集索引字段的順序進(jìn)行了排序,相當(dāng)于oracle的索引組織表。SQL Server的聚集索引就是表本身的一種組織形式,所以它的效率是非常高的。也正因?yàn)榇?,插入一條記錄,它的位置不是隨便放的,而是要按照順序放在該放的數(shù)據(jù)頁(yè),如果那個(gè)數(shù)據(jù)頁(yè)沒(méi)有空間了,就引起了頁(yè)分裂。所以很顯然,聚集索引沒(méi)有建在表的順序字段上,該表容易發(fā)生頁(yè)分裂。
曾經(jīng)碰到過(guò)一個(gè)情況,一位哥們的某張表重建索引后,插入的效率大幅下降了。估計(jì)情況大概是這樣的。該表的聚集索引可能沒(méi)有建在表的順序字段上,該表經(jīng)常被歸檔,所以該表的數(shù)據(jù)是以一種稀疏狀態(tài)存在的。比如張三下過(guò)20張訂單,而最近3個(gè)月的訂單只有5張,歸檔策略是保留3個(gè)月數(shù)據(jù),那么張三過(guò)去的 15張訂單已經(jīng)被歸檔,留下15個(gè)空位,可以在insert發(fā)生時(shí)重新被利用。在這種情況下由于有空位可以利用,就不會(huì)發(fā)生頁(yè)分裂。但是查詢(xún)性能會(huì)比較低,因?yàn)椴樵?xún)時(shí)必須掃描那些沒(méi)有數(shù)據(jù)的空位。
重建聚集索引后情況改變了,因?yàn)橹亟ň奂?a href='/map/suoyin/' style='color:#000;font-size:inherit;'>索引就是把表中的數(shù)據(jù)重新排列一遍,原來(lái)的空位沒(méi)有了,而頁(yè)的填充率又很高,插入數(shù)據(jù)經(jīng)常要發(fā)生頁(yè)分裂,所以性能大幅下降。
對(duì)于聚集索引沒(méi)有建在順序字段上的表,是否要給與比較低的頁(yè)填充率?是否要避免重建聚集索引?是一個(gè)值得考慮的問(wèn)題!
10、加nolock后查詢(xún)經(jīng)常發(fā)生頁(yè)分裂的表,容易產(chǎn)生跳讀或重復(fù)讀
加nolock后可以在“插、刪、改”的同時(shí)進(jìn)行查詢(xún),但是由于同時(shí)發(fā)生“插、刪、改”,在某些情況下,一旦該數(shù)據(jù)頁(yè)滿(mǎn)了,那么頁(yè)分裂不可避免,而此時(shí)nolock的查詢(xún)正在發(fā)生,比如在第100頁(yè)已經(jīng)讀過(guò)的記錄,可能會(huì)因?yàn)轫?yè)分裂而分到第101頁(yè),這有可能使得nolock查詢(xún)?cè)谧x101頁(yè)時(shí)重復(fù)讀到該條數(shù)據(jù),產(chǎn)生“重復(fù)讀”。同理,如果在100頁(yè)上的數(shù)據(jù)還沒(méi)被讀到就分到99頁(yè)去了,那nolock查詢(xún)有可能會(huì)漏過(guò)該記錄,產(chǎn)生“跳讀”。
上面提到的哥們,在加了nolock后一些操作出現(xiàn)報(bào)錯(cuò),估計(jì)有可能因?yàn)閚olock查詢(xún)產(chǎn)生了重復(fù)讀,2條相同的記錄去插入別的表,當(dāng)然會(huì)發(fā)生主鍵沖突。
11、合理使用like模糊查詢(xún)
有的時(shí)候會(huì)需要進(jìn)行一些模糊查詢(xún)比如:
select * from contact where username like ‘%yue%’
關(guān)鍵詞 %yue%,由于yue前面用到了“%”,因此該查詢(xún)必然走全表掃描,除非必要,否則不要在關(guān)鍵詞前加%
12、數(shù)據(jù)類(lèi)型的隱式轉(zhuǎn)換對(duì)查詢(xún)效率的影響
sql server2000的數(shù)據(jù)庫(kù),我們的程序在提交sql語(yǔ)句的時(shí)候,沒(méi)有使用強(qiáng)類(lèi)型提交這個(gè)字段的值,由sql server 2000自動(dòng)轉(zhuǎn)換數(shù)據(jù)類(lèi)型,會(huì)導(dǎo)致傳入的參數(shù)與主鍵字段類(lèi)型不一致,這個(gè)時(shí)候sql server 2000可能就會(huì)使用全表掃描。Sql server2005上沒(méi)有發(fā)現(xiàn)這種問(wèn)題,但是還是應(yīng)該注意一下。
13、SQL Server 表連接的三種方式
Merge Join
Nested Loop Join
Hash Join
SQL Server 2000只有一種join方式——Nested Loop Join,如果A結(jié)果集較小,那就默認(rèn)作為外表,A中每條記錄都要去B中掃描一遍,實(shí)際掃過(guò)的行數(shù)相當(dāng)于A結(jié)果集行數(shù)x B結(jié)果集行數(shù)。所以如果兩個(gè)結(jié)果集都很大,那Join的結(jié)果很糟糕。
SQL Server 2005新增了Merge Join,如果A表和B表的連接字段正好是聚集索引所在字段,那么表的順序已經(jīng)排好,只要兩邊拼上去就行了,這種join的開(kāi)銷(xiāo)相當(dāng)于A表的結(jié)果集行數(shù)加上B表的結(jié)果集行數(shù),一個(gè)是加,一個(gè)是乘,可見(jiàn)merge join 的效果要比Nested Loop Join好多了。
數(shù)據(jù)分析咨詢(xún)請(qǐng)掃描二維碼
若不方便掃碼,搜微信號(hào):CDAshujufenxi
SQL Server 中 CONVERT 函數(shù)的日期轉(zhuǎn)換:從基礎(chǔ)用法到實(shí)戰(zhàn)優(yōu)化 在 SQL Server 的數(shù)據(jù)處理中,日期格式轉(zhuǎn)換是高頻需求 —— 無(wú)論 ...
2025-09-18MySQL 大表拆分與關(guān)聯(lián)查詢(xún)效率:打破 “拆分必慢” 的認(rèn)知誤區(qū) 在 MySQL 數(shù)據(jù)庫(kù)管理中,“大表” 始終是性能優(yōu)化繞不開(kāi)的話(huà)題。 ...
2025-09-18CDA 數(shù)據(jù)分析師:表結(jié)構(gòu)數(shù)據(jù) “獲取 - 加工 - 使用” 全流程的賦能者 表結(jié)構(gòu)數(shù)據(jù)(如數(shù)據(jù)庫(kù)表、Excel 表、CSV 文件)是企業(yè)數(shù)字 ...
2025-09-18DSGE 模型中的 Et:理性預(yù)期算子的內(nèi)涵、作用與應(yīng)用解析 動(dòng)態(tài)隨機(jī)一般均衡(Dynamic Stochastic General Equilibrium, DSGE)模 ...
2025-09-17Python 提取 TIF 中地名的完整指南 一、先明確:TIF 中的地名有哪兩種存在形式? 在開(kāi)始提取前,需先判斷 TIF 文件的類(lèi)型 —— ...
2025-09-17CDA 數(shù)據(jù)分析師:解鎖表結(jié)構(gòu)數(shù)據(jù)特征價(jià)值的專(zhuān)業(yè)核心 表結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 規(guī)范存儲(chǔ)的結(jié)構(gòu)化數(shù)據(jù),如數(shù)據(jù)庫(kù)表、Excel 表、 ...
2025-09-17Excel 導(dǎo)入數(shù)據(jù)含缺失值?詳解 dropna 函數(shù)的功能與實(shí)戰(zhàn)應(yīng)用 在用 Python(如 pandas 庫(kù))處理 Excel 數(shù)據(jù)時(shí),“缺失值” 是高頻 ...
2025-09-16深入解析卡方檢驗(yàn)與 t 檢驗(yàn):差異、適用場(chǎng)景與實(shí)踐應(yīng)用 在數(shù)據(jù)分析與統(tǒng)計(jì)學(xué)領(lǐng)域,假設(shè)檢驗(yàn)是驗(yàn)證研究假設(shè)、判斷數(shù)據(jù)差異是否 “ ...
2025-09-16CDA 數(shù)據(jù)分析師:掌控表格結(jié)構(gòu)數(shù)據(jù)全功能周期的專(zhuān)業(yè)操盤(pán)手 表格結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 存儲(chǔ)的結(jié)構(gòu)化數(shù)據(jù),如 Excel 表、數(shù)據(jù) ...
2025-09-16MySQL 執(zhí)行計(jì)劃中 rows 數(shù)量的準(zhǔn)確性解析:原理、影響因素與優(yōu)化 在 MySQL SQL 調(diào)優(yōu)中,EXPLAIN執(zhí)行計(jì)劃是核心工具,而其中的row ...
2025-09-15解析 Python 中 Response 對(duì)象的 text 與 content:區(qū)別、場(chǎng)景與實(shí)踐指南 在 Python 進(jìn)行 HTTP 網(wǎng)絡(luò)請(qǐng)求開(kāi)發(fā)時(shí)(如使用requests ...
2025-09-15CDA 數(shù)據(jù)分析師:激活表格結(jié)構(gòu)數(shù)據(jù)價(jià)值的核心操盤(pán)手 表格結(jié)構(gòu)數(shù)據(jù)(如 Excel 表格、數(shù)據(jù)庫(kù)表)是企業(yè)最基礎(chǔ)、最核心的數(shù)據(jù)形態(tài) ...
2025-09-15Python HTTP 請(qǐng)求工具對(duì)比:urllib.request 與 requests 的核心差異與選擇指南 在 Python 處理 HTTP 請(qǐng)求(如接口調(diào)用、數(shù)據(jù)爬取 ...
2025-09-12解決 pd.read_csv 讀取長(zhǎng)浮點(diǎn)數(shù)據(jù)的科學(xué)計(jì)數(shù)法問(wèn)題 為幫助 Python 數(shù)據(jù)從業(yè)者解決pd.read_csv讀取長(zhǎng)浮點(diǎn)數(shù)據(jù)時(shí)的科學(xué)計(jì)數(shù)法問(wèn)題 ...
2025-09-12CDA 數(shù)據(jù)分析師:業(yè)務(wù)數(shù)據(jù)分析步驟的落地者與價(jià)值優(yōu)化者 業(yè)務(wù)數(shù)據(jù)分析是企業(yè)解決日常運(yùn)營(yíng)問(wèn)題、提升執(zhí)行效率的核心手段,其價(jià)值 ...
2025-09-12用 SQL 驗(yàn)證業(yè)務(wù)邏輯:從規(guī)則拆解到數(shù)據(jù)把關(guān)的實(shí)戰(zhàn)指南 在業(yè)務(wù)系統(tǒng)落地過(guò)程中,“業(yè)務(wù)邏輯” 是連接 “需求設(shè)計(jì)” 與 “用戶(hù)體驗(yàn) ...
2025-09-11塔吉特百貨孕婦營(yíng)銷(xiāo)案例:數(shù)據(jù)驅(qū)動(dòng)下的精準(zhǔn)零售革命與啟示 在零售行業(yè) “流量紅利見(jiàn)頂” 的當(dāng)下,精準(zhǔn)營(yíng)銷(xiāo)成為企業(yè)突圍的核心方 ...
2025-09-11CDA 數(shù)據(jù)分析師與戰(zhàn)略 / 業(yè)務(wù)數(shù)據(jù)分析:概念辨析與協(xié)同價(jià)值 在數(shù)據(jù)驅(qū)動(dòng)決策的體系中,“戰(zhàn)略數(shù)據(jù)分析”“業(yè)務(wù)數(shù)據(jù)分析” 是企業(yè) ...
2025-09-11Excel 數(shù)據(jù)聚類(lèi)分析:從操作實(shí)踐到業(yè)務(wù)價(jià)值挖掘 在數(shù)據(jù)分析場(chǎng)景中,聚類(lèi)分析作為 “無(wú)監(jiān)督分組” 的核心工具,能從雜亂數(shù)據(jù)中挖 ...
2025-09-10統(tǒng)計(jì)模型的核心目的:從數(shù)據(jù)解讀到?jīng)Q策支撐的價(jià)值導(dǎo)向 統(tǒng)計(jì)模型作為數(shù)據(jù)分析的核心工具,并非簡(jiǎn)單的 “公式堆砌”,而是圍繞特定 ...
2025-09-10