數據庫命名規範

SQL Server 設計.命名編碼規範html


比較好的網址:sql

http://www.cnblogs.com/JimmyZhang/archive/2007/08/30/875504.html
數據庫

1.簡介express


數據庫設計是指對於一個給定的應用環境,構造最優的數據庫模式,創建數據庫及其應用系統,有效存儲數據,知足用戶信息要求和處理要求

數據庫設計和開發標準是使Newegg Support Center的數據庫系統的設計和開發正式化的標準。經過此標準,來規範數據庫設計。


經過一致的系統解決方案,能給咱們的系統帶來如下優勢:


¨       開發出高可管理性的高質量系統


¨       可以快速的進行開發


¨       減小維護代碼的時間


Ø                  很是容易的把代碼從一個項目拷貝至另外一個項目


Ø                  節省把遊標,錯誤處理信息從一個項目中拷貝到另外一個項目中的時間


Ø                  使程序邏輯簡單化


Ø                  不用花費時間在常規的事情上,好比對象名稱轉換等,並可容許屢次設計,編程和對復瑣事件的測試工做


¨                   在代碼出錯時大大節省時間


¨                   只要在第一次是有個良好的設計


 


3.開發環境


3.1數據庫模型


CA公司的ERwin/SQL是數據建模的一個首選工具.


在開發常常改變的項目時,使用ERwin來生成表(建立/刪除),索引,規則,數據類型等數據庫對象的腳本,在對項目文件進行修改以前,請確認已經對這些腳本進行過備份.


任何數據庫的改變,無論是在開發中仍是在產品服務器中,都要用ERwinDiagram中進行相應的修改.若是產品服務器上作出了更改,則必定要對主腳本和ERwin diagram進行相應更新.


3.2 Diagrams


使用Visio進行數據diagram,流程圖,服務器拓樸和其它diagrams進行設計.文檔化系統或者處理流程能夠大大有利於團隊間的協做.


3.3版本控制


推薦使用Visual SourceSafe(VSS)對NESE數據庫對象進行管理.


在任何項目中,都應該有很好的代碼更改控制,初始版本文件應該放到VSS中並被註釋.全部對這些文件的後繼更改都應該放到VSS中管理.


3.4 源碼目錄結構


在項目剛開始時,找到一個全部Team成員都可以訪問的共享.按照如下結構初始化VSS目錄和數據庫子目錄:


\CMD 包括全系統腳本的腳本文件,若是是一個多數據庫的系統,應該有一個可能建立全部數據庫的命令文件.


\DBName  在系統中的每一個數據庫應該有本身的目錄結構,以下:


\CMD:用來建立此數據庫的腳本文件,而且來更改數據庫構架.


\DAT:用來刷新此數據庫的數據文件.


\SP:用來存儲存儲過程的腳本


\TBL:除了下面子目錄,這個目錄應該包括表的定義腳本,每一個表應該有它本身的腳本,此腳本應該包括經表的刪除,建立語句,索引,觸發器,完整性參照,Check約束,默認值約束等,每一個表建立語句應該包括在不一樣的腳本中,並被把歸類到相似於下面的相應的子目錄中.注意:本目錄中的腳本和如下子目錄的腳本應該命名爲它所影響的表名,好比:表名.sql:


\Check:爲每一個表建立獨立的Check約束定義腳本,Check約束應該使用alter table add constraint 來建立,而且每一個alter table 語句只能包括一個contraint。


\DEFAULT:爲每一個表建立單獨的Default約束定義腳本。注意這些是Default Constraints,而不是在建立表時的Default。


\FK:爲每一個表建立單獨的外鍵約束定義腳本。


\Index:爲每一個表建立一個索引定義腳本,並把對應索引定義腳本放進去.


\PK:爲每一個表建立單獨的主鍵定義腳本


 \TRG:爲每一個表建立單獨的觸發器定義腳本,並把應用到此表上的觸發器放進此腳本文件中。


\UDF:用戶自定義函數。


\View:視圖定義腳本。


\DCL: 數據控制語句腳本—主要包括控制全部的數據庫對象的Grant 和 Revoke語句。


\MISC:這兒用於存放各式各樣的其它腳本,例如alter table腳本或者一次更改的腳本,注意,不要放此目錄當作是一個包羅各類腳本的容器,只應包括須要放到VSS中,可是又不屬於上面所列目錄的腳本。


4.物理數據庫模型&字典


E-R 圖表和數據字典可讓任何使用數據庫的人都明確的知道如何從數據庫中得到數據。E-R圖對代表表之間關係頗有用,而數據字典則說明了每一個字段的用途以及任何可能存在的別名


4.1  物理數據模型


物理模型圖形化的展示數據庫的實現,它由邏輯數據模型及底層關係型數據庫管理系統來決定,由邏輯模型轉變爲物理模型包括如下的任務:


Ø       爲實體和屬性建立合理的數據庫名字


Ø       爲每一個屬性設計數據類型和是否容許爲空


Ø       定義主鍵,外鍵和索引


Ø       定義規則和默認值


Ø       爲優化性能,儘量使數據庫設計規範化,好比遵循1NF(第一範式),2NF(第二範式) 和3NF(第三範式)


爲加快數據庫設計速度,目前有不少數據庫輔助工具(CASE工具),如Rational公司的Rational Rose,CA公司的Erwin, Sybase公司的Power Designer以及Oracle公司的Oracle Designer等。


ERwin主要用來創建數據庫的概念模型和物理模型。它能用圖形化的方式,描述出實體、聯繫及實體的屬性。ERwin支持IDEF1X方法。經過使用ERwin建模工具自動生成、更改和分析IDEF1X模型,不只能獲得優秀的業務功能和數據需求模型,並且能夠實現從IDEF1X模型到數據庫物理設計的轉變。ERwin工具繪製的模型對應於邏輯模型和物理模型兩種。在邏輯模型中,IDEF1X工具箱能夠方便地用圖形化的方式構建和繪製實體聯繫及實體的屬性。在物理模型中,ERwin能夠定義對應的表、列,並可針對各類數據庫管理系統自動轉換爲適當的類型。


