1、不要把聚集索引浪費(fèi)在主鍵上,除非你只按主鍵查詢
雖然SQL SERVER默認(rèn)是在主鍵上建立聚集索引的,但實(shí)際應(yīng)用中,這樣做比較浪費(fèi)。通常,我們會(huì)在每個(gè)表中都建立一個(gè)ID列,以區(qū)分每條數(shù)據(jù),并且這個(gè)ID列是自動(dòng)增大的,步長(zhǎng)一般為1。此時(shí),如果我們將這個(gè)列設(shè)為主鍵,SQL SERVER會(huì)將此列默認(rèn)為聚集索引。這樣做有好處,就是可以讓您的數(shù)據(jù)在數(shù)據(jù)庫(kù)中按照ID進(jìn)行物理排序,但這樣做實(shí)用價(jià)值不大。
從我們前面談到的聚集索引的定義我們可以看出,使用聚集索引的最大好處就是能夠根據(jù)查詢要求,迅速縮小查詢范圍,避免全表掃描。在實(shí)際應(yīng)用中,因?yàn)镮D號(hào)是自動(dòng)生成的,我們并不知道每條記錄的ID 號(hào),所以我們很難在實(shí)踐中用ID號(hào)來進(jìn)行查詢。這就使讓ID號(hào)這個(gè)主鍵作為聚集索引成為一種資源浪費(fèi)。聚集索引相對(duì)與非聚集索引的優(yōu)勢(shì)是很明顯的,而每個(gè)表中只能有一個(gè)聚集索引的規(guī)則,這使得聚集索引變得更加寶貴,應(yīng)該用在其他查詢頻率高的字段上。其次,讓每個(gè)ID號(hào)都不同的字段作為聚集索引也不符合“大數(shù)目的不同值情況下不應(yīng)建立聚合索引”規(guī)則;當(dāng)然,這種情況只是針對(duì)用戶經(jīng)常修改記錄內(nèi)容,特別是索引項(xiàng)的時(shí)候會(huì)負(fù)作用,但對(duì)于查詢速度并沒有影響。
2、索引的建立要根據(jù)實(shí)際應(yīng)用需求來進(jìn)行
并非是在任何字段上簡(jiǎn)單地建立索引就能提高查詢速度。聚集索引建立的規(guī)則大致是“既不能絕大多數(shù)都相同,又不能只有極少數(shù)相同”。舉個(gè)例子,在公文表的收發(fā)日期字段上建立聚合索引是比較合適的。在政務(wù)系統(tǒng)中,我們每天都會(huì)收一些文件,這些文件的發(fā)文日期將會(huì)相同,在發(fā)文日期上建立聚合索引對(duì)性能的提升應(yīng)該是相當(dāng)大的。在群集索引下,數(shù)據(jù)物理上按順序存于數(shù)據(jù)頁上,重復(fù)值也排列在一起,因而在范圍查找時(shí),可以先找到這個(gè)范圍的起末點(diǎn),且只在這個(gè)范圍內(nèi)掃描數(shù)據(jù)頁,避免了大范圍掃描,提高了查詢速度。
另一個(gè)相反的例子:比如在雇員表的“性別”列上只有“男”與“女”兩個(gè)不同值,因此就完全沒必要建立索引。
3、在聚集索引中加入所有需要提高查詢速度的字段,形成復(fù)合索引
根據(jù)一些實(shí)驗(yàn)的結(jié)果,我們可以得出一些可供參考的結(jié)論:
僅用復(fù)合聚集索引的起始列作為查詢條件和同時(shí)用到復(fù)合聚集索引的全部列的查詢,速度是幾乎一樣的,甚至比后者還要快(在查詢結(jié)果集數(shù)目一樣的情況下);
僅用復(fù)合聚集索引的非起始列作為查詢條件的話,
這個(gè)索引是不起任何作用的。
復(fù)合聚集索引的所有列都用上,而且因?yàn)椴樵儣l件嚴(yán)格,查詢結(jié)果少的話,會(huì)形成“索引覆蓋”,性能可以達(dá)到最優(yōu)。
最重要的一點(diǎn):無論是否經(jīng)常使用復(fù)合聚合索引的其他列,其起始列一定要是使用最頻繁的列。
4.根據(jù)實(shí)踐得出的一些其他經(jīng)驗(yàn),特定情況下有效
用聚合索引比用不是聚合索引的主鍵速度快;
用聚合索引比用一般的主鍵作order by速度快,特別是在小數(shù)據(jù)量情況;
使用聚合索引內(nèi)的時(shí)間段,搜索時(shí)間會(huì)按數(shù)據(jù)占整個(gè)數(shù)據(jù)表的百分比成比例減少,而無論聚合索引使用了多少個(gè);
日期列不會(huì)因?yàn)橛蟹置氲妮斎攵鴾p慢查詢速度;
由于改變一個(gè)表的內(nèi)容,將會(huì)引起索引的變化。頻繁的insert,update,delete語句將導(dǎo)致系統(tǒng)花費(fèi)較大的代價(jià)進(jìn)行索引更新,引起整體性能的下降。一般來講,在對(duì)查詢性能的要求高于對(duì)數(shù)據(jù)維護(hù)性能要求時(shí),應(yīng)該盡量使用索引,否則,就要慎重考慮一下付出的代價(jià)。在某些極端情況下,可先刪除索引,再對(duì)數(shù)據(jù)庫(kù)表更新大量數(shù)據(jù),最后再重建索引,新建立的索引總是比較好用。
T1表 10000000萬條數(shù)據(jù),(插入時(shí)間36分鐘,count(*)查詢19秒,空間占用670M左右)
1.真正充分的利用索引
比如like '張%' 就是符合SARG(符合掃描參數(shù))標(biāo)準(zhǔn)
而like '%張' 就不符合該標(biāo)準(zhǔn)
通配符%在字符串首字符的使用會(huì)導(dǎo)致索引無法使用,雖然實(shí)際應(yīng)用中很難避免這樣用,但還是應(yīng)該對(duì)這種現(xiàn)象有所了解,至少知道此種用法性能是很低下的。
**********************************************
2.“非”操作符不滿足SARG形式,使得索引無法使用
不滿足SARG形式的語句最典型的情況就是包括非操作符的語句,如:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等。
如果使用not 或者 <>,最好轉(zhuǎn)換成別的方法,比如例子如下:
T1表 10000000萬條數(shù)據(jù),構(gòu)建如下:(插入時(shí)間36分鐘,count(*)查詢19秒,空間占用670M左右)
DECLARE @i INT
SET @i = 1
WHILE @i<1000000
BEGIN
INSERT INTO t1 VALUES ('zhang'+CONVERT(char(50), @i),'3.2',77);
SET @i + 1;
END
三種查詢方式:
SELECT * FROM t1 WHERE id <>300000
SELECT * FROM t1 WHERE id NOT IN (300000)
SELECT * FROM t1 WHERE id >299999 AND id < 300001
在執(zhí)行計(jì)劃中可以明顯看出,使用最后一種方式而不是前面兩種方式進(jìn)行查詢。
網(wǎng)上是這么說的,但自己做的試驗(yàn)100W條數(shù)據(jù),開銷計(jì)劃是一樣的。
*********************************************
3. 函數(shù)運(yùn)算不滿足SARG形式,使得索引無法使用
例:下列SQL條件語句中的列都建有恰當(dāng)?shù)乃饕珗?zhí)行速度卻非常慢:
select * from record where substring(card_no,1,4)=′5378′(13秒)
select * from record where amount/30< 1000(11秒)
select * from record where convert(char(10),date,112)=′19991201′(10秒)
分析:
where子句中對(duì)列的任何操作結(jié)果都是在SQL運(yùn)行時(shí)逐列計(jì)算得到的,因此它不得不進(jìn)行全表掃描,而沒有使用該列上面的索引;如果這些結(jié)果在查詢編譯時(shí)就能得到,那么就可以被SQL優(yōu)化器優(yōu)化,使用索引,避免表搜索,因此將SQL重寫成下面這樣:
select * from record where card_no like ′5378%′(< 1秒)
select * from record where amount < 1000*30(< 1秒)
select * from record where date= ′1999/12/01′ (< 1秒)
你會(huì)發(fā)現(xiàn)SQL明顯快很多
待測(cè)試.......
**********************************************
4.盡量不要對(duì)建立了索引的字段,作任何的直接處理
select * from employs where first_name + last_name ='beill cliton';
無法使用索引,改為:
select * from employee where
first_name = substr('beill cliton',1,instr('beill cliton',' ')-1)
and
last_name = substr('beill cliton',instr('beill cliton',' ')+1)
則可以使用索引
***********************************************
5.不同類型的索引效能是不一樣的,應(yīng)盡可能先使用效能高的
比如:數(shù)字類型的索引查找效率高于字符串類型,定長(zhǎng)字符串char,nchar的索引效率高于變長(zhǎng)字符串varchar,nvarchar的索引。
應(yīng)該將
where username='張三' and age>20
改進(jìn)為
where age>20 and username='張三'
注意:此處,SQL的查詢分析優(yōu)化功能可以做到自動(dòng)重排條件順序,但還是建議預(yù)先手工排列好。
**************************************************
6.某些情況下IN 的作用與OR 相當(dāng) ,且都不能充分利用索引
例:表stuff有200000行,id_no上有非群集索引,請(qǐng)看下面這個(gè)SQL:
select count(*) from stuff where id_no in(′0′,′1′) (23秒)
我們期望它會(huì)根據(jù)每個(gè)or子句分別查找,再將結(jié)果相加,這樣可以利用id_no上的索引;但實(shí)際上,它卻采用了"OR策略",即先取出滿足每個(gè)or子句的行,存入臨時(shí)數(shù)據(jù)庫(kù)的工作表中,再建立唯一索引以去掉重復(fù)行,最后從這個(gè)臨時(shí)表中計(jì)算結(jié)果。因此,實(shí)際過程沒有利用id_no 上索引,并且完成時(shí)間還要受tempdb數(shù)據(jù)庫(kù)性能的影響。
實(shí)踐證明,表的行數(shù)越多,工作表的性能就越差,當(dāng)stuff有620000行時(shí),執(zhí)行時(shí)間會(huì)非常長(zhǎng)!如果確定不同的條件不會(huì)產(chǎn)生大量重復(fù)值,還不如將or子句分開:
select count(*) from stuff where id_no=′0′
select count(*) from stuff where id_no=′1′
得到兩個(gè)結(jié)果,再用union作一次加法合算。因?yàn)槊烤涠际褂昧怂饕?,?zhí)行時(shí)間會(huì)比較短,
select count(*) from stuff where id_no=′0′
union
select count(*) from stuff where id_no=′1′
從實(shí)踐效果來看,使用union在通常情況下比用or的效率要高的多,而exist關(guān)鍵字和in關(guān)鍵字在用法上類似,性能上也類似,都會(huì)產(chǎn)生全表掃描,效率比較低下,根據(jù)未經(jīng)驗(yàn)證的說法,exist可能比in要快些。
***************************************************
7.使用變通的方法提高查詢效率
like關(guān)鍵字支持通配符匹配,但這種匹配特別耗時(shí)。例如:select * from customer where zipcode like “21_ _ _”,即使在zipcode字段上已建立了索引,在這種情況下也可能還是采用全表掃描方式。如果把語句改為:select * from customer where zipcode >“21000”,在執(zhí)行查詢時(shí)就會(huì)利用索引,大大提高速度。但這種變通是有限制的,不應(yīng)引起業(yè)務(wù)意義上的損失,對(duì)于郵政編碼而言,zipcode like “21_ _ _” 和 zipcode >“21000” 意義是完全一致的。
*********************************************************人各有志,但富貴在天,人生允許彷徨,但不允許蹉跎.
8.order by按聚集索引列排序效率最高
排序是較耗時(shí)的操作,應(yīng)盡量簡(jiǎn)化或避免對(duì)大型表進(jìn)行排序,如縮小排序的列的范圍,只在有索引的列上排序等等。
我們來看:(gid是主鍵,fariqi是聚合索引列)
select top 10000 gid,fariqi,reader,title from tgongwen
用時(shí):196 毫秒。 掃描計(jì)數(shù) 1,邏輯讀 289 次,物理讀 1 次,預(yù)讀 1527 次。
select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc
用時(shí):4720毫秒。 掃描計(jì)數(shù) 1,邏輯讀 41956 次,物理讀 0 次,預(yù)讀 1287 次。
select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc
用時(shí):4736毫秒。 掃描計(jì)數(shù) 1,邏輯讀 55350 次,物理讀 10 次,預(yù)讀 775 次。
select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc
用時(shí):173毫秒。 掃描計(jì)數(shù) 1,邏輯讀 290 次,物理讀 0 次,預(yù)讀 0 次。
select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc
用時(shí):156毫秒。 掃描計(jì)數(shù) 1,邏輯讀 289 次,物理讀 0 次,預(yù)讀 0 次。
同時(shí),按照某個(gè)字段進(jìn)行排序的時(shí)候,無論是正序還是倒序,速度是基本相當(dāng)?shù)摹?/p>
********************************************************
9.關(guān)于節(jié)省數(shù)據(jù)查詢系統(tǒng)開銷方面的措施
(1)使用TOP盡量減少取出的數(shù)據(jù)量
(2)字段提取要按照“需多少、提多少”的原則,避免“select *”
字段大小越大,數(shù)目越多,select所耗費(fèi)的資源就越多,比如取int類型的字段就會(huì)比取char的快很多。我們每少提取一個(gè)字段,數(shù)據(jù)的提取速度就會(huì)有相應(yīng)的提升。提升的幅度根據(jù)舍棄的字段的大小來判斷
(3)count(*) 與 count(字段) 方法比較
用count(*)和用 count(主鍵)的速度是相當(dāng)?shù)?,而count(*)卻比其他任何除主鍵以外的字段匯總速度要快,而且字段越長(zhǎng),匯總速度就越慢。如果用 count(*), SQL SERVER會(huì)自動(dòng)查找最小字段來匯總。當(dāng)然,如果您直接寫count(主鍵)將會(huì)來的更直接些
(4)有嵌套查詢時(shí),盡可能在內(nèi)層過濾掉數(shù)據(jù)
如果一個(gè)列同時(shí)在主查詢和where子句中出現(xiàn),很可能當(dāng)主查詢中的列值改變之后,子查詢必須重新查詢一次。而且查詢嵌套層次越多,效率越低,因此應(yīng)當(dāng)盡量避免子查詢。如果子查詢不可避免,那么要在子查詢中過濾掉盡可能多的行
(5)多表關(guān)聯(lián)查詢時(shí),需注意表順序,并盡可能早的過濾掉數(shù)據(jù)
在使用Join進(jìn)行多表關(guān)聯(lián)查詢時(shí)候,應(yīng)該使用系統(tǒng)開銷最小的方案。連接條件要充份考慮帶有索引的表、行數(shù)多的表,并注意優(yōu)化表順序;說的簡(jiǎn)單一點(diǎn),就是盡可能早的將之后要做關(guān)聯(lián)的數(shù)據(jù)量降下來。
一般情況下,sqlserver 會(huì)對(duì)表的連接作出自動(dòng)優(yōu)化。例如:
select name,no from A
join B on A. id=B.id
join C on C.id=A.id
where name='wang'
盡管A表在From中先列出,然后才是B,最后才是C。但sql server可能會(huì)首先使用c表。它的選擇原則是相對(duì)于該查詢限制為單行或少數(shù)幾行,就可以減少在其他表中查找的總數(shù)據(jù)量。絕大多數(shù)情況下,sql server 會(huì)作出最優(yōu)的選擇,但如果你發(fā)覺某個(gè)復(fù)雜的聯(lián)結(jié)查詢速度比預(yù)計(jì)的要慢,就可以使用SET FORCEPLAN語句強(qiáng)制sql server按照表出現(xiàn)順序使用表。如上例加上:SET FORCEPLAN ON…….SET FORCEPLAN OFF 表的執(zhí)行順序?qū)?huì)按照你所寫的順序執(zhí)行。在查詢分析器中查看2種執(zhí)行效率,從而選擇表的連接順序。SET FORCEPLAN的缺點(diǎn)是只能在存儲(chǔ)過程中使用
聯(lián)系客服