對於設計和建立數據庫徹底是個新手?不要緊,Joe Celko, 世界上讀者數量最多的SQL做者之一,會告訴你這些基礎。和往常同樣,即便是最專業的數據庫老手,也會給他們帶來驚喜。Joe是DMBS雜誌是多年來最受 讀者喜好的做者。他在美國、英國,北歐,南美及非洲傳授SQL知識。他在ANSI / ISO SQL標準委員會工做了10年,爲SQL-89和SQL-92標準作出了傑出貢獻。web
有不少表類型,每一個都有它們特定規則和完整性約束的需求。無論什麼需求,表層級的約束會確保那些規則被執行,數據完整性被保持。sql
在第一篇,咱們爲了它們是什麼,更好的區分它們來命名數據元。在第二篇,咱們用SQL裏的數據類型和約束來模型化數據元,來提供咱們行。在第三篇,咱們將把這些行放入表。表是在一個名稱下,不少行的集合。數據庫
在表裏,列只會出現一次。這樣作是有道理的;若是你兩次記錄某人的鞋子大小,這將是多餘的,當列不一致時是混淆的。如今咱們能夠有表層級的在每行的列裏的檢查(CHECK)約束。這和以前列上的(CHECK)檢查並無啥區別。它們能夠在CREATE TABLE語句裏,多個列聲明裏命名並出現,不附加到任何行。例如:架構
1 CONSTRAINT Valid_Employee_Age-- don't hire people before they are born 2 CHECK (emp_birth_date < emp_hire_date)
一般不該該把檢查組合成一個大的CHECK()子句。錯誤信息會包含約束名稱,所以獨立的約束會,相比單個複雜命名的約束,給讓你更清楚的發現問題。sqlserver
繼續咱們的冗餘問題,在表層級咱們想每一個行因同個緣由而惟一。這能夠經過約束實現。兩個表層級的約束是UNIQUE和PRIMARY KEY,它們能夠是單列或多列組合。spa
UNIQUE約束表示在表裏,列或列的組合是惟一的。但在列或多個列中有NULL,若是它是惟一值,咱們仍是容許的。PRIMARY KEY聲明,與對於表裏面的全部列,NOT NULL且UNIQUE有一樣的效果。但因爲歷史緣由,表只能有一個PRIMARY KEY聲明。這些列用來做爲表之間的其餘約束,但如今不要擔憂這個。設計
惟一性約束如何使用取決於涉及的表類型。通常來講,咱們能夠把表分爲三類:指針
實體表是多個同類事物,經過列的模型屬性定義。每一行是這類東西的實例。每行有一樣的列。若是你能夠看到它感受,看到它或感覺它,那它是一個實體。實體表的命名不該該是單數(除非這個集合裏真的只有一個成員),由於它模型化了一組。命名應該是複數,可能的話,使用集合命名。例如,「Employee」很差,「Employees」更好,「Personnel」最好。「Tree」很差,「Trees」更好,「Forest」最好。你能夠添加你本身的例子。rest
實體也區分弱和強。強實體存在有它本身的優勢,同時,弱實體存在由於一個或多個強實體。你須要購買前,你能夠有個折扣。code
關係表指的是一個或多個實體表,而且它們之間創建關係。關係能夠有它本身委外引用實體的屬性。結婚登記號屬於婚姻,不屬於丈夫,妻子或牧師。
關係級別是關係裏實體的個數。二元關係有2個實體,在現實世界中咱們喜歡它們,由於它們簡單。二元迭代關係關聯到實體自己。通常的n元關係涉及n個實體,就像有買家,賣家和銀行的房貸。一般不能把n元關係分解爲二元關係。成員的關係能夠是可選或必須的。可選的關係表示咱們能夠有一類的0實體——並非全部的買賣都有折扣。
關係基數是對於每2個實體,相關出現的實際數量。關係的基本鏈接類型有:1:1,1:n,和n:n。這些術語一般是符合可選(0或更多)或必須的(1或更多)的關係。
1:1關係是一個實體A的最多一個實例與實體B的一個實例關聯的時候。例如,拿一般的丈夫和妻子的關係。每一個丈夫有且只要一個妻子;每一個妻子有且只有一個丈夫。在這個例子都是必須一個的。
1:n關係是實體A的一個實例,對於實體B的一個實例有0個,一個或多個實體B的實例,實體A的實例只有一個的時候。一個例子會是一個部門有不少員工;每一個員工分配到一個部門。取決於你的業務規則,你會容許未分配部門的員工或空的部門。
n:n關係,有時稱做非特定的,對於實體A的一個實例,有0個,一個或多個實體B的實例,而且對於實體B的一個實例,有0個,一個或多個實體A的實例。這樣的例子能夠是披薩和客戶。
輔助表不是實體也不是關係;它提供信息。它們是像日曆或在SQL裏替換計算的查詢表(look up tables)。它們常常被誤解被當實體或關係表對待。
咱們來具體說下。銷售訂單是客戶(實體)和咱們的庫存(實體)之間的關係。訂單明細是存在的弱實體,由於咱們有訂單。這個關係有一個不是庫存或客戶一部分的訂單號。運費從輔助表得到。對於這個例子,這裏我用了一些骨架表。對於訂單項目,我使用GTIN(Global Trade Item Number),對於客戶,我使用GUNS(Data Universal Numbering System)。在你設計數據庫的時候,記得都先看看行業標準。
1 CREATE TABLE Sales_Orders 2 3 (order_nbr INTEGER NOT NULL PRIMARY KEY 4 5 CHECK (order_nbr > 0), 6 7 customer_duns CHAR(9) NOT NULL, 8 9 order_shipping_amt DECIMAL (5,2) NOT NULL 10 11 CHECK (shipping_amt >= 0.00), 12 13 etc); 14 15 CREATE TABLE Sales_Order_Details 16 17 (order_nbr INTEGER NOT NULL, 18 19 gtin CHAR(15) NOT NULL, 20 21 PRIMARY KEY (order_nbr, gtin), 22 23 item_qty INTEGER NOT NULL 24 25 CHECK (item_qty > 0), 26 27 item_unit_price DECIMAL (8,2) NOT NULL 28 29 CHECK (item_unit_price >=0.00)); 30 31 CREATE TABLE Customers 32 33 (customer_duns CHAR(9) NOT NULL PRIMARY KEY 34 35 CHECK (customer_duns LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'), 36 37 etc); 38 39 CREATE TABLE Inventory 40 41 (gtin CHAR(15) NOT NULL PRIMARY KEY 42 43 CHECK (gtin LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'), 44 45 onhand_qty INTEGER NOT NULL 46 47 CHECK (onhand_qty >= 0),
咱們能夠看到訂單表是客戶和庫存間的關係。訂單有它們本身的主鍵(order_nbr),但沒有東西強制咱們使用有效的客戶DUNS號或對於咱們庫存裏的產品GTIN號。事實上,我能夠插入顯然無效的DUNS和GTIN碼到訂單表,如今就這樣聲明。
這就是咱們要引入REFERENCES子句的地方。它是讓咱們從數據模型強制全部基數和程度的東西。引用(reference)不是個連接或指針。這些是物理概念,引用是個邏輯概念,咱們不知道它如何實現。它強制的是,在引用表裏,引用表列符合單行的規則。這意味着在引用表裏的行必須惟一;默認狀況下,在引用表裏可使用主鍵(PRIMARY KEY),但沒必要這樣。在引用表的值能夠稱爲外鍵(Foreign Keys)——它們不在它們的表裏,但在架構裏的其它地方。
這是上面有更多信息的主要架構:
1 CREATE TABLE Sales_Orders 2 (order_nbr INTEGER NOT NULL PRIMARY KEY 3 CHECK (order_nbr > 0), 4 customer_duns CHAR(9) NOT NULL 5 REFERENCES Customers(customer_duns), 6 order_shipping_amt DECIMAL (5,2) DEFAULT 0.00 NOT NULL 7 CHECK (shipping_amt >= 0.00), 8 etc); 9 10 CREATE TABLE Sales_Order_Details 11 (order_nbr INTEGER NOT NULL 12 REFERENCES Orders(order_nbr), 13 gtin CHAR(15) NOT NULL 14 REFERENCES Inventory(gtin), 15 PRIMARY KEY (order_nbr, gtin),-- two column key 16 item_qty INTEGER NOT NULL 17 CHECK (item_qty > 0), 18 item_unit_price DECIMAL (8,2) NOT NULL 19 CHECK (item_unit_price >= 0.00)); 20 21 CREATE TABLE Customers 22 (customer_duns CHAR(9) NOT NULL PRIMARY KEY 23 CHECK (customer_duns LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'), 24 etc); 25 26 CREATE TABLE Inventory 27 (gtin CHAR(15) NOT NULL PRIMARY KEY 28 CHECK (gtin LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'), 29 onhand_qty INTEGER NOT NULL 30 CHECK (onhand_qty >= 0), 31 etc);
注意,在DUNS和GTIN是主鍵的地方,咱們只有CHECK()約束,不是在它們出現的引用表裏。實體表,客戶和庫存是引用的;關係表,訂單,引用了其它表。這是經常使用模式,但這個設置不是固定的。
這個子句的多列看起來像這樣:
1 FOREIGN KEY (order_nbr, gtin) 2 REFERENCES Sales_Order_Details(order_nbr, gtin)
在FOREIGN KEY子句的列是引用表裏須要匹配的引用主鍵,列對列,但會有不一樣的名稱。我能夠經過在相應的地方放置惟一約束來設置1:1,1:n和n:n的關係。做爲輔助表的一個例子,咱們能夠基於訂單總額計算運費。表看起來像這樣:
1 CREATE TABLE Shipping_Costs 2 (start_order_amt_tot DECIMAL (10,2) NOT NULL, 3 end_order_amt_tot DECIMAL (10,2) NOT NULL, 4 CONSTRAINT Valid_Shipping_Range 5 CHECK (start_order_amt_tot < end_order_amt_tot), 6 PRIMARY KEY (start_order_amt_tot, end_order_amt_tot), 7 shipping_amt DECIMAL (5,2) NOT NULL 8 CHECK (shipping_amt > 0.00));
當咱們在輔助運費表上聲明瞭主鍵(PRIMARY KEY),對於實體,它不想主鍵——沒有驗證或覈查,它不是個標識。使用這個表,咱們能夠這樣查詢:
1 SELECT shipping_amt 2 FROM Shipping_Costs 3 WHERE <order amount total> BETWEEN start_order_amt_tot AND end_order_amt_tot;
做爲練習,嘗試寫下會從重複和斷層上阻止開始和結束範圍的約束。若是你須要的話,能夠從新設計表。
在修正後的主要架構裏,當你下沒有庫存的訂單,你會收到錯誤提示「沒有庫存!」,這樣的話,你能夠試下別的。但若是你嘗試從庫存裏刪除產品,你一樣也會收到錯誤提示「額,有人已經下了此產品的訂單」,所以在能夠從庫存裏刪它以前,你必須到每一個訂單用別的值或NULL值來替換它。
這裏就是引用完整性(Declarative Referential Integrity (DRI))用的地方。語法是:
1 ON DELETE [NO ACTION | SET DEFAULT | SET NULL | CASCADE] 2 ON UPDATE [NO ACTION | SET DEFAULT | SET NULL | CASCADE]
刪除和更新是所謂的「數據基礎事件(data base events)」;當它們發生到表時,就會發生DRI操做。
在這些操做完成後,引用完整性約束仍是有效的。這是最終的架構:
1 CREATE TABLE Sales_Orders 2 (order_nbr INTEGER NOT NULL PRIMARY KEY 3 CHECK (order_nbr > 0), 4 customer_duns CHAR(9) NOT NULL 5 REFERENCES Customers(customer_duns) 6 ON UPDATE CASCADE 7 ON DELETE CASCADE, 8 order_shipping_amt DECIMAL (5,2) DEFAULT 0.00 NOT NULL 9 CHECK (shipping_amt >= 0.00), 10 etc); 11 12 CREATE TABLE Sales_Order_Details 13 (order_nbr INTEGER NOT NULL 14 REFERENCES Orders(order_nbr) 15 ON UPDATE CASCADE 16 ON DELETE CASCADE, 17 gtin CHAR(15) NOT NULL 18 REFERENCES Inventory(gtin) 19 ON UPDATE CASCADE 20 ON DELETE CASCADE, 21 PRIMARY KEY (order_nbr, gtin),-- two column key 22 item_qty INTEGER NOT NULL 23 CHECK (item_qty > 0), 24 item_unit_price DECIMAL (8,2) NOT NULL 25 CHECK (item_unit_price >= 0.00));
看看下面的狀況發生時,你會找出會發生什麼?
顯然,我留下未處理的問題和其餘東西,但咱們會接觸這些。
http://www.sqlservercentral.com/articles/Stairway+Series/69927/