設計人員可根據須要選用相應的數據庫設計建模工具。例如需求分析完成以後,設計人員可使用Erwin畫ER圖,將ER圖轉換爲關係數據模型,生成數據庫結構;畫數據流圖,生成應用程序。


ERwin是如今被普遍使用的數據模型設計軟件,IDEF1X用來創建信息模型的標準。物理模型基於邏輯模型,ERwin同時支持邏輯模型和物理模型視圖的設計。同時,ERwin能夠產生全部的用來建立數據庫的DDL腳本.


4.2 IDEF1X 方法


下面的關係圖描述最經常使用的IDEF1X表達式協定:




 4.3物理數據庫數據字典


使用ERwin來對物理數據庫數據字典進行維護。在屬性定義編輯器中輸入屬性定義 。而後你就能夠從ERwin中生成報表,把它們放到word 文檔中,在屬性報表中,將包括屬性名字,數據類型,Null/Not Null和主/外鍵選項。把實體和屬性定義放到一個一個文檔中是很困難的,因此在不一樣的報表中建立它們.


4.4 性能設計


做爲一個常規的規則,在一個具備標識列(identity property)的列上定義一個主鍵。這使與之存在關係的表有一個窄的行寬,從而使同一數據頁中能被存儲更多的數據行,所以相同條件下將會有更少的描描操做。


Ø       定義彙集主鍵,這將會使二級索引佔用較少空間。


Ø       在外鍵上建立索引。


Ø       不要使用過多的索引,要確認你知道哪一個索引將被使用。


Ø       確認對應表存在統計信息。同時在數據庫中打開Auto Generate Stats 和 Auto create stats 選項。在一個只讀的數據庫中,咱們可能須要手動的建立統計信息.


Ø       在一個只讀的數據庫或者是不多被更改的數據庫中使用100%的索引填充率。


Ø       SELECT語句中,在非事務和特別的完整性要求的上下文中,要使用Table Hints――WITH NOLOCK


 


5.命名協定


5.1 數據庫命名原則及版本控制


5.1.1數據庫命名原則


數據庫命名要聽從如下命名原則:


表意性原則:數據庫命名本着表意性原則,即命名應儘可能反映存儲/action/view/column的數據內容。


長名原則:不多使用或者不使用縮寫,適用於DB命名以外的任一對象


數目最少化原則:數據庫對象應該儘可能知足數據最小化原則,也就是數據庫數目,存儲過程,視圖等數量最小化。


5.1.2 數據庫版本控制


當因爲多個版本的應用系統同時存在或者是其它特殊緣由而使用完成類似的功能的對象存在同時存在時,使用版本控制:版本號在對象名的後面加上[_v1]


如:


    Up_IM_DailyUsageStatsUpdate_v2


         Up_IM_CustomerGetUsingLastName_v2


 


5.2 Server/命名實例的命名


基於Domain命名, DOMAIN 的粒度,可分爲三級 ,分別是Server級別,Database級別和表的前綴級別。此爲第一級別。


 


1.              ONLINE PRODUCTION SERVER


NEWSQL


NEWSQL2


EHISSQL


          WAREHOUSE PRODUCTION SERVER


S3SQL01


S4SQL01


S7SQL01


S6SQL01


 


5.3  數據庫命名


數據庫命名應當聽從上面所說起到的協定,包括沒有特殊的字符,使用文字數據字式字符……..,項目代碼命名不該該一樣被數據庫使用.數據庫名字應該惟一以免衝突等.以Domain來標識, 拆分DATABASE,基於大的邏輯範疇,如operation 範疇以及部門類別,另外:其它與部門相關的DATABASE 能夠放到相關的SERVER 下。


Ø       數據庫數目最少化原則


Ø       數據設置儘量MERGER原則


Ø        peration database AND Dept level Database set up rules


Ø        拆分DATABASE,基於大的邏輯範疇,如operation 範疇以及部門類別


Ø       其它與部門相關的DATABASE 能夠放到相關的SERVER 下。


Ø       下面爲與OPEARATION 相關的DATABASE示例:


如: 下面爲與OPEARATION 相關的DATABASE:


 


                      ItemProfile


                      Inventory


                      Codecenter


                      CustomerProfile


                      SalesOrder


                      Accounting


                      Sevice(RAM)


                       eCommerce


                      SupplerChainManagement


                      DropShip                               
                      SerialNumber 
                      WorkFlow


                      ShippingCarrier


                      WarehouseManagment


                      HumanResource


Ø       其它與部門相關的DATABASE 能夠放到相關的SERVER 下。


                       MKT


                       Pm


                       Mis


                       CUSTOMER SVR


                       ADMIN


                       ACCOUNTING


 


 


2.       INTERNAL DEPT DATABASE SERVER


 


5.4數據庫對象—表,視圖,列名,約束,規則,默認值


數據庫對象應該被清晰的命名,要確認不存在歧義。數據庫對象的名字應該包括本身儘量多的信息,並要詳細的說明與之相對應的項目文檔.選擇合適的命名空間是一個很讓人困惑的問題(好比咱們不能從一個存儲過程的命名上看到是哪一個項目在使用這個對象),命名名字惟一的在數據庫級別標識本身,不管在哪兒使用它,請確保在一個項目中命名方法的一致.在一個小的上下文看來很清晰的對象在一個大的上下文中可能就會丟失他的含義.節省擊鍵次數永遠不能用來做選擇名字的標準.


下面供述了數據庫對象的命名協定,全部的數據庫對象命名都應該遵循下面協定.


Ø       若是不是長度限制,縮寫應該被避免.當使用縮寫時應該聽從下面的縮寫規範的規則.縮寫應該在項目範圍內保持一致並被存檔.


Ø       不要使用特殊的字符;對文字數字式字符加以限制.


Ø       在命名時爲了更好的標識表與應用程序之間的關係,咱們要使用「[function name]|[main module name]_表名」的形式.


好比: Newegg_customer


         Blocked_customer


         Abs_customer


EDI_....


MDF_....


 


Ø       當要修改一個對象的架構時,首先要確認與之存在依存關係的對象不受影響.要查看與之存在依存關係的對象,能夠在企業管理器中」右鍵相應對象」->」ALL TASKS」->」DISPLAY DEPENDENCIES」來查看或者使用系統存儲過程SP_DEPENDS來查看:


