国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

數(shù)據(jù)庫(kù)查詢優(yōu)化(轉(zhuǎn)載)

2024-07-21 02:46:21
字體:
供稿:網(wǎng)友
數(shù)據(jù)庫(kù)查詢優(yōu)化(轉(zhuǎn)載)1 使用SET NOCOUNT ON 選項(xiàng):

缺 省地,每次執(zhí)行SQL語句時(shí),一個(gè)消息會(huì)從服務(wù)端發(fā)給客戶端以顯示SQL語句影響的行數(shù)。這些信息對(duì)客戶端來說很少有用。通過關(guān)閉這個(gè)缺省值,你能減少在 服務(wù)端和客戶端的網(wǎng)絡(luò)流量,幫助全面提升服務(wù)器和應(yīng)用程序的性能。為了關(guān)閉存儲(chǔ)過程級(jí)的這個(gè)特點(diǎn),在每個(gè)存儲(chǔ)過程的開頭包含“SET NOCOUNT ON”語句。

2 正確使用UNION和UNION ALL:

許 多人沒完全理解UNION和UNION SELECT是怎樣工作的,因此,結(jié)果浪費(fèi)了大量不必要的SQLServer資源。當(dāng)使用UNION時(shí),它相當(dāng)于在結(jié)果集上執(zhí)行SELECT DISTINCT。換句話說,UNION將聯(lián)合兩個(gè)相類似的記錄集,然后搜索重復(fù)的記錄并排除。如果這是你的目的,那么使用UNION是正確的。但如果你 使用UNION聯(lián)合的兩個(gè)記錄集沒有重復(fù)記錄,那么使用UNION會(huì)浪費(fèi)資源,因?yàn)樗獙ふ抑貜?fù)記錄,即使你確定它們不存在。

所以如果你知道你要聯(lián)合的記錄集里沒有重復(fù),那么你要使用UNION ALL,而不是UNION。UNION ALL聯(lián)合記錄集,但不搜索重復(fù)記錄,這樣減少SQLServer資源的使用,從而提升性能。

3 盡量不用SELECT * :

絕 大多數(shù)情況下,不要用 * 來代替查詢返回的字段列表,用 * 的好處是代碼量少、就算是表結(jié)構(gòu)或視圖的列發(fā)生變化,編寫的查詢SQL語句也不用變,都返回所有的字段。但數(shù)據(jù)庫(kù)服務(wù)器在解析時(shí),如果碰到 *,則會(huì)先分析表的結(jié)構(gòu),然后把表的所有字段名再羅列出來。這就增加了分析的時(shí)間。

4 慎用SELECT DISTINCT:

DISTINCT子句僅在特定功能的時(shí)候使用,即從記錄集中排除重復(fù)記錄的時(shí)候。這是因?yàn)镈ISTINCT子句先獲取結(jié)果集然后去重,這樣增加SQLServer有用資源的使用。當(dāng)然,如果你需要去做,那就只有去做了。

當(dāng)如果你知道SELECT語句將從不返回重復(fù)記錄,那么使用DISTINCT語句對(duì)SQLServer資源不必要的浪費(fèi)。

5 少用游標(biāo):

任何一種游標(biāo)都會(huì)降低SQLServer性能。有些情況不能避免,大多數(shù)情況可以避免。所以如果你的應(yīng)用程序目前正在使用TSQL游標(biāo),看看這些代碼是否能夠重寫以避免它們。如果你需要一行一行的執(zhí)行操作,考慮下邊這些選項(xiàng)中的一個(gè)或多個(gè)來代替游標(biāo)的使用:

使用臨時(shí)表

使用WHILE循環(huán)

使用派生表

使用相關(guān)子查詢

使用CASE語句

使用多個(gè)查詢

上面每一個(gè)都能取代游標(biāo)并且執(zhí)行更快。 如果你不能避免使用游標(biāo),至少試著提高它們的速度,找出加速游標(biāo)的方法。

6 選擇最有效率的表名順序:

