T1表 10000000萬條數據,(插入時間36分鍾,count(*)查詢19秒,空間占用670M左右)
1.真正充分的利用索引
比如like '張%' 就是符合SARG(符合掃描參數)標准
而like '%張' 就不符合該標准
通配符%在字符串首字符的使用會導致索引無法使用,雖然實際應用中很難避免這樣用,但還是應該對這種現象有所了解,至少知道此種用法性能是很低下的。
**********************************************
2.“非”操作符不滿足SARG形式,使得索引無法使用
不滿足SARG形式的語句最典型的情況就是包括非操作符的語句,如:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等。
如果使用not 或者 <>,最好轉換成別的方法,比如例子如下:
T1表 10000000萬條數據,構建如下:(插入時間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
在執行計划中可以明顯看出,使用最后一種方式而不是前面兩種方式進行查詢。
網上是這么說的,但自己做的試驗100W條數據,開銷計划是一樣的。
*********************************************
3. 函數運算不滿足SARG形式,使得索引無法使用
例:下列SQL條件語句中的列都建有恰當的索引,但執行速度卻非常慢:
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子句中對列的任何操作結果都是在SQL運行時逐列計算得到的,因此它不得不進行全表掃描,而沒有使用該列上面的索引;如果這些結果在查詢編譯時就能得到,那么就可以被SQL優化器優化,使用索引,避免表搜索,因此將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秒)
你會發現SQL明顯快很多
待測試.......
**********************************************
4.盡量不要對建立了索引的字段,作任何的直接處理
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.不同類型的索引效能是不一樣的,應盡可能先使用效能高的
比如:數字類型的索引查找效率高於字符串類型,定長字符串char,nchar的索引效率高於變長字符串varchar,nvarchar的索引。
應該將
where username='張三' and age>20
改進為
where age>20 and username='張三'
注意:此處,SQL的查詢分析優化功能可以做到自動重排條件順序,但還是建議預先手工排列好。
**************************************************
6.某些情況下IN 的作用與OR 相當 ,且都不能充分利用索引
例:表stuff有200000行,id_no上有非群集索引,請看下面這個SQL:
select count(*) from stuff where id_no in(′0′,′1′) (23秒)
我 們期望它會根據每個or子句分別查找,再將結果相加,這樣可以利用id_no上的索引;但實際上,它卻采用了"OR策略",即先取出滿足每個or子句的 行,存入臨時數據庫的工作表中,再建立唯一索引以去掉重復行,最后從這個臨時表中計算結果。因此,實際過程沒有利用id_no 上索引,並且完成時間還要 受tempdb數據庫性能的影響。
實踐證明,表的行數越多,工作表的性能就越差,當stuff有620000行時,執行時間會非常長!如果確定不同的條件不會產生大量重復值,還不如將or子句分開:
select count(*) from stuff where id_no=′0′
select count(*) from stuff where id_no=′1′
得到兩個結果,再用union作一次加法合算。因為每句都使用了索引,執行時間會比較短,
select count(*) from stuff where id_no=′0′
union
select count(*) from stuff where id_no=′1′
從實踐效果來看,使用union在通常情況下比用or的效率要高的多,而exist關鍵字和in關鍵字在用法上類似,性能上也類似,都會產生全表掃描,效率比較低下,根據未經驗證的說法,exist可能比in要快些。
***************************************************
7.使用變通的方法提高查詢效率
like關鍵字支持通配符匹配,但這種匹配特別耗時。例 如:select * from customer where zipcode like “21_ _ _”,即使在zipcode字段上已建立了索 引,在這種情況下也可能還是采用全表掃描方式。如果把語句改 為:select * from customer where zipcode >“21000”,在執行查詢時就會利用索引,大大提高速度。但 這種變通是有限制的,不應引起業務意義上的損失,對於郵政編碼而 言,zipcode like “21_ _ _” 和 zipcode >“21000” 意義是完全一致的。
*********************************************************人各有志,但富貴在天,人生允許彷徨,但不允許蹉跎.
8.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 次。
同時,按照某個字段進行排序的時候,無論是正序還是倒序,速度是基本相當的。
********************************************************
9.關於節省數據查詢系統開銷方面的措施
(1)使用TOP盡量減少取出的數據量
(2)字段提取要按照“需多少、提多少”的原則,避免“select *”
字段大小越大,數目越多,select所耗費的資源就越多,比如取int類型的字段就會比取char的快很多。我們每少提取一個字段,數據的提取速度就會有相應的提升。提升的幅度根據舍棄的字段的大小來判斷
(3)count(*) 與 count(字段) 方法比較
用 count(*)和用 count(主鍵)的速度是相當的,而count(*)卻比其他任何除主鍵以外的字段匯總速度要快,而且字段越長,匯總速度就越 慢。如果用 count(*), SQL SERVER會自動查找最小字段來匯總。當然,如果您直接寫count(主鍵)將會來的更直接些
(4)有嵌套查詢時,盡可能在內層過濾掉數據
如果一個列同時在主查詢和where子句中出現,很可能當主查詢中的列值改變之后,子查詢必須重新查詢一次。而且查詢嵌套層次越多,效率越低,因此應當盡量避免子查詢。如果子查詢不可避免,那么要在子查詢中過濾掉盡可能多的行
(5)多表關聯查詢時,需注意表順序,並盡可能早的過濾掉數據
在使用Join進行多表關聯查詢時候,應該使用系統開銷最小的方案。連接條件要充份考慮帶有索引的表、行數多的表,並注意優化表順序;說的簡單一點,就是盡可能早的將之后要做關聯的數據量降下來。
一般情況下,sqlserver 會對表的連接作出自動優化。例如:
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可能會首先使用c表。它的選擇原則是相對於該查詢限制為單行或少數幾行,就可 以減少在其他表中查找的總數據量。絕大多數情況下,sql server 會作出最優的選擇,但如果你發覺某個復雜的聯結查詢速度比預計的要慢,就可以使 用SET FORCEPLAN語句強制sql server按照表出現順序使用表。如上例加 上:SET FORCEPLAN ON…….SET FORCEPLAN OFF 表的執行順序將會按照你所寫的順序執行。在查詢分析器中查看2種執行效 率,從而選擇表的連接順序。SET FORCEPLAN的缺點是只能在存儲過程中使用
原文:http://hi.baidu.com/mayw1985/item/2092f0427fcdf5e6dc0f6cab