EXEC SP_DEPENDS 'dbo.GetCategories’        


Ø       當底層表結構更改時,視圖都應該被從新建立.這個應該特別被注意,特別是視圖是使用如」SELECT * FROM table」時.


Ø       強烈建議任什麼時候候都不要使用相似於」SELECT * FROM」的語句.


Ø       當在不存在事務上下文中或者是完整性要求不是很高時,在SELECT語句中使用WITH NOLOCK.


 


5.5 縮寫規範


因爲長度的限制而須要使用縮寫時,應當聽從如下的規則:


Ø        以分類單詞做爲名字的起始.


Ø       縮寫的第一個字符應該與單詞的第一個字母相同,好比,SFER就不是Transfer的有效縮寫.


Ø       Identify and abbreviate the root word, usually the shortest form of the word.


Ø       縮寫時省略元音字符,除非特殊緣由,重複字符中將被省略一個。


Ø       縮寫不該該暗示到其它的單詞(好比:單詞「parent」可能被縮寫爲」PRNT」,可是「PRNT」很容易讓人想起單詞「PRINT」,一個比較好的縮寫就是使用」PSRNT」)


Ø       類似單詞的縮寫應該一致,好比若是Charge縮寫爲CHRG,則change應該被縮寫爲CHNG.


Ø       當組合詞或者短語被普遍使用時,而且大多人都能識別出這些縮寫時,應該使用他們的首字母縮寫,由於大多人都能識別出這些縮寫。


5.6 列名


列名應該基於邏輯設計中的屬性名,


修飾符:          可選,它以角色名而被你們所熟知(PrimaryPartID, AlternatePartID)


主詞(primary word):    除非是主鍵,不然不須要。由於表名已經限定列名,因此沒必要要冗餘的加入此信息。


限定詞:           可選;附加的描述信息,用來幫助咱們更明確的瞭解數據的意義(InventoryOnHandQuantity, CustomerLastName)


分類詞:         須要;用來標識數據的類型,通常列名的最後一個單詞(PartID, CountryCode, CustomerName, InvoiceAmount).  下面是一些標準的分類詞:


CLASSIFIER                       DESCRIPTION


Address                             Street or mailing address data


Age                                   Chronological age in years


Average                             A numerical average (self-explanatory)


Amount                             Currency amount -- always is money


Code                                 Code from a specific domain of coded values


Count                                Count of something -- years, payments, etc.


Date                                  Calendar date


Datetime                            Date including time


Day                                   Day of month (1 - 31)


Description                       Brief narrative description (usually of a code)


Duration                            Span of time, whether days, months, years, etc.


Identifier                            Unique identifier for something


Image                                A graphic image, such as a bitmap


Indicator                            A boolean indicator


Measurement                      Measurement of physical dimensions


Month                                Month of year


Name                                 Formal name of something


Number                              Number (self-explanatory)


Percent                              Number expressed as a percent


Quantity                             a number of things


Rate                                  Number expressed as a rate


Ratio                                 A proportion, or expression of relationship in quantity, size, amount, etc. between two things


Text                                   Freeform textual information


Time                                  Time of day


Weight                              Weight measurement


Year                                  Calendar year or julian year number


 


下面的ERwin diagram 示例推薦的命名協定:


 


5.7 存儲過程命名


存儲過程命名使用上面描述的表,視圖和列的命名同樣的規則,包括不要使用特殊的字符,大寫首詞,不要使用組名(好比,myProc1),不要使用系統保留的「sp_」前綴(好比可使用up_),使用文字數據式字符,爲了使存儲過程名字更有意義,名字中應包括「對象」+「動做」的方式 。


 存儲過程命名示例:


Up_IM_CustomerGet(IM 標識itemmaintain系統使用)


Up_IM_LoginValidate


Up_IM_RegionDataRotate


Up_IM_DailyUsageStatsUpdate


Up_IM_CustomerGetUsingLastName


 


下面是可選的命名規則,使用「動做」+「對象」的方式:


            Up_IM_BuildSalesPerspectives


            Up_IM_InsertSellInFileAudit


 


5.8遊標命名


遊標應該如下面的標準來命名:


表名或者對象名字{使用此遊標的對象名字}+Cursor


例如:


            FiscalMonthCURSOR


            EmployeeListCURSOR


 


5.9 觸發器命名


觸發器命名應該聽從如下的協定:


       表名+ { Ins | Upd | Del}


Ins標識此觸發器爲insert觸發器,UPD標識此觸發器爲update觸發器,同理DEL被用來標識delete觸發器:


觸發器示例:


CustomerIns


ProductCodeDel


BusinessUnitUpd


 


5.10 索引命名


 索引的命名應該在表空間內惟一,當查看執行計劃時能夠有效的對索引進行識別..


       [IX][類型(U標識Unique,C標識Clustered)][列名(s)]


Ø       當對單列進行索引時,你可能須要使用列的全名.


Ø       當對多列進行索引時,要使用你所能想到的最優的縮寫.


Ø       當對一個表的全部列進行索引時,使用ALL單詞.


Ø       在多列名中使用下劃線以增長可讀性.


Ø       不要爲索引加上序列號


例如:


IXUC_SalesId   (clustered unique)


IXU_SalesDate (unique)


IX_SalesId_MonId_QtrId_WkId (composite index)


IX_MonId_YrId (composite index)


IXC_SalesId   (clustered not unique)


 


5.11  主鍵和外鍵命名


主鍵:對主鍵進行命名應該聽從如下標準:


      PK_表名


例如,主鍵名


       PK_Customer


外鍵:對外鍵進行命名時應該聽從如下標準:


        ChildTableName_FK_ParentTableName


外鍵名字示例:


Customer_FK_Country


Sales_FK_Customer


 


5.12 Check約束命名


Check約束的命名聽從如下的約定:


       表名CK{數字}


數字被用來區別同一表中的其它Check約束.


Check約束命名示例:


      CustomerCK1


 


5.13 源文件命名