SQLSERVER的 解析器按照從右到左的順序處理FROM子句中的表名,因此FROM子句中寫在最后的表(基礎(chǔ)表driving table)將被最先處理,在FROM子句中包含多個(gè)表的情況下,必須選擇記錄條數(shù)最少的表作為基礎(chǔ)表,當(dāng)SQLSERVER處理多個(gè)表時(shí),會(huì)運(yùn)用排序及 合并的方式連接它們。首先,掃描第一個(gè)表(FROM子句中最后的那個(gè)表)并對(duì)記錄進(jìn)行排序;然后掃描第二個(gè)表(FROM子句中最后第二個(gè)表);最后將所有 從第二個(gè)表中檢索出的記錄與第一個(gè)表中合適記錄進(jìn)行合并。

例如: 表 TAB1有 16384 條記錄,表 TAB2 有5條記錄,選擇TAB2作為基礎(chǔ)表 (最好的方法):

select count(*) from TAB1 a, TAB2 b

選擇TAB1作為基礎(chǔ)表 (不佳的方法):

select count(*) from TAB2 a, TAB1 b

如果有3個(gè)以上的表連接查詢,那就需要選擇交叉表(intersection table)作為基礎(chǔ)表,交叉表是指那個(gè)被其他表所引用的表。

7 使用表的別名(Alias):

當(dāng)在SQL語句中連接多個(gè)表時(shí),請(qǐng)使用表的別名并把別名前綴于每個(gè)Column上,這樣可以減少解析的時(shí)間并減少那些由Column歧義引起的語法錯(cuò)誤。

8 SARG你的WHERE條件:

ARGE來 源于"Search Argument"(搜索參數(shù))的首字母拼成的"SARG",它是指WHERE子句里,列和常量的比較。如果WHERE子句是sargable(可 SARG的),這意味著它能利用索引加速查詢的完成。如果WHERE子句不是可SARG的,這意味著WHERE子句不能利用索引(或至少部分不能利用), 執(zhí)行的是全表或索引掃描,這會(huì)引起查詢的性能下降。

在WHERE子句里不可 SARG的搜索條件如"IS NULL", "<>", "!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE"和"LIKE '%500'",通常(但不總是)會(huì)阻止查詢優(yōu)化器使用索引執(zhí)行搜索。另外在列上使用包括函數(shù)的表達(dá)式、兩邊都使用相同列的表達(dá)式、或和一個(gè)列(不是常 量)比較的表達(dá)式,都是不可SARG的。

并不是每一個(gè)不可SARG的WHERE子句都注定要全表掃描。如果WHERE子句包括兩個(gè)可SARG和一個(gè)不可SARG的子句,那么至少可SARG的子句能使用索引(如果存在的話)幫助快速訪問數(shù)據(jù)。

大多數(shù)情況下,如果表上有包 括查詢里所有SELECT、JOIN、WHERE子句用到的列的覆蓋索引,那么覆蓋索引能夠代替全表掃描去返回查詢的數(shù)據(jù),即使它有不可SARG的 WHERE子句。但記住覆蓋索引尤其自身的缺陷,如此經(jīng)常產(chǎn)生寬索引會(huì)增加讀磁盤I/O。某些情況下,可以把不可SARG的WHERE子句重寫成可 SARG的子句。例如:

WHERE SUBSTRING(firstname,1,1) = 'm'

可以寫成:

WHERE firstname like 'm%'

這兩個(gè)WHERE子句有相同的結(jié)果,但第一個(gè)是不可SARG的(因?yàn)槭褂昧撕瘮?shù))將運(yùn)行得慢些,而第二個(gè)是可SARG的,將運(yùn)行得快些。

如果你不知道特定的WHERE子句是不是可SARG的,在查詢分析器里檢查查詢執(zhí)行計(jì)劃。這樣做,你能很快的知道查詢是使用了索引還是全表掃描來返回的數(shù)據(jù)。仔細(xì)分析,許多不可SARG的查詢能寫成可SARG的查詢。下面分幾點(diǎn)講解WHERE條件的SARG。

8.1 WHERE子句中的連接順序

SQLSERVER采用自下而上的順序解析WHERE子句,根據(jù)這個(gè)原理,表之間的連接必須寫在其他WHERE條件之前,那些可以過濾掉最大數(shù)量記錄的條件必須寫在WHERE子句的末尾。例如:

