T-SQL 編碼標準
Brian Walker
可能讓人覺得很奇怪,但好像的確沒有什么“正式的”T-SQL 編碼標準。早在 1999 年末的時候,我驚喜地發(fā)現(xiàn) John Hindmarsh 提出的 SQL Server 7.0 標準,我在 2000 年 2 月的社論中對他的某些建議進行了總結。(2000 年 2 月以及本月的“下載”中都包括了 John 原來的標準。)后來,Ron Talmage 撰寫了一系列專欄文章,提出了他對各種“最佳方法”的建議,當然,SQL Server 小組也已正式發(fā)布了 SQL Server 最佳方法分析器 (SQLBPA)。現(xiàn)在,一位具有超過 25 年經(jīng)驗的數(shù)據(jù)庫管理員和應用程序開發(fā)員 Brian Walker 又提出了他的建議和提示。

進行 T-SQL 編程時常常會忽略編碼標準,但這些標準卻是開發(fā)小組順利開展工作的關鍵工具。這里介紹的編碼標準是我多年的開發(fā)成果。它們當然還沒有得到普遍接受,而且不可否認,有些標準帶有主觀色彩。我的目的實際上更多的是為了提高大家的意識,而不是吹捧自己是 T-SQL 樣式方面的仲裁者:最重要的是要建立某些合理的編碼標準并遵循這些標準。您在這篇文章中會發(fā)現(xiàn)有關 T-SQL 編程的一系列不同的編碼標準、技巧和提示。它們并未以任何特定的優(yōu)先級或重要性順序列出。
讓我們從格式開始。表面上,T-SQL 代碼的格式似乎并不重要,但一致的格式可以使您的同事(不論是同一小組的成員還是更大范圍的 T-SQL 開發(fā)團隊的成員)更輕松地瀏覽和理解您的代碼。T-SQL 語句有一個結構,遵循一目了然的結構使您可以更輕松地查找和確認語句的不同部分。統(tǒng)一的格式還使您可以更輕松地在復雜 T-SQL 語句中增刪代碼段,使調試工作變得更容易。下面是 SELECT 語句的格式示例:
SELECT C.Name , E.NameLast , E.NameFirst , E.Number , ISNULL(I.Description,'NA') AS Description FROM tblCompany AS C JOIN tblEmployee AS E ON C.CompanyID = E.CompanyID LEFT JOIN tblCoverage AS V ON E.EmployeeID = V.EmployeeID LEFT JOIN tblInsurance AS I ON V.InsuranceID = I.InsuranceID WHERE C.Name LIKE @Name AND V.CreateDate > CONVERT(smalldatetime, '01/01/2000') ORDER BY C.Name , E.NameLast , E.NameFirst , E.Number , ISNULL(I.Description,'NA') SELECT @Retain = @@ERROR, @Rows = @@ROWCOUNT IF @Status = 0 SET @Status = @Retain
?一個嵌套代碼塊中的語句使用四個空格的縮進。(上述代碼中的多行 SELECT 語句是一個 SQL 語句。)在同一語句中開始新行時,使 SQL 關鍵字右對齊。將代碼編輯器配置為使用空格,而不是使用制表符。這樣,不管使用何種程序查看代碼,格式都是一致的。
?大寫所有的 T-SQL 關鍵字,包括 T-SQL 函數(shù)。變量名稱及光標名稱使用混和大小寫。數(shù)據(jù)類型使用小寫。
?表名別名要簡短,但意義要盡量明確。通常,使用大寫的表名作為別名,使用 AS 關鍵字指定表或字段的別名。
?當一個 T-SQL 語句中涉及到多個表時,始終使用表名別名來限定字段名。這使其他人閱讀起來更清楚,避免了含義模糊的引用。
?當相關數(shù)字出現(xiàn)在連續(xù)的代碼行中時(例如一系列 SUBSTRING 函數(shù)調用),將它們排成列。這樣容易瀏覽數(shù)字列表。
?使用一個(而不是兩個)空行分隔 T-SQL 代碼的邏輯塊,只要需要就可以使用。
?聲明 T-SQL 局部變量(例如 @lngTableID)時,使用適當?shù)臄?shù)據(jù)類型聲明和一致的大寫。
?始終指定字符數(shù)據(jù)類型的長度,并確保允許用戶可能需要的最大字符數(shù),因為超出最大長度的字符會丟失。
?始終指定十進制數(shù)據(jù)類型的精度和范圍,否則,將默認為未指定精度和整數(shù)范圍。
?使用錯誤處理程序,但要記住行首 (BOL) 中的錯誤檢查示例不會象介紹的那樣起作用。用來檢查 @@ERROR 系統(tǒng)函數(shù)的 T-SQL 語句 (IF) 實際上在進程中清除了 @@ERROR 值,無法再捕獲除零之外的任何值。(即使示例起作用,它們也只能捕獲最后發(fā)生的一個錯誤,而不是您更想捕獲的第一個錯誤。)必須使用 SET 或 SELECT 立即捕獲錯誤代碼,如前面示例所示。如果狀態(tài)變量仍然為零,應轉換到狀態(tài)變量。
?避免使用“未聲明的”功能,例如系統(tǒng)表中未聲明的列、T-SQL 語句中未聲明的功能或者未聲明的系統(tǒng)存儲過程或擴展的存儲過程。
?不要依賴任何隱式的數(shù)據(jù)類型轉換。例如,不能為數(shù)字變量賦予字符值,而假定 T-SQL 會進行必要的轉換。相反,在為變量賦值或比較值之前,應使用適當?shù)?CONVERT 函數(shù)使數(shù)據(jù)類型相匹配。另一個示例:雖然 T-SQL 會在進行比較之前對字符表達式進行隱式且自動的 RTRIM,但不能依賴此行為,因為兼容性級別設置非字符表達式會使情況復雜化。
?不要將空的變量值直接與比較運算符(符號)比較。如果變量可能為空,應使用 IS NULL 或 IS NOT NULL 進行比較,或者使用 ISNULL 函數(shù)。
?不要使用 STR 函數(shù)進行舍入,此函數(shù)只能用于整數(shù)。如果需要十進制值的字符串形式,應先使用 CONVERT 函數(shù)(轉至不同的范圍)或 ROUND 函數(shù),然后將其轉換為字符串。也可以使用 CEILING 和 FLOOR 函數(shù)。
?使用數(shù)學公式時要小心,因為 T-SQL 可能會將表達式強制理解為一個不需要的數(shù)據(jù)類型。如果需要十進制結果,應在整數(shù)常量后加點和零 (.0)。
?決不要依賴 SELECT 語句會按任何特定順序返回行,除非在 ORDER BY 子句中指定了順序。
?通常,應將 ORDER BY 子句與 SELECT 語句一起使用??深A知的順序(即使不是最方便的)比不可預知的順序強,尤其是在開發(fā)或調試過程中。(部署到生產(chǎn)環(huán)境中之前,可能需要刪除 ORDER BY 子句。)在返回行的順序無關緊要的情況下,可以忽略 ORDER BY 的開銷。
?不要在 T-SQL 代碼中使用雙引號。應為字符常量使用單引號。如果沒有必要限定對象名稱,可以使用(非 ANSI SQL 標準)括號將名稱括起來。
?在 SQL Server 2000 中,盡量使用表變量來代替臨時表。如果表變量包含大量數(shù)據(jù),請注意索引非常有限(只有主鍵索引)。
?先在例程中創(chuàng)建臨時表,最后再顯式刪除臨時表。將 DDL 與 DML 語句混合使用有助于處理額外的重新編譯活動。
?要認識到臨時表并不是不可使用,適當?shù)厥褂盟鼈兛梢允鼓承├谈行В?,當需要重復引用大型表或常用表中的某個數(shù)據(jù)集時。但是,對于一次性事件,最好使用導出表。
?使用表值 UDF 時要小心,因為在變量(而不是常量)中傳遞某個參數(shù)時,如果在 WHERE 子句中使用該參數(shù),會導致表掃描。還要避免在一個查詢中多次使用相同的表值 UDF。但是,表值 UDF 確實具有某些非常方便的動態(tài)編譯功能。[相關資料:參閱 Tom Moreau 在 2003 年 11 月份“生成序列號”專欄中的“使用 UDF 填充表變量”。-編者按]
?幾乎所有的存儲過程都應在開始時設置 SET NOCOUNT ON,而在結束時設置 SET NOCOUNT OFF。[SET NOCOUNT ON 使 SQL Server 無需在執(zhí)行存儲過程的每個語句后向客戶端發(fā)送 DONE_IN_PROC 消息。- 編者按] 此標準同樣適用于觸發(fā)器。
?只要在例程中使用多個數(shù)據(jù)庫修改語句,包括在一個循環(huán)中多次執(zhí)行一個語句,就應考慮聲明顯式事務。
?使用基于光標的方法或臨時表方法之前,應先尋找基于集的解決方案來解決問題。基于集的方法通常更有效。
?與臨時表一樣,光標并不是不可使用。對小型數(shù)據(jù)集使用 FAST_FORWARD 光標通常要優(yōu)于其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數(shù)據(jù)時。在結果集中包括“合計”的例程通常要比使用光標執(zhí)行的速度快。如果開發(fā)時間允許,基于光標的方法和基于集的方法都可以嘗試一下,看哪一種方法的效果更好。
?使用包含序號(從 1 到 N)的表很方便。
?理解 CROSS JOIN 的工作原理并加以利用。例如,您可以在工作數(shù)據(jù)表和序號表之間有效地使用 CROSS JOIN,結果集中將包含每個工作數(shù)據(jù)與序號組合的記錄。
?我的結束語是:T-SQL 代碼往往很簡潔,因此如果某個代碼塊看起來很難處理或重復內容較多,那么可能存在一種更簡單,更好的方法。
結論
如果您對我的建議有任何看法,歡迎隨時向我發(fā)送電子郵件進行討論,也可以就其他問題提出您的建議。我希望您將此作為談話的開場白。
其他信息:摘自 Karen 2000 年 2 月份的社論
在標準開發(fā)的前沿陣地上,有一股以 SQL Server 數(shù)據(jù)庫管理員 John Hindmarsh 為首的獨立的新生力量。MCT、MCSE 和 MCDBA 都是最值得您花時間去研究的。John 的貢獻是撰寫了一份詳細的白皮書,概述了他對各種 SQL Server 相關標準提出的建議。我所知道的其他唯一提出類似建議的文章是 Andrew Zanevsky 的《Transact-SQL Programming》(ISBN 1-56592-401-0) 中的“Format and Style”一章。Andrew、SQL Server Professional 的投稿人 Tom Moreau 和 Paul Munkenbeck 以及 John 的朋友兼同事 Stephen James 都為 John 的白皮書做出過貢獻。下面是 John 為編寫存儲過程提供的建議示例:
• |
使用 SQL-92 標準連接句法。 |
• |
為了提高性能,應優(yōu)先使用連接,然后使用子查詢或嵌套查詢。 |
• |
確保變量和參數(shù)的類型和大小與表數(shù)據(jù)列相匹配。 |
• |
確保使用所有變量和參數(shù),或者全部刪除。 |
• |
盡可能將臨時對象放置在本地。 |
• |
只使用在存儲過程中創(chuàng)建的臨時表。 |
• |
檢查輸入?yún)?shù)的有效性。 |
• |
優(yōu)先使用 SELECT...INTO,然后使用 INSERT...SELECT,以避免大量死鎖。 |
• |
維護工作需要的邏輯單元;在可以縮短的情況下,不要創(chuàng)建大量或長時間運行的進程。 |
• |
不要在任何代碼中使用 SELECT *。 |
• |
在過程中使用縮進、塊、制表符和空格(參閱示例腳本)。 |
• |
T-SQL 語句要大寫。 |
• |
在過程中添加大量注釋,確保可以識別進程。在有助于澄清處理步驟的地方使用行注釋。 |
• |
包括事務管理,除非要從 MTS 進程中調用過程。(為 MTS 進程編寫獨立的過程。) |
• |
監(jiān)視 @@TRANCOUNT 以確定事務的責任級別。 |
• |
避免使用 GOTO,錯誤處理程序中除外。 |
• |
避免使用嵌套過程。 |
• |
避免隱式解析對象名稱,確保所有對象都歸 dbo 所有。 |
有關 SQL Server Professional 和 Pinnacle Publishing 的詳細信息,請訪問其 Web 站點 http://www.pinpub.com/
注意:這不是 Microsoft Corporation 的 Web 站點。Microsoft 對該 Web 站點上的內容不承擔任何責任。
本文轉載自 2004 年 12 月份的 SQL Server Professional。除非另行說明,否則版權所有 2004 Pinnacle Publishing, Inc.。保留所有權利。SQL Server Professional 是 Pinnacle Publishing 獨立發(fā)行的刊物。未經(jīng) Pinnacle Publishing, Inc. 事先同意,不得以任何方式使用或復制本文的任何部分(評論文章中的簡短引用除外)。如需與 Pinnacle Publishing, Inc. 聯(lián)系,請撥打 1-800-788-1900。
© 2005 Microsoft Corporation 版權所有。保留所有權利。使用規(guī)定。
原文出處:http://www.microsoft.com/china/msdn/library/data/sqlserver/sp04l9.mspx?mfr=true