文件命名應該使用和數據庫對象命名相同的規則-包括沒有特殊字符,限制名字以免歧義,大寫每一個單詞的首字符,全部的包括SQL腳本的文件應該以.SQL爲結束字符,而不是.SP,.TRG等。可執行的批處理文件應該是以.CMD爲擴展名.


5.14 Job的命名


Job命名使用如下方式,其中在」 Job名」中要標識出此JOB的功能:


Job_[應用系統名_][數據庫名_]Job名/ [storce procedure名]


For Example:


Job_IM_IMDB_backup


5.15 用戶自定義函數命名


Ø       知足NESE命名的基本原則


Ø       用戶定義的函數使用fn來標識


Ø       名字中應包括「對象」+「動做」的方式


Ø       For example:


        Un_IM_CustomerGet(IM 標識itemmaintain系統使用)


 


5.16 用戶自定義數據類型命名


Ø       知足NESE命名的基本原則


Ø       用戶定義的函數使用Udt來標識


Ø       用戶定義的數據類型


如:


        UdtPhone


        UdtBirthday


        UdtAge


5.17 複製命名


在知足NESE命名的基本原則利用Rp來標識複製


       Rp_[DBname]複製名{_[Trn]|[Mrg]|[Snp]}


      [Trn]:標識爲Transactional Publication


      [Mrg]:Merge Publication


      [Snp]:Snapshot publication


如:


     Rp_Foodmart_Trn


 


 


 


 


6.  SQL Server代碼規範


6.1   對象建立腳本


使用數據庫(USE)


不要在SQL Server源文件使用USE 數據庫名語句,這會致使只能在此數據庫建立對象,OSQL命令行工具能夠在數據庫中重定向.


刪除對象:


IF Exists語句應該在Drop 對象語句以前執行:


IF object_id(‘tablename’) IS NOT NULL


   DROP TABLE TableName


 


表建立,索引,觸發器


在開發過程當中,全部的表建立腳本均可以合併爲一個項目中的文檔中(使用ERwin將會很是將會很是容易).此文檔中每一個表建立語句的後面應該有Go語句,當系統到達一個穩定點時,應該把文檔中的從多個表按照表的不一樣分到單獨的文件以利於維護.這此文檔應該放到\TBL子目錄下.


在每一個建立單獨表的文檔中,都應該在建立表語句的前面使用if exists() drop table 語句.


每表建立單獨的索引腳本文件,並把它們放到\TBL的子目錄\INDEX下,命名爲<表名>.SQL.在每一個建立索引的語句以前,應該使用」if exists() drop…」語句,這樣當此索引 已經存在時,它將會被先刪除而後從新建立.


若是必要,建立單獨的觸發器腳本,並把它放置在\TBL的子目錄 \TRG下.也命名爲<表名>.SQL.


主鍵,外鍵,Check約束和默認值


全部的約束和默認值定義都應該直接經過ALTER TABLE命令來建立,在建立表的同時對約束和默認值進行嵌入式的定義應該被禁止,爲了清晰起見.一個ALTER TABLE語句不該該包括多個對錶的更改,咱們推薦在每一個更改語句以前使用「if exists() alter …. Drop onstraint…」,這樣當此約束已經存在時,能夠被從新建立。


主鍵定義有單獨的腳本,並放置在\TBL的子目錄\PK下。


外鍵定義也應該有獨立的腳本,並放置與\TBL的子目錄\FK下。


每一個表的Check約束語句也應該放在一個單獨的目錄\TBL的子目錄\CHECK之下。


表中列的默認值定義應該放在一個獨立的腳本,並放置與TBL的子目錄\DEFAULT之下。


在CREATE TABLE 中顯式的爲表的每一列定義是否爲空,不要依靠數據庫的默認設定,由於在數據庫中這可能不一樣,從而出現了無心識的錯誤。


爲索引和主鍵指定CLUSTERED或者NONCLUSTERED,不要依靠數據庫的默認設定。


Grant 語句


.Grant語句應該獨立與表,存儲過程等的建立語句,爲Grant語句建立單獨的文件可使咱們很容易的從新對數據庫對象進行受權。


 


6.2   數據庫更改腳本


當在產品服務器上對數據庫進行更改時,要很是當心和注意.這會避免產品服務庫發生丟失數據或者中止服務等災難的發生.


這個腳本應該是能夠對數據庫的對象進行判斷的,好比,若是一個對象已經存在,那應該成功結束,而不是把此對象刪除,由於此對象可能已經包括數據.好比表,若是要刪除一個已經存在的表,則也刪除了表中的數據.


當表或者視圖從新被建立時,也要從新建立受權.


任什麼時候候,當表被建立或者被從新建立時,確認全部的依靠關係,好比PK,FK,索引,觸發器,默認值和視圖都應該對從新建立.


當底層表結構更改時,視圖都應該被從新建立.這個應該特別被注意,特別是視圖是使用如」Select * from table」時.


若是一個表被複制,在SQL Server 2000使用sp_repladdcoumn和sp_ repldropcolumn存儲過程.


腳本包括錯誤檢測及事務的回滾.當把一個表分紅兩個表或者是多個複雜的輪換時,這個就很重要.


 在腳本中應該包括Raiserror腳本,這樣會使之後的打錯工做較爲容易.理想狀況下,應該首先使用Raiserror(‘msg’,0.1) WITH NOWAIT,由於這樣當它被執行時會被送到客戶端。


DECLARE @rows int, @rc int


SET NOCOUNT ON


IF NOT EXISTS( SELECT * FROM INFORMATION_SCHEMA.COLUMNS


                        WHERE COLUMN_NAME = 'AgreementDurationID' AND


                                       TABLE_NAME = 'BudgetSellIn')