(低效)

SELECT * FROM EMP E

WHERE SAL > 50000

AND JOB = ‘MANAGER’

AND 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO)

(高效)

SELECT * FROM EMP E

WHERE 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO)

AND SAL > 50000

AND JOB = ‘MANAGER’

8.2 避免困難的正規(guī)表達(dá)式:

MATCHES和LIKE關(guān)鍵字支持通配符匹配,技術(shù)上叫正規(guī)表達(dá)式。但這種匹配特別耗費(fèi)時(shí)間。例如:

SELECT * FROM customer WHERE zipcode LIKE "98_ _ _"

即使在zipcode字段上建立了索引,在這種情況下也還是采用順序掃描的方式。如果把語句改為SELECT * FROM customer WHERE zipcode >="98000",在執(zhí)行查詢時(shí)就會(huì)利用索引來查詢,顯然會(huì)大大提高速度。

另外,還要避免非開始的子串。例如語句:

SELECT * FROM customer WHERE zipcode[2,3] >"80"

在where子句中采用了非開始子串,因而這個(gè)語句也不會(huì)使用索引。

8.3 避免對(duì)大型表行數(shù)據(jù)的順序存取:

在嵌套查詢中,對(duì)表的順序存 取對(duì)查詢效率可能產(chǎn)生致命的影響。比如采用順序存取策略,一個(gè)嵌套3層的查詢,如果每層都查詢1000行,那么這個(gè)查詢就要查詢10億行數(shù)據(jù)。避免這種情 況的主要方法就是對(duì)連接的列進(jìn)行索引。例如,兩個(gè)表:學(xué)生表(學(xué)號(hào)、姓名、年齡……)和選課表(學(xué)號(hào)、課程號(hào)、成績(jī))。如果兩個(gè)表要做連接,就要在“學(xué) 號(hào)”這個(gè)連接字段上建立索引。

還可以使用并集來避免順序存取。盡管在所有的檢查列上都有索引,但某些形式的where子句強(qiáng)迫優(yōu)化器使用順序存取。下面的查詢將強(qiáng)迫對(duì)orders表執(zhí)行順序操作:

SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008

雖然在customer_num和order_num上建有索引,但是在上面的語句中優(yōu)化器還是使用順序存取路徑掃描整個(gè)表。因?yàn)檫@個(gè)語句要檢索的是分離的行的集合,所以應(yīng)該改為如下語句:

SELECT * FROM orders WHERE customer_num=104 AND order_num>1001

UNION ALL

SELECT * FROM orders WHERE order_num=1008

這樣就能利用索引路徑處理查詢。

8.4 EXISTS和IN的使用:

在 許多基于基礎(chǔ)表的查詢中,為了滿足一個(gè)條件,往往需要對(duì)另一個(gè)表進(jìn)行聯(lián)接。   在這種情況下,使用EXISTS(或NOT EXISTS)通常將提高查詢的效率。在子查詢中,NOT IN子句將執(zhí)行一個(gè)內(nèi)部的排序和合并。無論在哪種情況下,NOT IN都是最低效的,因?yàn)樗鼘?duì)子查詢中的表執(zhí)行了一個(gè)全表遍歷。為了避免使用NOT IN,我們可以把它改寫成外連接(Outer Joins)或NOT EXISTS。

8.5 避免在索引列上使用IS NULL和IS NOT NULL:

避免在索引中使用任何可以為空的列,SQLSERVER將無法使用該索引。對(duì)于單列索引,如果列包含空值,索引中將不存在此記錄;對(duì)于復(fù)合索引,如果每個(gè)列都為空,索引中同樣不存在此記錄。如果至少有一個(gè)列不為空,則記錄存在于索引中。  

  如果唯一性索引建立在表的A列和B列上,并且表中存在一條記錄的A,B值為(123,null),SQLSERVER將不接受下一條具有相同A,B值(123,null)的記錄插入。  

  如果所有的索引列都為空,SQLSERVER將認(rèn)為整個(gè)鍵值為空,而空不可能等 于空,因此你可以插入1000條具有相同鍵值的記錄,當(dāng)然它們都是空!因?yàn)榭罩挡淮嬖谟谒饕兄校訵HERE子句中對(duì)索引列進(jìn)行空值比較將使 SQLSERVER停用該索引。下面的代碼將會(huì)很低效(索引失效):

