30 分鐘快快樂樂學 SQL Performance Tuning
• Primary Key 欄位的長度儘量小,能用 small integer 就不要用 integer。例如員工資料表,若能用員工編號當主鍵,就不要用身分證號碼。
• 一般欄位亦同。若該資料表要存放的資料不會超過 3 萬筆,用 small integer 即可,不必用 integer。
• 文字欄位若長度固定,如:身分證號碼,就不要用 varchar 或 nvarchar,應該用 char 或 nchar。
• 文字欄位若長度不固定,如:地址,則該用 varchar 或 nvarchar。除了可節省儲存空間外,存取硬碟時也會較有效率。
• 設計欄位時,若其值可有可無,最好也給一個預設值,並設成「不允許 NULL」(一般欄位預設為「允許 NULL」)。因為 SQL Server 在存放和查詢有 NULL 的資料表時,會花費額外的運算動作 [2]。
• 若一個數據表的欄位過多,應垂直切割成兩個以上的資料表,並可用同名的 Primary Key 一對多連結起來,如:Northwind 的 Orders、Order Details 資料表。以避免在存取資料時,以「集簇索引 (clustered index)」掃描時會載入過多的資料,或修改資料時造成互相鎖定或鎖定過久。
------------------------------
2、適當地建立索引
• 記得自行幫 Foreign Key 欄位建立索引,即使是很少被 JOIN 的資料表亦然。
• 替常被查詢或排序的欄位建立索引,如:常被當作 WHERE 子句條件的欄位。
• 用來建立索引的欄位,長度不宜過長,不要用超過 20 個 Byte
• 不要替內容重複性高的欄位建立索引,如:性別;反之,若重複性低的欄位則適合建立索引,如:姓名。
• 不要替使用率低的欄位建立索引,以免浪費硬碟空間。
• 不宜替過多欄位建立索引,否則反而會影響到「INSERT、UPDATE、DELETE」的性能,尤其是以「OLTP (聯機事務處理;線上交易)」為主的網站資料庫。
• 若資料表存放的資料很少,就不必刻意建立索引。否則可能資料庫沿著存放索引的「樹狀結構」(Balanced Tree) 去搜尋索引中的資料,反而比掃描整個資料表還慢。
• 若查詢時符合條件的資料很多,則透過「非集簇索引 (non-clustered index)」搜尋的性
• 建立「集簇索引」的欄位選擇至為重要,會影響到整個索引結構的性能。要用來建立「集簇索引」的欄位,務必選擇「整數」型別 (鍵值會較小)、唯一、不可為 NULL。
------------------------------
3、適當地使用索引
• 有些書籍會提到,使用「LIKE、%」做模糊查詢時,即使您已替某個欄位建立索引 (如下方程式碼的 CustomerID欄位),但以常量字元開頭才會使用到索引,若以萬用字元 (%) 開頭則不會使用索引,如下所示:
USE Northwind; GO SELECT * FROM Orders WHERE CustomerID LIKE 'D%'; --使用索引 SELECT * FROM Orders WHERE CustomerID LIKE '%D'; --不使用索引 |
在 SQL Server 2005 執行完成後按 Ctrl+L,可檢閱如下圖的「執行計劃」。
圖 1 可看出「查詢最佳化程式」有使用到索引做搜尋
圖 2 在此的「集簇索引」掃描,並未直接使用索引,性能上幾乎只等於掃描整個資料表
但經版工反覆測試,這種語法是否會使用到索引,抑或會逐筆掃描,並非絕對的。仍要看所下的查詢關鍵詞,以及欄位內 所儲存的資料內容而定。但對於儲存資料筆數龐大的資料表,最好還是少用 LIKE 做模糊查詢。
• 以下的運算子會造成「負向查詢」,常會讓「查詢最佳化程式」無法有效地使用索引,最好能用其它運算子和語法改寫 (經版工測試,並非有負向運算子,就絕對無法使用索引):
NOT 、 != 、 <> 、 !> 、 !< 、 NOT EXISTS 、 NOT IN 、 NOT LIKE
• 避免讓 WHERE 子句中的欄位,去做字串的串接或數字運算,否則可能導致「查詢最佳化程式」無法直接使用索引,而改採「集簇索引掃描」(經版工測試並非絕對)。
• 資料表中的資料,會依照「集簇索引」欄位的順序存放,因此當您下 BETWEEN、GROUP BY、ORDER BY 時若有包含「集簇索引」欄位,由於資料已在資料表中排序好,因此可提升查詢速度。
• 若使用「複合索引」,要注意索引順序上的第一個欄位,才適合當作過濾條件。
------------------------------
4、避免在 WHERE 子句中對欄位使用函式
對欄位使用函式,也等於對欄位做運算或串接的動作,一樣可能會讓「查詢最佳化程式」無法有效地使用索引。但真正對性能影響最重大的,是當您的資料表內若有 10 萬筆資料,則在查詢時就需要呼叫函式 10 萬次,這點才是真正的性能殺手。程式設計師應注意,在系統開發初期可能感覺不出差異,但當系統上線且資料持續累積後,這些語法細節所造成的性能問題就會逐步浮現。
SELECT * FROM Orders WHERE DATEPART(yyyy, OrderDate) = 1996 AND DATEPART(mm, OrderDate)=7 可改成 SELECT * FROM Orders WHERE OrderDate BETWEEN '19960701' AND '19960731' |
SELECT * FROM Orders WHERE SUBSTRING(CustomerID, 1, 1) = 'D' 可改成 SELECT * FROM Orders WHERE CustomerID LIKE 'D%' |
注意當您在下 UPDATE、DELETE 語句時,若有采用 WHERE 子句,也應符合上述原則。。
------------------------------
5、AND 與 OR 的使用
在 AND 運算中,「只要有一個」條件有用到索引 (如下方的 CustomerID),即可大幅提升查詢速度,如下圖 3 所示:
SELECT * FROM Orders WHERE CustomerID='VINET' AND Freight=32.3800 --使用索引,會出現下圖 3 的畫面 |
SELECT * FROM Orders WHERE Freight=32.3800 --不使用索引,會出現上圖 2 的畫面 |
圖 3
但在 OR 運算中,則要「所有的」條件都有可用的索引,才能使用索引來提升查詢速度。因此 OR 運算子的使用必須特別小心。
若您將上方 AND 的範例,邏輯運算子改成 OR 的話,如下所示:
SELECT * FROM Orders WHERE CustomerID='VINET' OR Freight=32.3800 |
由於無法有效地使用索引,也會出現圖 2 的畫面。
在使用 OR 運算子時,只要有一個條件 (欄位) 沒有可用的索引,則其它所有的條件 (欄位) 都有索引也沒用,只能如圖 2 般,把整個資料表或整個集簇索引都掃描過,以逐筆比對是否有符合條件的資料。
據網路上檔案的說法 [1],上述的 OR 運算語句,我們還可用 UNION 聯集適當地改善,如下:
SELECT * FROM Orders WHERE CustomerID='VINET' UNION SELECT * FROM Orders WHERE Freight=32.3800 |
此時您再按 Ctrl+L 檢閱「執行計劃」,會發現上半段的查詢會使用索引,但下半段仍用集簇索引掃描,對性能不無小補。
------------------------------
6、適當地使用子查詢
相較於「子查詢 (Subquery)」,若能用 JOIN 完成的查詢,一般會比較建議使用後者。原因除了 JOIN 的語法較容易理解外,在多數的情況下,JOIN 的性能也會比子查詢較佳;但這並非絕對,也有的情況可能剛好相反。
我們知道子查詢可分為「獨立子查詢」和「關聯子查詢」兩種,前者指子查詢的內容可單獨執行,後者則無法單獨執行,亦即外層查詢的「每一次」查詢動作都需要引用內層查詢的資料,或內層查詢的「每一次」查詢動作都需要參考外層查詢的資料。
以下我們看一個比較極端的例子 [2]。若我們希望所有查詢出來的資料,都能另外給一個自動編號,版工我在之前的文章「ASP.NET 資料分頁第一篇 - 探討分頁原理及 SQL Server 2005 的 ROW_NUMBER 函式」中有介紹過,可用
SQL Server 2005 中新增的 ROW_NUMBER 函式輕易地達成,且 ROW_NUMBER 函式還能再加上「分群 (PARTITION BY)」等功能,而且執行效能極佳。
圖 4 將 Orders 資料表的 830 筆資料都撈出來,並在右側給一組自動編號
現在我們要如上圖 4 般,將 Northwind 資料庫中 Orders 資料表的 830 筆資料都撈出來,並自動給一組編號,若用 ROW_NUMBER 函式的寫法如下所示,而且性能極佳,只要 2 ms (毫秒),亦即千分之二秒。
SET STATISTICS TIME ON SELECT OrderID, ROW_NUMBER() OVER(ORDER BY OrderID) AS 編號 FROM dbo.Orders |
但如果是傳統的「子查詢」寫法,或 輔以 AS 關鍵詞的「衍生資料表」的語法,寫法必須如下 (拷貝後在 SQL Server 中實際可執行):
SET STATISTICS TIME ON SELECT OrderID, (SELECT COUNT(*) FROM dbo.Orders AS 內圈 WHERE 內圈.OrderID <= 外圈.OrderID) AS 編號 FROM dbo.Orders AS 外圈 ORDER BY 編號 |
但這種舊寫法,會像先前所提到的,外層 (外圈) 查詢的「每一次」查詢動作都需要引用內層 (內圈) 查詢的資料。以上方示例而言,外層查詢的每一筆資料,都要等內層查詢「掃描整個資料表」並作比對和計數,因此 830 筆資料每一筆都要重複掃描整個資料表 830 次,所耗用的時間也因此爆增至 170 ms。
若您用相同的寫法,去查詢 AdventureWorks 資料庫中,有 31,465 筆資料的 Sales.SalesOrderHeader 資料表,用 ROW_NUMBER 函式要 677 ms,還不到 1 秒鐘;但用子查詢的話,居然要高達 233,835 ms,將近快 4 分鐘的時間。
-- 用ROW_NUMBER的寫法,改查詢 AdventureWorks 資料庫 (31,465 筆資料,要 677 ms,還不到 1 秒鐘) SELECT SalesOrderID, ROW_NUMBER() OVER(ORDER BY SalesOrderID) AS rownum FROM Sales.SalesOrderHeader |
-- 用「子查詢」的寫法,改查詢 AdventureWorks 資料庫 (31,465 筆資料,要233,835
ms,將近 4 分鐘) SELECT SalesOrderID, (SELECT COUNT(*) FROM Sales.SalesOrderHeader AS 內圈 WHERE 內圈.SalesOrderID <= 外圈.SalesOrderID) AS 編號 FROM Sales.SalesOrderHeader AS 外圈 ORDER BY 編號 |
雖然這是較極端的範例,但由此可知子查詢的撰寫,在使用上不可不慎,尤其是「關聯子查詢」。程式設計師在系統開發初期、資料量還很少時感受不到此種 SQL 語法的重大陷阱;但等到系統上線幾個月或一兩年後,就會有反應遲緩的現象, 不可不慎。
注:AS 關鍵詞及「衍生資料表」是 SQL Server 的語法,「衍生資料表」只會存在記憶體中,AS 關鍵詞的作用是賦予一個別名。過去許多必須用暫存資料表或 View (檢視) 的情況,現在都可以用「衍生資料表」來取代,如此一來不但可以降低資料庫管理工作的負擔,亦可提升查詢效能。
------------------------------
7、其他查詢技巧
• DISTINCT、ORDER BY 語法,會讓資料庫做額外的計算。此外「聯集」的使用,若沒有要剔除重複資料的需求,使用 UNION ALL 會比 UNION 更優,因為後者會加入類似 DISTINCT 的演算法。
• 在 SQL Server 2005 中,存取資料庫物件時,最好明確指定該物件的「結構描述 (Schema)」,也就是使用兩節式的名稱,如下方程式碼所示。否則若呼叫者的預設 Schema 不是 dbo,則 SQL Server 在執行時,會先尋找該使用者預設 Schema 所搭配的物件,找不到的話才會轉而使用預設的 dbo,會多耗費尋找的時間。因此若要執行一個叫做 dbo.mySP1 的 Stored Procedure,應使用以下的兩節式名稱:
EXEC dbo.mySP1 |
------------------------------
8、儘可能用 Stored Procedure 取代應用程式直接存取資料表
Stored Procedure 除了經過事先編譯、性能較好以外,亦可節省 SQL 語句傳遞的網路頻寬,也方便商業邏輯的重複使用。再搭配自訂函式和 View 的使用,將來若要修改資料表結構、重新切割或「反正規化」時亦較方便。
------------------------------
9、儘可能在資料來源層,就先過濾資料
使用 SELECT 語法時,儘量避免傳回所有的資料至前端而不設定 WHERE 等過濾條件。雖然 ASP.NET 中 SqlDataSource、ObjectDataSource 控制元件的 FilterExpression 可再做篩選,GridView 控制元件的 SortExpression 可再做排序,但會多消耗掉資料庫的系統資源、web server 的記憶體和網路頻寬。最好還是在資料庫和資料來源層,就先用 SQL 條件式或 Stored Procedure篩選出所要的資料。有關這方面,網友們可參考版工我之前寫的「ASP.NET 資料分頁」系列的四篇帖子。
------------------------------
結論:
本文的觀念,不管是寫 SQL statement、Stored Procedure、自訂函式或 View 皆然。本文只是挑出程式設計師較容易犯的 SQL 語法性能問題,以期能在短時間瀏覽過本文後,在寫 ADO.NET 程式時能修正以往隨興的 SQL 語句撰寫習慣。文中提到的幾點,只不過是 SQL 語法性能議題的入門。市面上有很多更進階的書籍,例如:「The
Art of SQL,有興趣者可自行翻閱。
------------------------------------------------------------
------------------------------------------------------------
參考檔案:
------------------------------
參考書籍:
------------------------------
相關檔案:
相關推薦
30 分鐘快快樂樂學 SQL Performance Tuning
• Primary Key 欄位的長度儘量小,能用 small integer 就不要用 integer。例如員工資料表,若能用員工編號當主鍵,就不要用身分證號碼。 • 一般欄位亦同。若該資料表要存放的資料不會超過 3 萬筆,用 small integer 即可,不必用 integer。 • 文字欄位若長度
30分鐘全面解析-SQL事務+隔離級別+阻塞+死鎖
以前總是追求新東西,發現基礎才是最重要的,今年主要的目標是精通SQL查詢和SQL效能優化。 本系列主要是針對T-SQL的總結。 概述: 本篇主要是對SQL中事務和併發的詳細講解。 一、事務 1.什麼是事務 為單個工作單元而執行的一系列操作。如查詢、修改資料、修改資料定義。 2.語法 (
Microsoft.SQL.Server2012.Performance.Tuning.Cookbook學習筆記(一)
str perm phi prev pid brush -c rpc enabled 一、Creating a trace or workload 註意點: In the Trace Properties dialog box, there is a checkbox op
30分鐘帶你學透快應用介面開發的最正確姿勢
這篇文章適合快速開發快應用,提高效率,不管你是資深的前端開發,還是剛入門快應用開發的菜鳥,本文都值得你一讀。通過閱讀本文,我相信一定能節省你的時間,少走很多彎路,將主要的精力放在專案的開發中來。 一、入門式 從官網的介紹可以知道,快應用是以前端技術棧作為開發基礎,渲染
SQL 日期 提前 30分鐘 日期格式化
問題描述:資料庫表中有個時間欄位,我需要將這個欄位的時間往前提前30分鐘顯示,比如資料庫存的是2017-12-14 10:00:00,那麼我希望取出來是2017-12-14 09:30:00; &n
【跟我學oracle18c】第四十九天:Database 2 Day + Performance Tuning Guide: 2.1使用自動工作負載儲存庫收集資料庫統計資訊(AWR,ASH)
2.1 Gathering Database Statistics Using the Automatic Workload Repository 資料庫統計資訊提供關於資料庫上的負載型別以及資料庫使用的內部和外部資源的資訊。要使用ADDM準確診斷資料庫的效能問題,必須提供統計資訊。
30分鐘學會如何使用Shiro
springmvc+mybatis dubbo+zookeeper restful redis分布式緩存 shiro kafka 一、架構要學習如何使用Shiro必須先從它的架構談起,作為一款安全框架Shiro的設計相當精妙。Shiro的應用不依賴任何容器,它也可以在JavaSE下使用。但
Shell腳本編程30分鐘入門
shell編程 ace sta .sh bsd 數據類型 對象 重定義 勝任 什麽是Shell腳本 Shell腳本(英語:Shell script),又稱Shell命令稿、程序化腳本,是一種電腦程序與文本文件,內容由一連串的shell命令組成,經由Unix Shell直譯其
30分鐘掌握ES6/ES2015核心內容[上和下], 不錯的說
還得 天下 span default es6 ava arguments nts http ECMAScript 6(以下簡稱ES6)是JavaScript語言的下一代標準。因為當前版本的ES6是在2015年發布的,所以又稱ECMAScript 2015。 也就是說,E
php 利用cookie設置用戶30分鐘未操作自動退出
func 成功 color emp ase upper highlight 解密 tmpl 登陸控制器需要做的登陸成功把用戶ID等信息存入cookie: $this->systemSetKey(array(‘name‘=>$admin_info[‘admin_
30分鐘LINQ教程
tool in子句 str from sql語句 自己的 object 找到 出現 千萬別被這個頁面的滾動條嚇到!!! 我相信你一定能在30分鐘之內看完它!!! 在說LINQ之前必須先說說幾個重要的C#語言特性 一:與LINQ有關的語言特性 1.隱式類型
項目實戰:iOS極光推送集成(30分鐘搞定)
adg append ati 技術分享 tro markdown ocs sym xcode 推送有非常多,如個推、友盟、融雲和極光等等。在這裏就講下怎樣使用極光推送。主要內容是將官方文檔資料詳細匯總並一步一步集成到項目中,您也能夠直接去官方文檔閱
正則表達式30分鐘入門教程
位數 explicit 模式 his tca 再次 miss 影響 註釋 deerchao的blog Be and aware of who you are. 正則表達式30分鐘入門教程 來園子之前寫的一篇正則表達式教程,部分翻譯自codeproje
30分鐘了解Kotlin基本語法
kotlin kotlin基本語法 本文來源:你我學習網 http://www.niwoxuexi.com,轉載請備註出處:https://www.niwoxuexi.com/blog/kotlin/article/236.html一、定義包包的聲明應處於源文件頂部:package my.demo
輕松學SQL Server數據庫pdf
圖形 復數 查詢 身份驗證 12.1 算術運算符 賦值運算 update window 下載地址:網盤下載 目錄: 第1章 數據庫與SQL Server 2008 11.1 數據庫基礎 21.1.1 數據庫的概念 21.1.2 數據庫模型 21.2 什麽是關系型數據庫 21
新手學SQL Server pdf
源代碼 ffffff 術語 互聯 容易 manage ffi 二進制 emma 下載地址:網盤下載 內容簡介編輯《新手學SQL Server》:打開SQL Server數據庫技術大門的金鑰匙 ◎由淺入深:從基本概念開始講解,逐步深入到實際開發 ◎示例豐富:講解知識
30分鐘學會如何使用Shiro(轉)
字段 col inf lan getc 含義 掌握 ide 包含 本文轉自http://www.cnblogs.com/learnhow/p/5694876.html 感謝作者 本篇內容大多總結自張開濤的《跟我學Shiro》原文地址:http://jinnianshilo
Spark Performance Tuning (性能調優)
() man inter ber index data- key 兩種 跟蹤 在集群上的 Spark Streaming application 中獲得最佳性能需要一些調整.本節介紹了可調整的多個 parameters (參數)和 configurations (配置)提高
Performance Tuning
mysql- www. mar nosql tutorials chm tab cas -c MySQL Related Performance Tuning. https://www.askapache.com/mysql/mysql-performance-tunin
自動登陸已失敗多次,30分鐘內請手動登陸
cnblogs post 衛士 失敗 spa 恢復 跳板 class 報錯 遠程登陸跳板機時,該電腦未加入域,遇到報錯:自動登陸已失敗多次,30分鐘內請手動登陸 本地登陸正常。 解決辦法:卸載跳板機上的360安全衛士,恢復正常。 自動登陸已失敗多次,30分鐘內請手動登陸