一. 經驗總結
1. 臨時表與內嵌視圖
臨時表會消耗大量資源。如果只需要將數據聯接到其他查詢,則可以試試使用內嵌視圖,以節省資源.
2. 避免Left Join和Null
LEFT JOIN 比 INNER JOIN 消耗資源更多,因爲它們包含與 NULL(不存在)數據匹配的數據
a. 創建一個 TABLE 數據類型,插入第一個表(LEFT JOIN 左側的表)中的所有行,然後使用第二個表中的值更新 TABLE 數據類型
b. 從設計數據庫的角度, 使第二個表中都有與第一個表匹配的行, 以便使用inner join進行檢索.
3. 大表連接查詢時
a. 可試着將一個表需要查出的欄位放到臨時表, 再與另一個表進行連接
b. 建立索引視圖(2005).
二. <引用【IT168技術】>
數據庫性能優化涉及到很多方面,在數據庫開發時可以通過一些基本的優化技巧提高數據庫的性能:
1.原則上爲創建的每個表都建立一個主鍵,主鍵唯一標識某一行記錄,用於強制表的實體完整性。SQL Server 2005 Database Engine 將通過爲主鍵列創建唯一索引來強制數據的唯一性。查詢中使用主鍵時,此索引還可用來對數據進行快速訪問。(注意:如果你建立了主鍵,默認情況下它就是聚集索引)
2.爲每一個外鍵列建立一個索引,如果確認它是唯一的,就建立唯一索引。當在查詢中組合相關表中的數據時,經常在聯接條件中使用外鍵列,索引使SQL Server 2005 數據庫引擎 可以在外鍵表中快速查找相關數據。
3.暫時不要爲其他列建立索引
4.當在TSQL中引用對象時,建議使用對象的架構名稱限定。(使用dbo.sysdatabases代替sysdatabases)未指定架構可能會導致混淆和意義不明確,還有一個重要原因,當很多連接同時運行同一個存儲過程時,如果未指定架構名稱,這些連接可能會因爲要獲取編譯鎖(compile lock)而互相阻塞。
5.使用SET NOCOUNT ON在每個存儲過程的開頭SET NOCOUNT OFF在結尾。當 SET NOCOUNT 爲 ON 時,將不給客戶端發送存儲過程中的每個語句的 DONE_IN_PROC 信息。當使用 Microsoft SQL Server 提供的實用工具執行查詢時,在 Transact-SQL 語句(如 SELECT、INSERT、UPDATE 和 DELETE)結束時將不會在查詢結果中顯示"n rows affected"。如果存儲過程中包含的一些語句並不返回許多實際的數據,則該設置由於大量減少了網絡流量,因此可顯著提高性能。
補充:
1.當 SET NOCOUNT 爲 ON 時,也更新 @@ROWCOUNT 函數。
2. @@ROWCOUNT是返回受上一語句影響的行數,包括找到記錄的數目、刪除的行數、更新的記錄數等,不要認爲是返回查找的記錄數目,而且@@ROWCOUNT要緊跟需要判斷語句,否則@@ROWCOUNT將返回0。
3. 使用錯誤處理程序,用來檢查 @@ERROR 系統函數的 T-SQL 語句 (IF) 實際上在進程中清除了 @@ERROR 值,無法再捕獲除零之外的任何值,必須使用 SET 或 SELECT 立即捕獲錯誤代碼。
6.慎用鎖,可以使用NOLOCK提示,它與READUNCOMMITTED是等價的。更簡單的做法是在存儲過程的開頭SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED,結尾READ COMMITTED。
7.查詢僅僅返回需要的行和列
8.在適當的時候使用事務,儘量將事務放在一個存儲過程中。
9.儘量少的使用臨時表,因爲大量使用臨時表可能使tempdb成爲瓶頸。可以使用表表達式,包括派生表、CTE、視圖和內聯表值UDF。
補充:CTE是SQL Server 2005的一項強大而靈活的功能。它使得SQL Server的可讀性更強,更易於管理,降低了查詢的複雜程度。執行遞歸查詢是CTE最重要也是最強大的功能。
10.避免使用NOT IN,可以用LEFT OUTER JOIN代替它
11.如果需要使用動態SQL,sp_executesql更具優勢,因爲它提供了輸入輸出接口,並且更有可能重用執行計劃,因爲你可以更容易的生成被重複調用的查詢字符串。注意有一點聲明的參數類型儘量和查詢關鍵字字段類型一致,否則可能導致低的查詢效率。
12.當修改你的代碼時,比較前後代碼執行的性能,如果發現在CPU、IO上有大的增長,需要檢查代碼的修改
13.儘量減少和服務器的交互,可以通過一次返回多結果集來解決。
14.一般情況下不需要使用INDEX和JOIN提示,因爲優化器會選擇最優的執行計劃。如果統計信息沒有更新會影響查詢計劃的選擇。
15.在本地測試時,可以看一看語句在CPU,IO或執行時間上是否異常。通常利用命令:set statistics io on, set statistics time on , set showplan on 等。
16.如果用到其他庫的Table或View,可以在當前庫中建立View來實現跨庫操作,最好不要直接使用“databse.shema.table_name”,因爲sp_depends(顯示有關數據庫對象依賴關係的信息)不能顯示出該SP所使用的跨庫table或view,不方便校驗。
--執行下面的存儲過程顯示數據庫中依賴於表的數據庫對象
EXEC sp_depends @objname = N'user';
17.儘量避免大事務操作,慎用holdlock子句,提高系統併發能力。
Code: sql
begin tran
--holdlock人爲加共享鎖
SELECT * from User WITH(HOLDLOCK) where UserID=1
waitfor delay '00:00:10'
commit tran
--會話
SELECT * from User WITH(HOLDLOCK) where UserID=1
update User set userpw=222222 where UserID=1
--若同時執行上述兩個語句,則第二個會話中的select查詢可以執行
--而update必須等待第一個會話中的共享鎖結束後才能執行,即要等待10秒
18.儘量避免反覆訪問同一張或幾張表,尤其是數據量較大的表,可以考慮先根據條件提取數據到臨時表中,然後再做連接。
19.儘量避免使用遊標,遊標的效率較差,因爲遊標基本上是強制優化器執行固定的計劃,並且逐行操作產生大量的開銷;如果使用了遊標,就要儘量避免在遊標循環中再進行表連接的操作。
20.不要在where子句中進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。
21. 儘量使用exists代替select count(*)來判斷是否存在記錄
優化器優化exists謂詞時優化器支持短路(short-circuiting)功能。只要找到一行,不需要再掃描其他行就可以確定該表是否包含行了。
count函數只有在統計表中所有行數時使用, count(1)、count(?)、count(*)哪個效率高的問題,經過測試堆、利用聚集索引、非聚集索引三種情況下這三個的執行效率基本上一樣,生成的查詢計劃是相同的。
22.注意insert、update操作的數據量,防止與其他應用衝突。如果數據量超過一定的數據頁面,系統將會進行鎖升級,頁級鎖會升級成表級鎖。
23.關於涉及tempdb的使用方面
臨時表和表變量被物理的保存在tempdb中,除此之外,SQL Server還爲很多隱式操作在tempdb中存儲數據。包括:作爲查詢執行計劃的一部分的脫機數據,排序,以及維護行版本(2005)。所以tempdb可能會成爲瓶頸。
1. 儘量少的使用distinct、order by、group by、having、join,因爲這些語句會加重tempdb的負擔。
2. 避免頻繁創建和刪除臨時表,減少系統表資源的消耗。
3. 在新建臨時表時,如果一次性插入數據量很大,那麼可以使用select into代替create table,避免log,提高速度;如果數據量不大,爲了緩和系統表的資源,建議先create table,然後insert。
4. 如果臨時表的數據量較大,需要建立索引,那麼應該將創建臨時表和建立索引的過程放在單獨一個子存儲過程中,這樣才能保證系統能夠很好的使用到該臨時表的索引。
5. 如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先truncate table,然後drop table,這樣可以避免系統表的較長時間鎖定。
6. 慎用大的臨時表與其他大表的連接查詢和修改,減低系統表負擔,因爲這種操作會在一條語句中多次使用tempdb的系統表。
24.關於索引的使用方面
1. 儘可能的使用索引字段作爲查詢條件,尤其是聚集索引,必要時可以通過index index_name來強制指定索引
2. 避免對大表查詢時進行table scan,必要時考慮新建索引。
3. 在使用索引字段作爲條件時,如果該索引是聯合索引,那麼必須使用到該索引中的第一個字段作爲條件時才能保證系統使用該索引,否則該索引將不會被使用。
4. 要注意索引的維護,週期性重建索引。