SELECT … FROM DEPARTMENT WHERE DEPT_CODE IS NOT NULL

8.6 避免在索引列上使用計(jì)算:

WHERE子句中,如果索引列是函數(shù)的一部分,優(yōu)化器將不使用索引而使用全表掃描。   例如下面的語句低效 :

SELECT …FROM DEPT WHERE SAL * 12 > 25000

而下面的語句將是高效的:

SELECT …FROM DEPT WHERE SAL > 25000/12

請(qǐng)務(wù)必注意,查詢中不要對(duì)索引列進(jìn)行處理,如:TRIM,substring,convert等等操作。

8.7 用WHERE子句替換HAVING子句:

避免使用HAVING子句,HAVING只會(huì)在檢索出所有記錄之后才對(duì)結(jié)果集進(jìn)行過濾,這個(gè)處理需要排序、統(tǒng)計(jì)等操作。如果能通過WHERE子句限制記錄的數(shù)目,那就能減少這方面的開銷。

9 避免或簡(jiǎn)化排序:

應(yīng)當(dāng)簡(jiǎn)化或避免對(duì)大型表進(jìn)行重復(fù)的排序。當(dāng)能夠利用索引自動(dòng)以適當(dāng)?shù)拇涡虍a(chǎn)生輸出時(shí),優(yōu)化器就避免了排序的步驟。以下是一些影響因素:

l 索引中不包括一個(gè)或幾個(gè)待排序的列;

l group by或order by子句中列的次序與索引的次序不一樣;

l 排序的列來自不同的表。

為了避免不必要的排序,就要正確地增建索引,合理地合并數(shù)據(jù)庫(kù)表(盡管有時(shí)可能影響表的規(guī)范化,但相對(duì)于效率的提高是值得的)。如果排序不可避免,那么應(yīng)當(dāng)試圖簡(jiǎn)化它,如縮小排序的列的范圍等。

10 臨時(shí)表的使用:

臨 時(shí)表有很多特殊的用途,象用來替代游標(biāo),不過它們?nèi)阅芤鹦阅軉栴},如果這個(gè)問題能消除,SQLServer將執(zhí)行得更快。在永久表和臨時(shí)表的數(shù)據(jù)行相同 的條件下,使用臨時(shí)表沒有永久表快。但有時(shí)還必須得使用臨時(shí)表,如先從存儲(chǔ)大量數(shù)據(jù)的永久表中提取符全條件的存放到臨時(shí)表,然后在臨時(shí)表上執(zhí)行操作。如果 是直接在存儲(chǔ)大量數(shù)據(jù)的永久表上執(zhí)行操作(如:統(tǒng)計(jì)、循環(huán)等),其性能將大打折扣。所以,使不使用臨時(shí)表,何時(shí)使用臨時(shí)表,需要具體情況決定。

11 是否使用視圖:

視圖最大的用途是處理安全相 關(guān)的問題,而不是一些懶惰的開發(fā)人員用來存儲(chǔ)經(jīng)常使用的查詢的方法。例如,如果你需要允許用戶訪問特定SQLServer的數(shù)據(jù),那么你也許可以考慮為用 戶(或組)創(chuàng)建一個(gè)視圖,然后給用

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 班戈县| 海口市| 宽城| 凯里市| 蒙阴县| 隆安县| 西畴县| 河南省| 蓬溪县| 衡阳县| 九江市| 景德镇市| 资源县| 金乡县| 南通市| 翼城县| 廉江市| 新营市| 萝北县| 富平县| 景东| 鹤壁市| 梅河口市| 闽侯县| 建宁县| 定远县| 广宗县| 宁晋县| 深州市| 贺兰县| 古浪县| 安新县| 水城县| 始兴县| 双流县| 府谷县| 吴旗县| 宜兰市| 托里县| 南阳市| 随州市|