BEGIN


   IF EXISTS (SELECT * FROM sysobjects


                    WHERE name ='BudgetSellIn' AND xtype = 'u' AND


                    replinfo in (1,3))


   BEGIN


      EXEC @rc = sp_repladdcolumn 


                              @source_object = 'BudgetSellIn'


                             ,@column = 'AgreementDurationID'


                          ,@typetext = 'int NOT NULL CONSTRAINT DF_BudgetSellIn_AgreementDuID DEFAULT 0'


      IF @rc <> 0


      BEGIN


            RAISERROR('Failed adding column AgreementDurationID to BudgetSellIn....', 0,1) WITH NOWAIT


            GOTO CRASH


      END


      -- Drop the default constraint


      ALTER TABLE BudgetSellIn


      DROP CONSTRAINT DF_BudgetSellIn_AgreementDuID


      IF @@ERROR <> 0


      BEGIN


           RAISERROR('Failed droping DEFAULT CONSTRAINT DF_BudgetSellIn_AgreementDuID from BudgetSellIn....', 0,1) WITH NOWAIT


           GOTO CRASH


      END


 


      RAISERROR('Successfully added column AgreementDurationID to BudgetSellIn..',0,1) WITH NOWAIT 


   END


END


ELSE


RAISERROR('New Column AgreementDurationID already exists in BudgetSellIn..',0,1) WITH NOWAIT  


IF NOT EXISTS(SELECT * FROM sysobjects


           WHERE name = 'BudgetSellIn_FK_AgreementDuration' and type = 'f')          


BEGIN


   ALTER TABLE BudgetSellIn


   ADD CONSTRAINT BudgetSellIn_FK_AgreementDuration FOREIGN KEY


   (AgreementDurationID) REFERENCES AgreementDuration(AgreementDurationID)


  


   IF @@ERROR <>0


   BEGIN


        RAISERROR('Failed Adding FOREIGN KEY To AgreementDuration....', 0,1) WITH NOWAIT


        GOTO CRASH


   END


   RAISERROR('Successfully added FOREIGN KEY To AgreementDuration....', 0,1) WITH NOWAIT  


ELSE


     RAISERROR('FOREIGN KEY to AgreementDuration Already exists on Table BudgetSellIn...',0,1) WITH NOWAIT


END


GOTO VERYEND


CRASH:


RAISERROR ('Error Adding New Columns to BudgetSellIn...', 18,127)


VERYEND:


 


6.3綜合開發實踐


Ø       不要在存儲過程當中使用輸入參數來更改它們的功能。這樣會使程序中的耦合度大大增長。例如,一個存儲過程根據輸入參數向Customer表中插入記錄或者Employee表中插入記錄,這會使此存儲過程複雜化和鬆耦合度,此時使用兩個獨立的存儲過程(一個存儲過程實現向Customer表插入記錄和一個實現向Employee表插入記錄)將會更好.


Ø       不要使用關鍵字做爲對象的標識.         


Ø       使用ANSI風格的鏈接。不要使用老的T-SQL 外鏈接「*=」,這可能在未來的版本中不被支持.


Ø       不要在存儲過程當中使用雙引號,這是由於ANSI標準中默認就是QUOTED_IDENTIFIERS是設置爲ON的,因此SQL Server將視任何雙引號以內的字符爲標識符.


Ø       當調用存在參數的存儲過程時,一個比較的好建議就是使用參數名和參數值來傳遞,而不是依次使用參數值來調用。


Ø       爲了清晰起見,it’s often a good practice to specifically include the correct parameters.  For example:


Ø       當建立索引時,無論是不是cluster,都要明確的使用它


Ø       當建立在一個具備Identity屬性列的表時,應該包括起始數字和增長數字,也要包括是否爲空。如:


            Create table Table1 (ColA int identity(1,1), colB varchar not null).


 


Ø       咱們都知道應該滿是避免硬編碼,可是必要時,增長註釋來講明硬編碼的意義,例如:


 「where SalesChannelLevelID = 1 – SellInsales」


Ø       當使用RPC調用一個存儲過程時,在服務器名字上加上中括號,好比,一個服務器的名字可能叫「Fridge\Inst1」,若是不加中括號,將會出錯.


INSERT INTO #CMODSSourcePROHistory


EXECUTE('['+@ServerName +'].CMODS.dbo.RSPGetSourcePROHistory')


 


6.4  文本文件格式


保持單行字符少於87個


每行SQL語句的寬度應該少於87個字符,若是太寬,咱們在查看和打印時易讀性會不好。


縮進間距


在T-SQL文件中,使用3個字符的縮進,咱們能夠在查詢分析器中配置Tab鍵爲3個空格的縮進(默認爲8),以下示例所示:


IF EXISTS (SELECT ...


           FROM Table ...)


BEGIN


      statement 1


      statement 2


      IF (...)


      BEGIN


            statement 1


      END


END –- end of if table rows exist


 


SQL代碼格式化


保證代碼在項目範圍內的一致性。爲全部的代碼段使用相同的格式和縮進方式.


大寫全部的關鍵字,而且爲SELECT,FROM,WHERE,GROUP BY,HAVING語句另起一行.爲WHERE中的每一列另起一行,右對齊這些關鍵字。當WHERE語句包括子SELECT或者OR語句時,縮進它們,以使得比較容易的分清他們以前的依從關係(好比哪些語句是在一塊兒的)


在複雜的鏈接中,縮進JOIN  和ON關鍵字,這會使SELECT,FROM和WHERE更突出,


排列CASE和相應的END語句。


咱們大多喜歡在列表的左邊使用逗號,由於這樣在未來增長新的列時不用在最後的列後面加上逗號.一樣這樣也很是容易檢查到是否有逗號沒有寫.


下面的例子顯示一個複雜的WHERE  T-SQL語句語句:


SELECT


       Col1


     ,Col2


     ,Col3


     ,CASE WHEN t1.Col1 = 1 THEN ‘First’


               WHEN t1.Col1 = 2 THEN ‘Second’


               ELSE ‘Last’


     END AS Col4Name


FROM  Table1 t1


       JOIN  Table2 t2


         ON  t1.Col1 = t2.Col2


        AND  t2.Date = ‘1/1/1994’


 WHERE  (t2.Col4 = 3


          OR  (    t2.Col3 < 6


               AND t2.Col4 > 5


              )


        )


        AND NOT EXISTS


              (SELECT *


                 FROM  Table5 t5


                WHERE  t5.Col2 = t1.Col2)


GROUP BY...


HAVING ...


 


在IF和WHILE時,爲BEGIN和END另起一行.


