在SQLServer中,有3種不一樣類型的約束。html
一、實體約束數據庫
實體約束是關於行的,好比某一行出現的值就不容許出如今其餘行,例如主鍵。編程
二、域約束ide
域約束是關於列的,對於全部行,某一列有那些約束,例如CHECK約束。post
三、參照完整性約束學習
若是某列的值必須與其餘列的值匹配,那就意味着須要一個參照完整性約束,例如外鍵。this
在學習約束以前,首先來了解下爲約束命名須要注意哪些地方。url
SQLServer在咱們不提供名稱時,會自動建立名稱,可是由系統自動建立的名稱並非特別有用。spa
例如,系統生成的主鍵名稱多是這樣的:PK_Employees_145C0A3F。 code
PK表明主鍵(primary key),Employees表明在Employees表中,而剩下的「145C0A3F」部分是爲了保證惟一性而隨機生成的值。只有經過腳本建立纔會獲得這種值,若是是經過Managerment Studio建立表,那麼就直接是PK_Employees。
對於系統自動生成的Check約束名稱如:CK_Customers_22AA2996。CK表明這是一個Check約束,Customers表明是在Customers表中,後面的22AA2996仍是一個隨機數。若是一個表中有多個Check約束,則命名可能以下:
CK_Customers_22AA2996
CK_Customers_25869641
CK_Customers_267ABA7A
若是你須要修改這些約束其中的一個,那麼你很難分辨這些約束究竟是哪個。
所以,爲了可以一眼看上去就知道這個約束是用來幹什麼的,咱們應該使用一種簡單明瞭的短語來進行命名。
例如要確保某一列電話號碼格式正確的約束,咱們可使用命名CK_Customers_PhoneNo這樣的短語來命名。
總之命名要作到如下幾點:
一、一致性
二、通俗易懂
三、知足以上兩個條件的狀況下簡化名稱。
主鍵是每行的惟一標識符,僅僅經過它就能準肯定位到一行,其中主鍵列在整個表中不能有重複,必須包含惟一的值(不能爲NULL)。因爲主鍵在關係數據庫中的重要性,所以它是全部鍵和約束中最重要的。
下面來講說主鍵的建立方式
一、在建立表的時候建立主鍵約束。
create table customer ( customerId int identity not null primary key, --建立主鍵約束 CustomerName nvarchar(30) not null );
怎麼樣,很是簡單吧!
二、在已存在的表上建立主鍵約束
如今假設已經存在了一張表,可是尚未主鍵約束:
alter table person add constraint PK_Employee_Id --外鍵名稱 primary key(personId) --personId 字段名
alter名稱告訴SQLServer以下信息:
一、添加了一些內容到表中(也能夠刪除表中的某些內容)
二、添加了什麼內容(一個約束)
三、對約束的命名(容許之後直接訪問約束)
四、約束的類型(主鍵約束)
五、約束應用於哪一個列。
三、複合主鍵的建立
若是實在Management Studio中,建立複合主鍵,只須要按住Ctrl鍵,選中兩個列,而後設置爲主鍵就OK了,很是簡單。下面主要講述使用T-SQL建立複合主鍵的方法:
ALTER TABLE 表名 WITH NOCHECK ADD CONSTRAINT [PK_表名] PRIMARY KEY NONCLUSTERED ( [字段名1], [字段名2] )
在多對多聯繫中,經常會有一張表來描述其餘兩張表的關係,就以此讀者和書爲例子:
ALTER TABLE ReaderAndBook ADD CONSTRAINT [PK_ReaderAndBook] PRIMARY KEY NONCLUSTERED ( ReaderId, BookId )
外鍵既能確保數據完整性,也能表現表之間的關係。添加了外鍵以後,插入引用表的記錄要麼必須被引用表中被引用列的某條記錄匹配,要麼外鍵列的值必須設置爲NULL。
外鍵和主鍵不同,每一個表中的外鍵數目不限制惟一性。在每一個表中,每一有-~253個外鍵。惟一的限制是一個列只能引用一個外鍵。一個列能夠被多個外鍵引用。
一、建立表的時候建立外鍵
create table orders ( orderId int identity not null primary key, customerId int not null foreign key references customer(customerId) --約束類型-外鍵-引用表(列名) );
二、在已存在的表中添加一個外鍵
假設上面的代碼去掉了添加外鍵行,那麼能夠書寫代碼以下:
alter table orders add constraint FK_Orders_CustomerId --添加約束 名稱 foreign key (customerId) references customer(customerId) --外鍵約束,外鍵列名,被引用列名
剛添加的約束和以前添加的約束同樣生效,若是某行引用customerId不存在,那麼就不容許把該行添加到Orders表中。
三、級聯動做
外鍵和其餘類型鍵的一個重要區別是:外鍵是雙向的,即不只是限制子表的值必須存在於父表中,還在每次對父表操做後檢查子行(這樣避免了孤行)。SQLServer的默認行爲是在子行存在時「限制」父行被刪除。然而,有時會自動刪除任何依賴的記錄,而不是防止刪除被引用的記錄。一樣在更新記錄時,可能但願依賴的記錄自動引用剛剛更新的記錄。比較少見的狀況是,你可能但願將引用行改變爲某個已知的狀態。爲此,能夠選擇將依賴行的值設置爲NULL或者那個列的默認值。
這種進行自動刪除和自動更新的過程稱爲級聯。這種過程,特別是刪除過程,能夠通過幾層的以來關係(一條記錄依賴於另外一條記錄,而這另外一條記錄又依賴其餘記錄)。在SQLServer中實現級聯動做須要作的就是修改外鍵語法-只須要在添加前面加上ON子句。例如:
alter table orders add constraint FK_Orders_CustomerId --添加約束 名稱 foreign key (customerId) references customer(customerId) --外鍵約束,外鍵列名,被引用列名 on update no action --默認 修改時不級聯更新子表 on delete cascade --刪除時級聯刪除依賴行
當在進行級聯刪除時,若是一個表級聯了另外一個表,而另外一個表又級聯了其餘表,這種級聯會一直下去,不受限制,這實際上是級聯的一個危險之處,很容易一個不當心刪掉大量數據。
級聯動做除了no action,cascade以外,還有set null和set default。後兩個是在SQLServer2005中引入的,若是要兼容到SQLServer2000的話,要避免使用這兩個級聯動做。可是他們的才作是很是簡單的:若是執行更新而改變了一個父行的值,那麼子行的值將被設置爲NULL,或者設置爲該列的默認值(無論SET NULL仍是SET DEFAULT)。
四、外鍵其餘方面的考慮
外鍵中的之只有相中可能的選擇:
一、在列中填充與被引用表中的相應列相匹配的值。
經過定義引用列爲NOT NULL,可使外鍵徹底是必須的(即用戶添加數據時必須引用表中必須有相匹配的一行數據)。
二、不填充任何值,而使該值爲NULL。
容許引用列有NULL值時,用戶能夠選擇不提供值-即便在被引用表沒有與NULL值匹配的行,仍是容許插入。
惟一約束與主鍵比較類似,共同點在於它們都要求表中指定的列(或者列的組合)上有一個惟一值,區別是惟一約束沒有被看做表中記錄的惟一標識符(即便你能夠按這樣的方式使用也有效),並且能夠有多個惟一約束(而在每一個表中只能有一個主鍵)。
一旦創建了惟一約束,那麼指定列中的每一個值必須是惟一的。若是更新或者插入一條記錄在帶惟一約束的列上有已經存在的值的記錄,SQLServer將拋出錯誤,拒絕這個記錄。
和主鍵不一樣,惟一約束不會自動防止設置一個NULL值,是否容許爲NULL由表中相應列的NULL選項的設置決定,但即便確實容許NULL值,一張表中也只可以插入一個NULL值(若是容許多個,那就不叫惟一了)。
在已存在的表上建立惟一約束:
alter table Account add constraint AK_AccountName --約束名 unique (Account_Name) -- 列名
AK表明替換鍵(Alternate Key),惟一約束也叫替換鍵。
主鍵和惟一約束的區別:
CHECK約束約束能夠和一個列關聯,也能夠和一個表關聯,由於它們能夠檢查一個列的值相對於另一個列的值,只要這些列都在同一個表中以及值是在更新或者插入的同一行中。CHECK約束還能夠用於檢查列值組合是否知足某一個標準。
能夠像使用where子句同樣的規則來定義CHECK約束。CHECK約束條件的示例以下:
目標 | SQL |
限制Month列爲合適的數字 | BETWEEN 1 AND 12 |
正確的SSN格式 | LIKE'[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]' |
限制爲一個快遞公司的特定列表 | IN('UPS','Fed Ex',EMS') |
價格必須爲正數 | UnitPrice >= 0 |
引用同一行中的另一列 | ShipDate >= OrderDate |
上面給出的列表只是一小部分,而條件實際上市無限多的。幾乎全部能夠放到where子句的條件均可以放到該約束中。並且和其餘選擇(規則和觸發器)相比,CHECK約束執行速度更快。
在已存在的表中添加一個CHECK約束:
alter table Account add constraint CN_AccountAge check (Account_Age > 18); -- 插入年齡必須大於18
若是此時視圖添加一條不知足的記錄,將報以下錯誤:
insert into Account values (22,'洪',17)
消息 547,級別 16,狀態 0,第 1 行 INSERT 語句與 CHECK 約束"CN_AccountAge"衝突。該衝突發生於數據庫"Nx",表"dbo.Account", column 'Account_Age'。 語句已終止。
和全部約束同樣,DEFAULT約束也是表定義的一個組成部分,它定義了當插入的新行對於定義了默認約束的列未提供相應數據時該怎麼辦。能夠定義它爲一個字面值(例如,設置默認薪水爲0,或者設置字符串列爲"UNKNOWN"),或者某個系統值(getdate())。
對於DEFAULT約束,要了解如下幾個特性:
一、默認值只在insert語句中使用-在update語句和delete語句中被忽略。
二、若是在insert語句中提供了任意值,那就不使用默認值。
三、若是沒有提供值,那麼老是使用默認值。
值得注意的是,update命令的規則由一個例外,若是顯示說明使用默認值就是例外。能夠經過使用關鍵字DEFAULT表示更新的值設置爲默認值。
5.1在建立表時定義DEFAULT約束:
create table person ( person_id int identity not null primary key, person_name nvarchar(30) not null default '無名氏', person_age int not null )
在執行語句後:
insert into person (person_age) values(24)
表中被插入一條記錄以下:
5.2在已存在的表上添加DEFAULT約束:
alter table person add constraint CN_DefaultName default '無名氏' for person_name
有時咱們想暫時或永久地消除約束。可是SQL Server並無提供刪除約束的方法。SQL Server只容許禁用外鍵約束或CHECK約束,而同時保持約束的完整性。
禁用一個數據完整性規則一般是由於已經有無效數據了。這樣的數據一般分爲如下兩類:
一、在建立約束時已經在數據庫中的數據
二、在約束建立之後但願添加的數據
SQL Server容許禁用完整性檢查一段時間來對例外的無效數據做處理,而後再從新啓用完整性(不是物理刪除數據完整性約束)。
注意:不能禁用主鍵約束或者惟一約束
6.一、在建立約束時,忽略檢查以前的不知足數據
要添加一個約束,可是有不該用到已存在的數據中,能夠再執行Alter Table語句添加約束時使用WITH NOCHECK選項。
按照上面建立Check約束的方法,已經Alter Table時,表中自己已經存在不符合的數據,那麼Alter Table操做將被SQL Server拒絕執行。除非已經存在的全部數據都知足CHECK約束的條件,不然SQL Server不會執行建立約束的命令。要解決這個問題,咱們能夠添加WITH NOCHECK。
咱們先新建一個表只有3個字段的表,Id、姓名、年齡,並在裏面插入一條不知足要求的數據:
insert into Account values (23,'洪',17)
而後執行添加約束命令:
alter table Account add constraint CN_AccountAge18 check (Account_Age > 18); -- 插入年齡必須大於18
SQL Server報一下錯誤:
消息 547,級別 16,狀態 0,第 1 行 ALTER TABLE 語句與 CHECK 約束"CN_AccountAge18"衝突。該衝突發生於數據庫"Nx",表"dbo.Account", column 'Account_Age'。
這時候咱們換一種方式去執行:
alter table Account WITH NOCHECK add constraint CN_AccountAge18 check (Account_Age > 18); -- 插入年齡必須大於18
以上代碼就可以成功執行,而且只有之後添加的數據具有約束,以前添加的不符合條件的數據記錄依然存在。
6.2臨時禁用已存在的約束
當咱們須要從另外一數據庫中導入數據到表中,而表中已創建了約束的時候,可能會存在一些數據和規則不匹配。固然有一個解決方式是先刪除約束,添加須要的數據,而後WITH NOCHECK在添加回去。可是這樣作太麻煩了。咱們不須要這麼作。咱們能夠採用名爲NOCHECK的選項來運行ALTER語句,這樣就可以取消須要的約束。
先來看看上節中建立的這個約束:
alter table Account add constraint CN_AccountAge18 check (Account_Age > 18); -- 插入年齡必須大於18
要取消以上約束能夠這樣來:
Alter Table Account NOCHECK constraint CN_AccountAge18
執行命令:
insert into Account values (25,'取消了約束',17)
執行成功,成功添加了一行數據。
留意到又可以向表中插入格式不匹配的數據了。
這裏要說明下,以下知道一個約束是不是啓用仍是禁用呢?sp_helpconstraint命令,當咱們執行sp_helpconstraint的時候,會有一列status_enabled顯示該約束的啓用狀態:
sp_helpconstraint Account
留意到status_enabled列爲Disabled說明是禁用的意思。
當要啓用約束時,只須要用將語句中的NO CHECK替換爲CHECK就能夠了:
Alter Table Account CHECK constraint CN_AccountAge18
執行以後,約束又啓用了:再來sp_helpconstraint看下:
留意到status_enabled列變成了Enabled。
status_enabled的兩種狀態以下:
Enabled:啓用;
Disabled:禁用;
規則和默認值的應用要早於CHECK和DEFAULT約束。他們是較老的SQL Server備用約束的一部分,固然也不是沒有優勢。自7.0版本以後,MicroSoft列出規則和默認值只是爲了向後兼容,而不許備在之後繼續支持這個特性。所以對於生成新代碼時,應該使用約束。
規則、默認值與約束的本質區別是:約束是一個表的特徵,自己沒有存在形式,而規則和默認值是表和自身的實際對象,自己存在。約束是在表定義中定義的,而規則和默認值是單獨定義,而後"綁定到"表上。
規則和默認值的獨立對象特性使得它們能夠在重用時不用從新定義。實際上,規則和默認值不限於被綁定到表上,它們也能夠綁定到數據類型上。
7.1規則
規則和CHECK約束很是類似。它們之間的惟一區別是規則每次只能做用於一個列。能夠將同一規則分別綁定到一個表中的多個列,可是規則分別做用於每一個列,根本不會意識到其餘列的存在。像QtyShipped
<= QtyOrdered這樣的約束不適用於規則(它引用多個列),而LIKE([0-9][0-9][0-9])這樣的定義適用於規則。
定義規則:
下面定義一個規則,這樣就能夠首先看到區別所在:
CREATE RULE Age18Rule AS @Age > 18
這裏比較的是一個變量,無論被檢查的列是什麼值,這個值將用於替換@Age。所以在這個示例中,規則所綁定的任何列的值都必須大於18。
到目前爲止,只是建立了一個規則,但這個規則還沒對任何表的任何列起做用,要激活這個規則須要使用一個存儲過程:sp_bindrule。
將規則Age18綁定到表person的person_age列:
EXEC sp_bindrule 'Age18Rule','person.person_age';
此時,若是咱們執行不知足規則的插入操做:
insert into person values ('綁定規則',17)
將返回以下報錯信息:
消息 513,級別 16,狀態 0,第 1 行 列的插入或更新與先前的 CREATE RULE 語句所指定的規則發生衝突。該語句已終止。衝突發生於數據庫 'Nx',表 'dbo.person',列 'person_age'。 語句已終止。
很明顯,規則已經生效。
要特別注意的是,在綁定以前,規則與任何表,任何列都沒有關係,所以在綁定的時候,第二個參數要加.指定表名與列名(tablename.column)。
解除綁定規則:
當咱們須要在一個列上解除綁定規則的時候,只要執行sp_unbindrule
刪除剛纔綁定的規則:
EXEC sp_unbindrule 'person.person_age';
這時候,執行剛纔的插入操做,就不會報錯了。
刪除規則:
若是但願將規則從數據庫中完全刪除,那麼能夠在表中使用很是熟悉的DROP語法。
DROP RULE <rule name>
如刪除剛纔建立的那條規則:
DROP RULE Age18Rule
7.2默認值
默認值相似於DEFAULT。實際上默認值-DEFAULT約束的關係與規則-CHECK約束的關係差很少。區別在於它們被追加到表中的方式和對用戶自定義數據類型的默認值(是對象,而不是約束)支持。
定義默認值的語法和定義規則相似:
CREATE DEFAULT <default_name> AS <default value>
建立默認值:
所以,假設要爲Age定義一個值爲0的默認值:
CREATE DEFAULT AgeDefault AS 0
綁定默認值:
一樣,若是不綁定到一個對象上,則默認值是不起做用的。要綁定的話,使用存儲過程sp_bindefault。
EXEC sp_bindefault 'AgeDefault','person.person_age';
要從表中解決默認值的綁定,使用sp_unbindefault:
sp_unbindefault 'person.person_age';
刪除默認值:
若是要從數據庫中完全刪除一個默認值,則可使用DROP語法,與刪除規則相同:
DROP DEFAULT AgeDefault
7.3肯定哪一個表和數據類型使用給定的規則或默認值
若是但願刪除或者修改規則或默認值。那麼您能夠先看看哪些表和數據類型在使用它們。SQL Server仍是採用系統存儲過程解決這個問題。這個存儲過程是sp_depends。其語法以下所示:
EXEC sp_depends <object name>
sp_depends提供了依賴於你所查詢對象的全部對象列表。
觸發器也可以用於實現數據完整性,這個內容比較多,新建一篇文章敘述。
通過以上的學習,對於數據完整性,你會發現有不少種能夠選擇,那麼如何挑選合適的約束呢?
限制 | 優勢 | 缺點 |
約束 | 快速 能夠引用其餘列 在命令執行前發生 遵循ANSI標準 |
必須對每一個表從新定義 不能引用其餘表 不能綁定到數據類型 |
規則 | 獨立的對象 可重用 能夠綁定到數據類型 命令執行前發生 |
稍慢 不能跨列使用 不能引用其餘表 實際上只用於向後兼容 |
默認值 | 很是靈活 能夠引用其餘列或其餘表 能夠經過.NET引用SQL Server以外的其餘信息 |
在命令執行以後發生 系統開銷很大 |
若是要實現更健壯的邏輯模型以及普遍使用用戶自定義數據類型,則通常使用規則和默認值。在這種狀況下規則和默認值能夠提供不少功能,容易管理,而不用太多的編程開銷。
只有在不能選擇約束時使用觸發器。和約束同樣,他們被附加到表中,並且必須對建立的每一個表從新定義。好的方面是觸發器幾乎能夠作數據完整性方面的任何操做。實際上再沒有出現外鍵時,他們常被用做外鍵的替代品。
而在其餘狀況下,應將約束做爲數據完整性解決方案的選擇。它們執行速度快,並且不難建立。他們的缺點是功能有限(除了外鍵約束,都不能引用其餘表),並且對於通用約束邏輯來講,須要一次次地從新定義。