一、分頁查詢優化
很多時候我們業務系統實現分頁功能可能會用如下sql實現:
select?*?from?employees?limit?10000,10;
表示從表 中取出從 10001 行開始的 10 行記錄。看似只查詢了 10 條記錄,實際這條 SQL 是先讀取 10010條記錄,然后拋棄前 10000 條記錄,然后讀到后面 10 條想要的數據。因此要查詢一張大表比較靠后的數據,執行效率是非常低的。這是典型的深度分頁問題。
分頁場景優化技巧 1、根據自增且連續的主鍵排序的分頁查詢
首先來看一個根據自增且連續主鍵排序的分頁查詢的例子:
select?*?from?employees?limit?90000,5;
該 SQL 表示查詢從第 90001開始的五行數據,沒添加單獨 order by,表示通過主鍵排序。我們再看表 ,因為主鍵是自增并且連續的,所以可以改寫成按照主鍵去查詢從第 90001開始的五行數據,如下:
select?*?from?employees?where?id?>?90000?limit?5;
查詢的結果是一致的。我們再對比一下執行計劃:
EXPLAIN?select?*?from?employees?limit?90000,5;
EXPLAIN?select?*?from?employees?where?id?>?90000?limit?5;
顯然改寫后的 SQL 走了索引,而且掃描的行數大大減少,執行效率更高。
但是,這條改寫的SQL 在很多場景并不實用,因為表中可能某些記錄被刪后,主鍵空缺,導致結果不一致,如下圖試驗所示(先刪除一條前面的記錄,然后再測試原 SQL 和優化后的 SQL):
兩條 SQL 的結果并不一樣,因此,如果主鍵不連續,不能使用上面描述的優化方法。
另外如果原 SQL 是 order by 非主鍵的字段,按照上面說的方法改寫會導致兩條 SQL 的結果不一致。所以這種改寫得滿足以下兩個條件:
2、根據非主鍵字段排序的分頁查詢
再看一個根據非主鍵字段排序的分頁查詢,SQL 如下:
select?*?from?employees?ORDER?BY?name?limit?90000,5;
EXPLAIN?select?*?from?employees?ORDER?BY?name?limit?90000,5;
發現并沒有使用 name 字段的索引(key 字段對應的值為 null),具體原因是:掃描整個索引并查找到沒索引的行(可能要遍歷多個索引樹)的成本比掃描全表的成本更高,所以優化器放棄使用索引。
知道不走索引的原因,那么怎么優化呢?
其實關鍵是讓排序時返回的字段盡可能少,即考慮使用覆蓋索引進行優化,所以可以讓排序和分頁操作先查出主鍵,然后根據主鍵查到對應的記錄,SQL改寫如下:
select?*?from?employees?e?inner?join?(select?id?from?employees?order?by?name?limit?90000,5)?ed?on?e.id?=?ed.id;
需要的結果與原 SQL 一致,執行時間減少了一半以上,此時查詢和排序都是在覆蓋索引樹上進行的,所以效率較高。我們再對比優化前后sql的執行計劃:
原 SQL 使用的是 file sort 排序,而優化后的 SQL 使用的是索引排序。
二、Join關聯查詢優化
示例表:
‐‐?示例表:
?CREATE?TABLE?`t1`?(
?`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
?`a`?int(11)?DEFAULT?NULL,
?`b`?int(11)?DEFAULT?NULL,
?PRIMARY?KEY?(`id`),
?KEY?`idx_a`?(`a`)
?)?ENGINE=InnoDB?DEFAULT?CHARSET=utf8;
?create?table?t2?like?t1;
?‐‐?插入一些示例數據
?‐‐?往t1表插入1萬行記錄
?drop?procedure?if?exists?insert_t1;
?delimiter?;;
?create?procedure?insert_t1()
?begin
?declare?i?int;
?set?i=1;
?while(i<=10000)do
?insert?into?t1(a,b)?values(i,i);
?set?i=i+1;
?end?while;
?end;;
?delimiter?;
?call?insert_t1();
?
?‐‐?往t2表插入100行記錄
?drop?procedure?if?exists?insert_t2;

?delimiter?;;
?create?procedure?insert_t2()
?begin
?declare?i?int;
?set?i=1;
?while(i<=100)do
?insert?into?t2(a,b)?values(i,i);
?set?i=i+1;
?end?while;
?end;;
?delimiter?;
?call?insert_t2();
mysql的表關聯常見有兩種算法
1、 嵌套循環連接 Nested-Loop Join(NLJ) 算法
一次一行循環地從第一張表(稱為驅動表)中讀取行,在這行數據中取到關聯字段,根據關聯字段在另一張表(被驅動表)里取出滿足條件的行,然后取出兩張表的結果合集。
EXPLAIN?select?*?from?t1?inner?join?t2?on?t1.a=?t2.a;??--?//?a字段有索引
從執行計劃中可以看到這些信息:
上面sql的大致流程如下:
整個過程會讀取 t2 表的所有數據(掃描100行),然后遍歷這每行數據中字段 a 的值,根據 t2 表中 a 的值索引掃描 t1 表中的對應行(掃描100次 t1 表的索引,1次掃描可以認為最終只掃描 t1 表一行完整數據,也就是總共 t1 表也掃描了100行)。因此整個過程掃描了 200 行。
2、 基于塊的嵌套循環連接 Block Nested-Loop Join(BNL)算法
把驅動表的數據讀入到 中,然后掃描被驅動表,把被驅動表每一行取出來跟 中的數據做對比。
EXPLAIN?select?*?from?t1?inner?join?t2?on?t1.b=?t2.b;?--?//?b字段沒有索引
Extra 中 的Using join buffer (Block Nested Loop)說明該關聯查詢使用的是 BNL 算法。
上面sql的大致流程如下:
整個過程對表 t1 和 t2 都做了一次全表掃描,因此掃描的總行數為10000(表 t1 的數據總量) + 100(表 t2 的數據總量) =10100。并且 里的數據是無序的,因此對表 t1 中的每一行,都要做 100 次判斷,所以內存中的判斷次數是100 * 10000= 100 萬次。
這個例子里表 t2 才 100 行,要是表 t2 是一個大表, 放不下怎么辦呢?
的大小是由參數 設定的,默認值是 256k。如果放不下表 t2 的所有數據話,策略很簡單,就是分段放。
比如 t2 表有1000行記錄, 一次只能放800行數據,那么執行過程就是先往 里放800行記錄,然后從 t1 表里取數據跟 中數據對比得到部分結果,然后清空 ,再放入 t2 表剩余200行記錄,再次從 t1 表里取數據跟 中數據對比。所以就多掃了一次 t1 表。
被驅動表的關聯字段沒索引為什么要選擇使用 BNL 算法而不使用 Nested-Loop Join 呢?
如果上面第二條sql使用 Nested-Loop Join,那么掃描行數為 100 * 10000 = 100萬次,這個是磁盤掃描。
很顯然,用BNL磁盤掃描次數少很多,相比于磁盤掃描,BNL的內存計算會快得多。因此MySQL對于被驅動表的關聯字段沒索引的關聯查詢,一般都會使用 BNL 算法。如果有索引一般選擇 NLJ 算法,有索引的情況下 NLJ 算法比 BNL算法性能更高.
對于關聯sql的優化
解釋:功能同join類似,但能讓左邊的表來驅動右邊的表,能改表優化器對于聯表查詢的執行順序。
比如:select * from t2 t1 on t2.a = t1.a; 代表指定mysql選著 t2 表作為驅動表。
對于小表定義的明確:
在決定哪個表做驅動表的時候,應該是兩個表按照各自的條件過濾,過濾完成之后,計算參與 join 的各個字段的總數據量,數據量小的那個表,就是“小表”,應該作為驅動表。
三、in和exsits優化
原則:小表驅動大表,即小的數據集驅動大的數據集。
In是In后的表先執行(適用于B表小于A表):
select?*?from?A?where?id?in?(?select?id?from?B)
Exists是Exists前面的表先執行(適用于A表小于B表):
select?*?from?A?where?id?in?(?select?id?from?B)
in:當B表的數據集小于A表的數據集時,in優于exists
select?*?from?A?where?id?in?(select?id?from?B)
//?#等價于:
?for(select?id?from?B){
?select?*?from?A?where?A.id?=?B.id
?}
exists:當A表的數據集小于B表的數據集時,exists優于in
將主查詢A的數據,放到子查詢B中做條件驗證,根據驗證結果(true或false)來決定主查詢的數據是否保留.
select?*?from?A?where?exists?(select?1?from?B?where?B.id?=?A.id)
?//?#?等價于:
?for(select?*?from?A){
?select?*?from?B?where?B.id?=?A.id
?}
?//?#?A表與B表的ID字段應建立索引
總結:
1、EXISTS ()只返回TRUE或FALSE,因此子查詢中的SELECT * 也可以用SELECT 1替換,官方說法是實際執行時會忽略SELECT清單,因此沒有區別
2、EXISTS子查詢的實際執行過程可能經過了優化而不是我們理解上的逐條對比
3、EXISTS子查詢往往也可以用JOIN來代替,何種最優需要具體問題具體分析
四、count(*)查詢優化
--?臨時關閉mysql查詢緩存,為了查看sql多次執行的真實時間
?set?global?query_cache_size=0;
?set?global?query_cache_type=0;
?EXPLAIN?select?count(1)?from?employees;
?EXPLAIN?select?count(id)?from?employees;
?EXPLAIN?select?count(name)?from?employees;
?EXPLAIN?select?count(*)?from?employees;
?--?注意:以上4條sql只有根據某個字段count不會統計字段
經過測試發現:四個sql的執行計劃一樣,說明這四個sql執行效率應該差不多
1、字段有索引: count(*)≈count(1)>count(字段)>count(主鍵 id)
字段有索引,count(字段)統計走二級索引,二級索引存儲數據比主鍵索引少,所以count(字段)>count(主鍵 id)
2、字段無索引: count(*)≈count(1)>count(主鍵 id)>count(字段)
字段沒有索引count(字段)統計走不了索引,count(主鍵 id)還可以走主鍵索引,所以count(主鍵 id)>count(字段)
count(1) 跟 count(字段) 執行過程類似,不過count(1)不需要取出字段統計,就用常量1做統計,count(字段)還需要取出字段,所以理論上count(1)比count(字段)會快一點。
count(*) 是例外,mysql并不會把全部字段取出來,而是專門做了優化(5.7版本),不取值,按行累加,效率很高,所以不需要用count(列名)或count(常量)來替代 count(*)。
為什么對于count(id),mysql最終選擇輔助索引而不是主鍵聚集索引?因為二級索引相對主鍵索引存儲數據更少,檢索性能應該更高,mysql內部做了點優化(應該是在5.7版本才優化)。
常見優化方法
當表中數據量非常大的時候,count這種通過計算統計的都會很慢,所以需要一些優化手段。
1、查詢mysql自己維護的總行數
對于myisam存儲引擎的表做不帶where條件的count查詢性能是很高的,因為myisam存儲引擎的表的總行數會被mysql存儲在磁盤上,查詢不需要計算.
對于innodb存儲引擎的表mysql不會存儲表的總記錄行數(因為有MVCC機制,后面會講),查詢count需要實時計算.
2、show table status
如果只需要知道表總行數的估計值可以用如下sql查詢,性能很高
3、將總數維護到Redis里
插入或刪除表數據行的時候同時維護redis里的表總行數key的計數值(用incr或decr命令),但是這種方式可能不準,很難保證表操作和redis操作的事務一致性.
4、增加數據庫計數表
插入或刪除表數據行的時候同時維護計數表,讓他們在同一個事務里操作
五、阿里MySQL規范解讀 (一) 建表規約
(二) 索引規約 (三) SQL語句 (四) ORM映射
正例:
Map?map?=?new?HashMap<>();
map.put(“start”,?start);
map.put(“size”,?size);
六、MySQL數據類型選擇
在MySQL中,選擇正確的數據類型,對于性能至關重要。一般應該遵循下面兩步:
在MySQL數據類型設置方面,盡量用更小的數據類型,因為它們通常有更好的性能,花費更少的硬件資源。并且,盡量 把字段定義為NOT NULL,避免使用NULL.
1、數值類型
優化建議:
2、日期和時間
優化建議:
3、字符串
優化建議:
PS:INT顯示寬度
我們經常會使用命令來創建數據表,而且同時會指定一個長度,如下。但是,這里的長度并非是TINYINT類型存儲的最大長度,而是顯示的最大長度。
CREATE?TABLE?`user`(
?`id`?TINYINT(2)?UNSIGNED
);
這里表示user表的id字段的類型是TINYINT,可以存儲的最大數值是255。所以,在存儲數據時,如果存入值小于等于255,如200,雖然超過2位,但是沒有超出TINYINT類型長度,所以可以正常保存;如果存入值大于255,如500,那么MySQL會自動保存為TINYINT類型的最大值255。
在查詢數據時,不管查詢結果為何值,都按實際輸出。這里TINYINT(2)中2的作用就是,當需要在查詢結果前填充0時,命令中加上就可以實現,如:
`id`?TINYINT(2)?UNSIGNED?ZEROFILL
這樣,查詢結果如果是5,那輸出就是05。如果指定TINYINT(5),那輸出就是00005,其實實際存儲的值還是5,而且存 儲的數據不會超過255,只是MySQL輸出數據時在前面填充了0。
換句話說,在MySQL命令中,字段的類型長度TINYINT(2)、INT(11)不會影響數據的插入,只會在使用時有 用,讓查詢結果前填充0。
來源://article/ details/
推薦:
主流Java進階技術(學習資料分享)
PS:因為公眾號平臺更改了推送規則,如果不想錯過內容,記得讀完點一下“在看”,加個“星標”,這樣每次新文章推送才會第一時間出現在你的訂閱列表里。點“在看”支持我們吧!
MySQL 性能優化,索引和查詢優化
要知道為什么使用索引,要知道如何去使用好索引,使自己的查詢達到最優性能,需要先了解索引的數據結構和磁盤的存取原理
1. 不使用順序查找,因為順序查找比較慢,通過特定數據結構的特點來提升查詢速度,這種數據結構就是可以理解成索引。
2. 索引一般以文件形式存儲在磁盤上,索引檢索需要磁盤I/O操作,為了盡量減少磁盤I/O。磁盤往往不是嚴格按需讀取,而是每次都會預讀,而且主存和磁盤以頁為單位交換數據,所以在讀取的數據不在主存中時,會從磁盤中讀取一批數據(頁)到主存中。
3. 不管在哪種程序優化上,要想快速挺高性能,直接將常用的、少變更的數據直接讀取到內存中,使用的時候就直接在內存上讀取,而不去磁盤上讀取,減少I/O操作,這樣就能使程序快上10倍以上。但由于內存容量的限制,也不可能將所有的數據都放內存中。
MySQL索引分類
普通索引:最基本的索引,沒有任何限制。
唯一索引:與”普通索引”類似,不同的就是:索引列的值必須唯一,但允許有空值。
主鍵索引:它是一種特殊的唯一索引,不允許有空值。
全文索引:僅可用于 MyISAM 表,針對較大的數據,生成全文索引很耗時好空間。
組合索引:為了更多的提高mysql效率可建立組合索引,遵循”最左前綴“原則。
覆蓋索引( Indexes):就是直接走的索引,直接在內存中就拿到值,不需要查詢數據庫。如分頁就要走覆蓋索引,因為性能比較高。
聚簇索引( Indexes):主鍵就是聚集索引。聚簇索引保證關鍵字的值相近的元組存儲的物理位置也相同(所以字符串類型不宜建立聚簇索引,特別是隨機字符串,會使得系統進行大量的移動操作),且一個表只能有一個聚簇索引。因為由存儲引擎實現索引,所以,并不是所有的引擎都支持聚簇索引。目前,只有solidDB和InnoDB支持。
非聚簇索引:二級索引葉子節點保存的不是指行的物理位置的指針,而是行的主鍵值。這意味著通過二級索引查找行。
InnoDB對主鍵建立聚簇索引。如果你不指定主鍵,InnoDB會用一個具有唯一且非空值的索引來代替。如果不存在這樣的索引,InnoDB會定義一個隱藏的主鍵,然后對其建立聚簇索引。一般來說,DBMS都會以聚簇索引的形式來存儲實際的數據,它是其二級索引的基礎。
優化要注意的一些事(重點)
1. 索引其實就是一種歸類方式,當某一個字段屬性都不能歸類,建立索引后是沒什么效果的,或歸類就二種(0和1),且各自都數據對半分,建立索引后的效果也不怎么強。
2. 主鍵的索引是不一樣的,要區別理解。
3. 當時間存儲為時間戳保存的可以建立前綴索引。
4. 在什么是字段上建立索引,需要根據查詢條件而定,不要一上來就建立索引,浪費內存還有可能用不到。
5. 大字段(blob)不要建立索引,查詢也不會走索引。
6. 常用建立索引的地方:
1)主鍵的聚集索引
2)外鍵索引
3)類別只有0和1就不要建索引了,沒有意義,對性能沒有提升,還影響寫入性能
4)用模糊其實是可以走前綴索引
7. 唯一索引一定要小心使用,它帶有唯一約束,由于前期需求不明等情況下,可能造成我們對于唯一列的誤判。
8. 由于我們建立索引并想讓索引能達到最高性能,這個時候我們應當充分考慮該列是否適合建立索引,可以根據列的區分度來判斷,區分度太低的情況下可以不考慮建立索引,區分度越高效率越高。
9. 寫入比較頻繁的時候,不能開啟MySQL的查詢緩存,因為在每一次寫入的時候不光要寫入磁盤還的更新緩存中的數據。
10. 建索引的目的:
1)加快查詢速度,使用索引后查詢有跡可循。
2)減少I/O操作,通過索引的路徑來檢索數據,不是在磁盤中隨機檢索。
3)消除磁盤排序,索引是排序的,走完索引就排序完成。
11. 其實建索引的原理就是將磁盤I/O操作的最小化,不在磁盤中排序,而是在內存中排好序,通過排序的規則去指定磁盤讀取就行,也不需要在磁盤上隨機讀取。
12. 由于磁盤整理磁盤碎片,所有有的時候我們也可以通過建立聚集索引來減少這一類的問題。
13. 當一個表中有100萬數據,而經常用到的數據只有40萬或40萬以下,是不用考慮建立索引的,沒什么性能提升。
14. 什么時候不適合建立索引:
1)頻繁更新的字段不適合建立索引
2)where條件中用不到的字段不適合建立索引,都用不到建立索引沒有意義還浪費空間
3)表數據可以確定比較少的不需要建索引
4)數據重復且發布比較均勻的的字段不適合建索引(唯一性太差的字段不適合建立索引),例如性別,真假值
5)參與列計算的列不適合建索引,如:
select * from table where amount+100>1000,-- 這樣是不走索引的,可以改造為:select * from table where amount>1000-100。
15. 使用count統計數據量的時候建議使用count(*)而不是count(列),因為count(*)MySQL是做了優化的。
16. 二次SQL查詢區別不大的時候,不能按照二次執行的時間來判斷優化結果,沒準第一次查詢后又保存緩存數據,導致第二次查詢速度比第二次快,很多時候我們看到的都是假象。
17. 什么時候開MySQL的查詢緩存,交易系統(寫多、讀少)、SQL優化測試,建議關閉查詢緩存,論壇文章類系統(寫少、讀多),建議開啟查詢緩存。
18. Explain 執行計劃只能解釋SELECT操作。
19. 查詢優化可以考慮讓查詢走索引,走索引能提升查詢速度,索引覆蓋是最快的,如下就是讓分頁走覆蓋索引提高查詢速度。
20. 子查詢比join快,雖然規律不絕對,但對大表多數有效
21. 復雜SQL語句優化的思路:
1)首先考慮在一個表中能不能取到有關的信息,盡量少關聯表
2)關聯條件爭取都走主鍵或外鍵查詢條件,能走到對應的索引
3)爭取在滿足業務上走小集合數據查找
4)INNER JOIN 和子查詢哪個更快,場景不一致速度也不同
22. where條件多條件一定要按照小結果集排大結果集前面
23. 盡量避免大事務操作,提高系統并發能力,有時無法避免,改用定時器延遲處理。
24. 什么情況不走索引:
SELECT ` famount ` FROM ` ` WHERE ` famount `+10=30;-- 不會使用索引,因為所有索引列參與了計算
SELECT `famount` FROM `` WHERE LEFT(``,4)
SELECT * FROM ` fuser` WHERE `` LIKE‘138%' -- 走索引
SELECT * FROM ` fuser ` WHERE ` ` LIKE "%7488%" -- 不走索引 -- 正則表達式不使用索引,這應該很好理解,所以為什么在SQL中很難看到regexp關鍵字的原因 -- 字符串與數字比較不使用索引;
EXPLAIN SELECT * FROM `a` WHERE `a`=1 -- 不走索引
select * from fuser where ='xxx' or femail='xx' or fstatus=1 --如果條件中有or,即使其中有條件帶索引也不會使用。換言之,就是要求使用的所有字段,都必須建立索引, 我們建議大家盡量避免使用or 關鍵字
25. 如果MySQL估計使用全表掃描要比使用索引快,則不使用索引。
26. 使用UNION ALL 替換OR多條件查詢并集。
27. 在大數據表刪除也是一個問題,避免刪除過程數據庫奔潰,可以考慮分配刪除,一次刪1000條,刪完后等一會繼續刪除
28. 大數據表優化:
1)建立匯總表
2)建立流水表
3)分庫分表
29. 建立匯總表,首先不用考慮分庫分表,使用定時器定時去匯總。
30. 分表,可以按水平或垂直切分。垂直分表其實就是將經常使用的數據和很少使用的數據進行垂直的切分,切分到不同的庫,提高單庫的數據容量,如:前3個月之前的交易記錄就可以放另一個庫中。
31. 建立流水表,數據冗余,有這個表記錄流水變更就不用去寫復雜SQL計算流水。
32. 分庫,多數據庫相同庫結構,分發處理并發能力,但同時帶來了數據同步問題,也可以使用分庫做主備分離
32. SQL優化順序:
1)盡量少作計算。
2)盡量少 join。
3)盡量少排序。
4)盡量避免 select *。
5)盡量用 join 代替子查詢。
6)盡量少 or。
7)盡量用 union all 代替 union。
8)盡量早過濾。
9)避免類型轉換。
10)優先優化高并發的 SQL,而不是執行頻率低某些“大”SQL。
11)從全局出發優化,而不是片面調整。
12)盡可能對每一條運行在數據庫中的SQL進行 Explain。
33. 如下是30條大數據表優化要點:
1)對查詢進行優化,應盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。
2)應盡量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:select id from t where num is null可以在num上設置默認值0,確保表中num列沒有null值,然后這樣查詢:select id from t where num=0
3)應盡量避免在 where 子句中使用!=或操作符,否則引擎將放棄使用索引而進行全表掃描。
4)應盡量避免在 where 子句中使用or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:select id from t where num=10 or num=20可以這樣查詢:select id from t where num=10 union all select id from t where num=20
5)in 和 not in 也要慎用,否則會導致全表掃描,如:select id from t where num in(1,2,3) 對于連續的數值,能用 between 就不要用 in 了:select id from t where num between 1 and 3
6)下面的查詢也將導致全表掃描:select id from t where name like '李%'若要提高效率,可以考慮全文檢索。
7)如果在 where 子句中使用參數,也會導致全表掃描。因為SQL只有在運行時才會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:select id from t where num=@num可以改為強制查詢使用索引:select id from t with(index(索引名)) where num=@num
8)應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:select id from t where num/2=100應改為:select id from t where num=100*2
9)應盡量避免在where子句中對字段進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:select id from t where (name,1,3)='abc' ,name以abc開頭的id 應改為: select id from t where name like 'abc%'
10)不要在 where 子句中的“=”左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。
11)在使用索引字段作為條件時,如果該索引是復合索引,那么必須使用到該索引中的第一個字段作為條件時才能保證系統使用該索引,否則該索引將不會被使用,并且應盡可能的讓字段順序與索引順序相一致。
12)不要寫一些沒有意義的查詢,如需要生成一個空表結構:select col1,col2 into #t from t where 1=0 這類代碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:create table #t(...)
13)很多時候用 exists 代替 in 是一個好的選擇:select num from a where num in(select num from b) 用下面的語句替換:select num from a where exists(select 1 from b where num=a.num)
14)并不是所有索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重復時,SQL查詢可能不會去利用索引,如一表中有字段sex,male、female幾乎各一半,那么即使在sex上建了索引也對查詢效率起不了作用。
15)索引并不是越多越好,索引固然可 以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有 必要。
16)應盡可能的避免更新 索引數據列,因為 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 索引數據列,那么需要考慮是否應將該索引建為 索引。
17)盡量使用數字型字段,若只含數值信息的字段盡量不要設計為字符型,這會降低查詢和連接的性能,并會增加存儲開銷。這是因為引擎在處理查詢和連接時會逐個比較字符串中每一個字符,而對于數字型而言只需要比較一次就夠了。
18)盡可能的使用 varchar/ 代替 char/nchar ,因為首先變長字段存儲空間小,可以節省存儲空間,其次對于查詢來說,在一個相對較小的字段內搜索效率顯然要高些。
19)任何地方都不要使用 select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段。
20)盡量使用表變量來代替臨時表。如果表變量包含大量數據,請注意索引非常有限(只有主鍵索引)。
21)避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。
22)臨時表并不是不可使用,適當地使用它們可以使某些例程更有效,例如,當需要重復引用大型表或常用表中的某個數據集時。但是,對于一次性事件,最好使用導出表。
23)在新建臨時表時,如果一次性插入數據量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,為了緩和系統表的資源,應先create table,然后insert。
24)如果使用到了臨時表,在存儲過程的最后務必將所有的臨時表顯式刪除,先 table ,然后 drop table ,這樣可以避免系統表的較長時間鎖定。
25)盡量避免使用游標,因為游標的效率較差,如果游標操作的數據超過1萬行,那么就應該考慮改寫。
26)使用基于游標的方法或臨時表方法之前,應先尋找基于集的解決方案來解決問題,基于集的方法通常更有效。
27)與臨時表一樣,游標并不是不可使 用。對小型數據集使用 游標通常要優于其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數據時。在結果集中包括“合計”的例程通常要比使用游標執行的速度快。如果開發時 間允許,基于游標的方法和基于集的方法都可以嘗試一下,看哪一種方法的效果更好。
28)在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每個語句后向客戶端發送 消息。
29)盡量避免大事務操作,提高系統并發能力。
30)盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。