1. 程式人生 > >SQL Server中有關約束(constraint)的一些細節

SQL Server中有關約束(constraint)的一些細節

並發 自動生成 fault 自動 方式 view str 自己 數據庫

原文:SQL Server中有關約束(constraint)的一些細節

本文出處:http://www.cnblogs.com/wy123/p/7350265.html
(保留出處並非什麽原創作品權利,本人拙作還遠遠達不到,僅僅是為了鏈接到原文,因為後續對可能存在的一些錯誤進行修正或補充,無他)


SQL Server 數據庫中的約束(Constrint)是作用是為了保證數據庫的完整性和一致性,可以建表的時候指定某個字段要符合某種約束(或者對已有表的字段添加約束),比如唯一性(或者主鍵)約束,非空約束,默認值約束等
對於具體的約束,可以分為主鍵(唯一鍵)約束,默認值約束,檢查約束,外鍵約束等幾類。

約束的創建方式

  1,建表的時候指定

    如下,可以在建表的時候指定某些字段滿足某種約束。

技術分享圖片

  2,以創建約束的方式指定

    也即在建表的時候沒有指定任何約束,在建表之後,以alter table的方式指定某些字段上的約束

    技術分享圖片

    

    對於Check或者Default約束,也可以事先定義出來規則(Rule),然後將規則綁定到對應表的字段
    如下分別定義了一個check約束和默認值約束,然後將表的字段綁定到對應的約束,這樣做的好處是對於某些復雜的約束,定義好約束之後就可以重用了
    需要註意的是,對於Rule來說,NULL值是不與任何規則沖突的,
    舉個例子,如下,雖然把TestConstrint.Sex字段指定了規則Rule_Sex,如果Sex定義的是int,該字段寫入數據的時候只能是0或者1,寫入2是失敗的,
    但是往該字段寫入null值是可以的,因為null與規則指定的(0,1)相比沒有任何意義(或者沒有任何結果)

    技術分享圖片

約束的一些特點

    對於建表的時候指定的未命名的約束,SQL Server會自動生成後綴隨機的默認的約束名字,為了增加規範性,正常情況下是不允許這麽寫的,因為我們不希望看到數據庫中一堆亂七八臟的命名。

    技術分享圖片

    如果是指定約束的名字,看起來就規範多了。

技術分享圖片

如果是通過定義規則,然後綁定到表的字段的情況下,通過SSMS的圖形界面,是看不到約束展開之後的內容的(雖然這個約束是生效的)

技術分享圖片

數據庫約束中那些不容易被註意的坑

    約束看起來非常簡單,不管是在建表的時候直接指定,或者是通過alter 表的方式增加約束,看起來都沒有任何問題,條條大道通羅馬,不過裏面還是有一些小坑的。
    一個數據庫中的約束不允許重名。
    也就是意味著,不同的表之間,或者表上與自定義約束之間,都不能存在同名的情況,包括主鍵約束(不能同名),檢查約束(check),默認值約束等,都不能同名
    約束的道理跟表一樣,一個庫的一個schema下中不能定義同名的表一個道理,一個庫的一個schema下不能定義同名的約束一樣。
    現在也不難理解,為什麽sqlserver默認生成的約束的名字,後面是一串隨機字符了吧?

    1,表與表之間的約束不能同名

    技術分享圖片

    2,表與自定義約束名之間不能同名。

    技術分享圖片

    當然有人會說,這有什麽,定義的時候報錯就知道了,還有更大的坑。
    這裏涉及到約束的另一種定義方法,建表的過程中指定命名的主鍵約束。

    比如如下的定義臨時表的腳本,看起來沒有任何問題,也確實沒有任何問題,
    實際中遇到的一個開發人員的問題,編寫的存儲過程中定義了臨時表,定義臨時表的時候指定了命名的主鍵約束(可能是抄的物理表的定義方式),測試通過並發布到生產環境中,一切看起來非常正常。
    發布之後測試了兩把,也表現為正常,等到系統真正被用戶使用的時候,部分用戶反饋系統(涉及到這個功能的地方)偶爾會報錯,時而正常,時而不正常(其實這種問題最難診斷的了)。
    監控應用程序會發現,某個時間段之內,發現這個存儲過程會連續地報錯一種錯誤,錯誤原因就是“無法創建索引或者約束”
    開發人員也很委屈,提交完代碼之後,明明是測試通過了的,更可惡的是,偶爾會報錯,大部分時間是正常的。

技術分享圖片

    通過觀察其代碼,然後冷靜下來想一想,也不難理解,數據庫中的約束是不能同名的,當然臨時表生存的臨時庫也不例外,
    臨時表用完就會自動銷毀沒有錯,意味著如果所有的Session都是串行執行,這個完全沒有問題。
    但是一旦出現並發調用的情況,不同的Session會同時調用這個存儲過程,
    一旦並發調用這個存儲過程,不同的Session會創建同一個名字的約束,鐵定只有一個會成功,這麽看,單線程測試正常,並發上來之後報錯也就不難理解了。

    

總結: 

    為了保證數據庫的完整性和一致性(外鍵,本文未涉及),可以使用約束來達到這個目的,但是約束本身有自己的某些規則和特點,它跟其他數據庫對象並沒有不同的,都不允許同一個schema下存在同名的對象。
    如果沒有按照其自身的某些規則來使用,就有可能造成某些潛在的問題。
    為了規範約束的命名,在定義約束名字的時候,要嚴格遵循簡寫前綴+schema+tableName+columnName的方式來定義,如果是臨時表,禁止定義命名約束。

SQL Server中有關約束(constraint)的一些細節