當在BEGIN和END中有不少行的T-SQL語句時,對END進行註釋以使它們可以很好的匹配起來.


IF EXISTS (SELECT *


           FROM #tmptable)


BEGIN


      statement1


    statement2


END


ELSE


BEGIN


      statement1


      statement2


END


 


WHILE (sky = ‘blue’)


BEGIN


      statement1


      statement2


END –- end of while sky = ‘blue’


 


當進行插入操做時,應該給出列名


歷來不該該依靠列的物理順序來進行表的插入操做。應該在插入操做進行指定插入列的順序,這將會使咱們的維護和排錯時更加容易.


INSERT Table1 (


      Column1


     ,Column2


     ,Column3


     ....)


SELECT


     Column1


     ,Column2


     ,Column3


     ...


FROM Table2


WHERE ....


OR


INSERT Table1


     (


      Column1


     ,Column2


     ,Column3


     ....)


VALUES (


      value1


     ,value2


     ,value3


      ....)


 


在SELECT列的列表中要使用表的別名


在Insert/Select 語句中,updates或者deletes操做可能要鏈接不少表,無論是否須要,咱們推薦老是爲每列加上表的別名。這將會防止當在鏈接的表中加入新列是出現錯誤「ambiguous column name」發生,同時這也能幫助維護人員列是來源於哪一個表,試着使用表的別名來標識表是很是有用的,可是記住不要超過3


或者4個字符。


在Insert/Select語句,


INSERT Table1 (


    CountryCode


     ,SubsidiaryCode


     ....)


SELECT


     Ctry.CountryCode


     ,Sub.SubsidiaryCode


     ...


FROM Country ctry


JOIN Subsidiary sub


    ON ctry.SubsidiaryCode = sub.SubsidiaryCode


 


------------------------------------


-- For update statements, do not use a column alias


-- for the target column on the set statement.


-- To avoid ambiguouity, the table alias is listed after the UPDATE.


------------------------------------


UPDATE ctry


SET Countryname = ictry.CountryName


      ,SubsidiaryCode = ictry.SubsidiaryCode


  FROM Import..Country ictry


  JOIN Country ctry


    ON ictry.CountryCode = ctry.CountryCode


 


 


標題(header)


全部的文件都應該有一個標題,把文件頭把在Create…AS語句以後,經過這種方法註釋,其中也將包括更改歷史,都將會存儲在sysComments中,在用來和最近的Visual Sourcesafe中的版本進行比較時將會很是有用.


/******************************************************************


*name        :             --函數名/View/Store proc


*function    :              --函數功能


*input       :               --輸入參數


*output      :               --輸出參數


*author      :               --做者


*CreateDate  :             --建立時間


*UpdateDate  :           --函數更改信息(包括做者、時間、更改內容等)


*************************************************************************/


如:


IF EXISTS ( SELECT * FROM sysobjects


                  WHERE name = ' AddPublications ' AND type = 'P' )


BEGIN


      PRINT 'Dropping Procedure: AddPublications '


      DROP PROCEDURE AddPublications


END


 


PRINT 'Creating Procedure: AddPublications '


GO


CREATE PROCEDURE AddPublications


AS


/******************************************************************


** Desc:  The purpose of this procedure is to create the Publications


** for replication, as defined in the Publication table. 


** 


** Processing Steps:


**        1.  If database is not already published, publish it


**        2.  For each Publication in Publication table:


**              IF Publication doesn't exist


**                 Create Publication


**              IF SnapShot Job for Publication doesn't exist


**                  Create SnapShot Job


**        3.  Change LogReader Agent from default settings


**        4.  Change Checkup Agent Heartbeat from default settings


**


** Parameters: none


** Restart:


**        Restart at the beginning.  No code modifications required.


**


** Tables Used: 


**               Publication                                    select


**               master..sysdatabases                        select


**               Distribution..MSPublications                 select


**               Distribution..MSSnapShot_Agents         select


**               Distribution..MSLogReader_Agents            select


**               msdb..sysjobs                                         select


**               msdb..sysjobsteps                                   select


**              


** Return values:   = 0 Success


**                         < 0 Failure


**


** Called By:       None


**


** Calls:           sp_replicationdboption


**                   sp_addpublication


**                   sp_addpublication_snapshot


**                   sp_update_job


**                  sp_update_jobstep


**                  sp_update_jobschedule


**


** Author:          Michael Eldridge


** Date:             02/05/1998


********************************************************************************


** Change History


********************************************************************************


** Date        Author     Description


** ----------  ---------  -----------------------------------------------------


** 10/03/1998  jsuther    Sphinx agents now have step named 'Run Agent.' not


**                                  'Run Replication Agent.'


** 08/18/2000  brianell   Standardized


******************************************************************************/


SET NOCOUNT ON


SET ARITHABORT ON


 


註釋


在註釋中的「Change History」部分中包括更改歷史.


在一個包括多行的註釋中,雙中劃線應該被放在每行的開始,不要使用/* */風格的註釋,由於註釋中可能被嵌套.


不容許註釋超過87個字符的寬度.


在註釋中包括下面的信息將會頗有幫助:誰申請了特定的更改,更改的時間,爲了達到的商業目的的描述.


 


Comment complicated sections of code just above the section of code at the same indent level.


-----------------------------------------------------------------


-- Coment at level one


-----------------------------------------------------------------


WHILE (...)


BEGIN


      statement 1


      statement 2


   -------------------------------------------------------------


   -- Comment at level 2


   -------------------------------------------------------------


      statement 3


END


 


6.5  使用Return


使用Reture來標識存儲過程,批處理的成功或者是失敗.


>= 0 標識成功


<-99 標識失敗 (SQL Server reserves -1 through -99).


當存儲過程當中包括不少語句時,Return一個明確的數字對於錯誤語句的定位來講是很是重要的.


IF @@error <> 0


BEGIN


     RAISERROR (‘ValidateEndDate 50001:  Invalid End Date’),18,127


     RETURN -200


END


 


輸出參數能夠被用來返回值.


EXEC @rc = ValidateReportEndDate @SourceID, @FiscalPeriodID output


 


因此有存儲過程應該用Return來標識是否有錯誤發生.即便發生錯誤的可能性不在在(好比一個查詢中只包括SELECT語句),也應該使用Return 0來標識成功執行.有效的使用Return將能很好的保證數據的一致性.


Select 語句能夠返回一個或者多個行集.不要使用」SELECT *  FROM Customer」,由於若是在Customer表中新增了一列,應用程序有可能沒有對此列進行處理的操做,從而有可能產生錯誤,而且「SELECT *  FROM Customer」對於數據庫性能來講有不少負面影響。


6.6     錯誤處理


咱們應該在咱們的系統中包括錯誤處理操做。咱們推薦使用@@ERROR對全部可能會出現問題的語句後進行判斷.


根據場景的不一樣,咱們能夠從兩種方法中選擇一種,那就是RAISEERROR和RETURN(只在存儲過程當中可用).


不要在RAISERROR中使用WITH_LOG,由於這要求SA的權限.


不要使用Sysmessages來爲RAISERROR存儲錯誤信息,由於在多數據庫服務器的環境下會增長維護成本.


當調用一個存儲過程時,必定要對錯誤進行檢查,也就是對Return的信息進行檢查.若是存儲過程返回一個值,此時它將會重值@@Error爲0.


EXEC @rc = ValidateReportEndDate @SourceID, @FiscalPeriodID output


IF @rc <> 0 or @@error <> 0


    RAISERROR (‘LoadSalesFile 50001:  Error calling ValidateReportEndDate Date’),18,127


    RETURN -200


END


 


6.6.1     在存儲過程和觸發器中使用RAISEERROR


在存儲過程和觸發器中, RAISERROR 應該按如下格式返回錯誤信息:


 {name of,proc or trigger generating error}, {error number} : {error message string}


示例:


CustomerListGet 50001: Unable to retrieve.  Invalid channel id.  (proc example)


CustomerINS 50002:  User is not a manager.  Insert denied.    (trigger example)


 


這是一個簡單的例子:


IF @@ERROR <> 0


BEGIN


      SELECT @errmsg='50000: Cannot Declare SnapShot_Cursor'


      GOTO ErrorHandler


END  


ErrorHandler:


SELECT @errmsg = @procname+'  '+@errmsg


RAISERROR (@errmsg,18,127)


6.7     Print 語句


在存儲過程和SQL腳本中咱們使用print 來把某些重要點的信息打印出來,從而幫助咱們排錯或者是查看實現的性能.在SQL語句完成後打印出對象名字,影響的行數和用時將會頗有用.


語句RAISEERROR(‘’,0,1) WITH NOWAIT語句在PRINT 或者SELECT中是首選的,由於它將將消息當即發送給客戶端。Print 或者SELECT將會在批處理結束時才送到客戶端(或者是緩衝區滿).


這就是個例子:


 


   SELECT @Msg = 'Rows inserted from  ' +@SalesTbl +':  ('+


                 + convert(varchar,@rows)


                 +')   at  '+convert(varchar,getdate(),120)


   RAISERROR (@Msg, 0,1) WITH NOWAIT


 


Or


   SELECT @Time = convert(varchar,getdate(),120)


   RAISERROR( '%d rows have been inserted to %s table at %s' ,0,1


                    ,@rows, @obj,@Time) WITH NOWAIT


Output:


34567 rows have been inserted to Sales133 table at 2001-02-21 14:47:26


 


6.8     參照完整性


使用ALTER TABLE ADD CONSTRAINT 命令來強制參照完整性.  除非要調整數據庫間的完整性,不然不要使用觸發器來強制參照完整性。


6.8.1     主鍵


每一個表都應該有主健存在。


當須要使用代理關鍵字來標識行的時候,使用identity列.  此時應該在此列上使用primary約束


從SQLServer 7.0開始,咱們通常推薦在主鍵上建立clustered 索引來最小化存儲空間和達到性能要求.


6.8.2     外鍵


在外鍵的定義中,FK列應該和被參照的PK(Unique)列有相同的數據類型.注意, 在一列上建立FK並不會自動在此列上建立索引.因此咱們推薦在FK上手動建立索引以提高性能.


6.9     觸發器


觸發器能夠被用來強制商業規則,在數據庫間強制數據完整性.


 


6.10     遊標


除非有特殊的緣由,定義遊標時要定義局部的,只讀向前的和靜態的.


 


DECLARE ErrorStatsCursor CURSOR LOCAL FORWARD_ONLY STATIC FOR


--    DECLARE ErrorStatsCursor INSENSITIVE CURSOR FOR -- This is the global cursor ANSI92


SELECT   PumpMilestoneID


FROM     PumpMilestone


ORDER BY PumpMilestoneID


 


OPEN ErrorStatsCursor


FETCH NEXT FROM ErrorStatsCursor


INTO @MilestoneID


 


WHILE @@FETCH_STATUS = 0


BEGIN


   -- First, get the average time for the Milestone


 


   ***


  


   SELECT @MsgID = @@Error


   IF @MsgID <> 0


   GOTO ErrorHandler


  


   FETCH NEXT FROM ErrorStatsCursor


   INTO @MilestoneID


END


 


CLOSE ErrorStatsCursor


DEALLOCATE ErrorStatsCursor


 


RETURN 0


 


ErrorHandler:


IF CURSOR_STATUS('local', 'ErrorStatsCursor') >= 0 -- Cursor Open


   CLOSE ErrorStatsCursor


IF CURSOR_STATUS('local', 'ErrorStatsCursor') = -1 -- Cursor Closed


   DEALLOCATE ErrorStatsCursor


 


/*    IF CURSOR_STATUS('global', 'ErrorStatsCursor') >= 0 -- Open and Allocated so close it


  CLOSE ErrorStatsCursor


IF CURSOR_STATUS('global', 'ErrorStatsCursor') = -1 -- Cursor Closed


  DEALLOCATE ErrorStatsCursor


*/


EXEC util_ErrorLog @ProcName, @MsgText, @MsgID


RAISERROR (@MsgText, 18, 127)


RETURN -200


6.11     用戶自定義函數


調用時必定要使用全部者後加用戶自定義函數名的形勢,例如:


SELECT dbo.fnColumnList('sysobjects', 1)


應該避免:


Ø       不可以使用臨時表,可是可使用表變量


Ø       在用戶自定義函數中不可以使用debugging,print和RAISEERROR


Ø       不可以輸出多個值,除非輸出表


Ø       在用戶自定義函數中不可以使用可選參數,好比你在一個函數中定義了三個參數,併爲它們賦值,之後若是你調用此函數時必定要爲這三個參數分別賦值.


 


6.12     局部變量命名標準


這是局部變量的命名標準:


 


@rows


int


標識此語句影響的行數:


SELECT @rows = @@rowcount


@err


int


本地變量存儲全局變量@@error的值:


SELECT @err = @@error


@errmsg


varchar(200)


這個用來存儲錯誤信息. 在 RAISERROR有他的編碼示例.


@msg


varchar(200)


被打印出來的message字符串。


 


6.13     返回最後插入的標識值


在@@IDENTITY以外,SQL Server 2000介紹了另外兩種方法來對Identiey進行檢查.我認爲 Scope_identity()比@@IDENTITY要好用一些.由於Scope_IDENTITY()返回插入到同一做用域中的 IDENTITY 列內的最後一個 IDENTITY 值.


           @@IDENTITY: 返回最後插入的標識值。


           Scope_identity():返回插入到同一做用域中的 IDENTITY 列內的最後一個 IDENTITY 值。一個做用域就是一個模塊——存儲過程、觸發器、函數或批處理。所以,若是兩個語句處於同一個存儲過程、函數或批處理中,則它們位於相同的做用域中。


 


以下:


drop table jbt1


drop table jbt1hist


 


create table jbt1 (a int identity(1,1), b int not null)


create table jbt1hist (a int, b int not null, c int identity(222,1))


 


drop trigger jbt1ins


create trigger jbt1ins on jbt1 for insert as


insert into jbt1hist (a,b) select a,b from jbt1


 


insert into jbt1 (b) select 1


select @@identity -- gives identity of the table in the trigger (jbt1hist).


Select SCOPE_IDENTITY() -- gives identity of table we just inserted into (jbt1)


 


另外一個就是IDENT_CURRENT, 返回爲任何會話和任何做用域中的指定表最後生成的標識值。


SELECT IDENT_CURRENT('extractlist')


7.     安全


數據是公司的重要資產. 在進行數據庫或者應用程序設計的時候就應該對安全性進行設計. 數據倉庫的安全性和有效的安全控制不僅是開發後期所考慮的事情.在設計時,安全就要有高的優先級,儘管有時安全控制將會使設計很是複雜.


爲了確保最大的安全性,應該應用下面的標準:


在混合驗證或者是Windows身份驗證中,應用程序不該該以SA來登陸SQLServer,此外,全部的用戶名和密碼都不可以在應用程序中硬編碼.


•  只給用戶分配他應該獲得的權限。


在混合或者是windows身體驗證中爲全部的用戶使用一個共用的賬號是不理的,每一個用戶必定要有它本身的登陸.


爲了防止打破全部者鏈,全部的數據庫對象的全部者都應該是DBO


8.     事務處理


8.1     編寫高效的事務處理代碼


Ø         批處理順序的批理批中的每個事務。在線處理系統(OLTP)一次處理一個事務。使用這兩種方式都要有一個有一個行之有效的方法來對handles recovery


Ø         避免使用沒必要要的大事務。


Ø         在用戶自定義事務中歷來不要使用DDL語句(好比CREATE TABLE或者SELECT INTO),由於它將會在事務的執行過程當中鎖定Sysobjects 和sysindexes。一個例外就是數據庫更改語句。


Ø         最小化事務執行的時間


Ø         在事務中以相同的順序使用數據庫對象來最小化死鎖。


Ø         老是對錯誤進行檢查,若是有錯誤發生,回滾事務


 


例如:


IF @@TRANCOUNT = 0 BEGIN TRAN


IF @@TRANCOUNT > 1 ROLLBACK TRAN


8.2     批處理


在批處理文件中使用OSQL來調用存儲過程和其它的SQL腳本.


若是服務器使用NT集成的驗證,使用/E 選項來打開信任鏈接.而不能使用/U /P.


 


下面是一個在NT環境下使用批處理的例子,若是有任何錯誤發生,它將會重試2次.


 


set sp=LevelSalesTransactions                                           >>%log%


set qry=osql /S%facsvr% /dstaging /E /b /n /I /w250 /H%_Job_Name%  /Q "exec %sp% '%1', %Semaphore% " >> %log%


(%qry% || (sleep 60 & %qry% || (sleep 60 & %qry%)))                            >>%Log%


if errorlevel 1 set errmsg=***%_job_name% failed running %sp% & goto problem


 


:Finish


call FINISHED.cmd


goto done


 


:problem


call errhandl.cmd


 


:done


 


附錄A:最多見的不該該出現的問題


 


數據庫設計和開發標準描述咱們在數據庫系統的開發過程當中應該怎麼作,爲了更好的強調這一點,下面的列表描述了哪些不該該作.


1.        建立沒有主健的表


2.        建立沒有clustered索引的表


3.        不使用事務來強制數據的一致性和完整性.


4.        使用了事務,可是即不檢查是是否有錯誤發生,也不對有問題的事務進行回滾.或者是沒有對有錯誤發生的事務進行後續的錯誤處理.


5.        在數據庫中不強制父子(或者主外鍵)關係,忽略了參照完整性.


6.        沒有對安裝,備份,恢復和維護等的具體計劃.


7.        在數據庫系統開發後來實現安全性或者是根本就沒有對安全性進行設計.


8.        在數據庫模型設計完成以前進行開發,而後對數據庫構架進行分修正.


9.        在主外鍵關係的相應列中數據類型不匹配.


10.    在數據庫中有無關的冗餘數據.


11.    不在存儲過程當中加上合理的註釋。


12.    在寫過代碼以後再寫設計文檔或者根本就不寫設計文檔.


13.    不對設計和編碼進行檢查.

命名規範:編程

                   

 
相關文章
相關標籤/搜索