數據庫設計(2/9):域,約束和默認值(Domains, Constraints and Defaults)

對於設計和建立數據庫徹底是個新手?不要緊,Joe Celko, 世界上讀者數量最多的SQL做者之一,會告訴你這些基礎。和往常同樣,即便是最專業的數據庫老手,也會給他們帶來驚喜。Joe是DMBS雜誌是多年來最受 讀者喜好的做者。他在美國、英國,北歐,南美及非洲傳授SQL知識。他在ANSI / ISO SQL標準委員會工做了10年,爲SQL-89和SQL-92標準作出了傑出貢獻。html


對於數據庫開發人員,SQL數據類型和域的清楚瞭解是基本要求,但不是基礎。若是你選擇了最合適的數據類型,它能夠避開不少錯誤。此外,而後若是你經過約束儘量精確的定義數據域,你會捕獲各類這樣的問題,那就是不然會困擾應用開發人員的工做。前端

第1篇,咱們爲它們是什麼而命名數據元,併爲它們分類。所以如今咱們知道,咱們不能用通用的,神奇的,廣泛的「id」做爲數據元。它必須是一個在架構裏有確切含義的確切名稱。更好的是,名稱應該是企業範圍,行業範圍或在全球的優先排序裏。正則表達式

在第2篇裏,咱們會決定經過選擇合適的域把那些數據放入計算機。域的想法要回到Codd博士。嗯,實際上在這個好博士(Good Doctor)以前,由於它來自數學。域像程序設計裏的數據類型,但涉及更多。域有合法的在它的值上完成的運算符,除此以外還有一個數據類型。例如,我可使用INTEGER在數據庫裏記錄溫度;那是個數據類型。當我看到100,那是個值。同時,我能夠加,減,乘和除整數,但在溫度上這樣作沒有意義。你不能把體溫是36°的人放一塊兒來燒水。模型的類型決定容許什麼操做。但我須要知道數字是否以華氏度(Fahrenheit (°F)),攝氏度(Celsius (°C))仍是絕對溫度(Kelvin (°K));那是計量單位。把這些放在一塊兒,你就有了域。sql

標準SQL有CREATE DOMAIN語句,它建立的架構對象不是真正的域。它只是個咱們即將討論的縮寫。數據庫

SQL有3大類數據類型:編程

  1. 數值(Numberic)
  2. 字符串(String)
  3. 日期(Temporal)

數值型分爲精確和近似的。精確數值有INTEGER, SMALLINT, DECIMAL, NUMERIC和BIGINT。它們保存精確的數值並有明確的計算運算符。近似數值有REAL, FLOAT 和 DOUBLE PRECISION(雙精度)。這些是浮點數,它們會有四捨五入的問題;近來IEEE浮點標準廣泛使用。SQL裏也不例外;大多數其它能夠進行計算的編程語言使用所有,或者使用這些類型的一些,由於它們在SQL 發明很早之前就嵌入了計算機硬件。PC和迷你計算機用戶極可能不知道二進碼十進制(BCD(Binary Coded Decimal))數,但多年來它們是商業電腦主機的組成部分。若是你不能理解這些類型,能夠google下它們。架構

字符串類型分爲定長和變長。定長字符串是CHAR (n) 和NCHAR(n),這裏(n)是它們的長度。NCHAR(n)是「National Character」的簡稱,它的真正含義是來自Unicode已經實行的任何語言的任何字符。CHAR (n) 是本地ASCII字符集。變長字符串不會像定長字符加個空白字符。對列選擇長度和字符集是你在使用它以前,真的要考慮的約束。過短的列會阻止真正的數據,同時太長的列會引入虛假的數據。我最喜歡的一個技巧,當我看到一個以NVARCHAR(255)定義的列,用它來加載中文的心經。這是禪宗佛教(Zen Buddhism)的經典經文。若是我不能教他們SQL,我會帶它們啓蒙。編程語言

日期類型分爲純日期時間和間隔類型。日期時間類型分爲日期和時間。它表示時間上一個點。日期類型包含年,月和日。時間類型包括時,分,秒和10進制的次秒。放一塊兒,在標準SQL裏它們組成TIMESTAMP數據類型,SQL Server稱它們爲DATETIME。間隔類型是像天,時,分和秒的時間持續。SQL Server不把它們做爲特定類型來表示,使用整數的函數來獲得相似結果。函數

對全部數據類型的四捨五入和截斷實現定義。全部數據類型容許NULL,同時在SQL標準裏,對全部的數據類型有一組核心的函數,提供全部有的屬性擴展和細節實現。例如,SQL Server有BIT數據類型,它是一個精確的數字,只容許0, 1, NULL值。你也會找到更多的數據類型,但這「三個值」會完成你的大多數工做。sqlserver

當你在數據上你要作計算時,使用數值類型。那就是說像數量,個數,合計等等數據元。若是你須要進行除簡單加減外的計算,對於四捨五入和溢出定義一些額外的小數位。

對於要排序的嗲嗎你也會使用數值類型。例子就是餐廳的星級(例如1星沒有2星的好,2星的沒有3星的好,以此類推)。不要爲不進行計算或比較的尺度使用數值類型。這個錯誤的常見例子是郵編(ZIP code);第一個數字0有它的意思,你不能在它們上面進行計算,也不能進行排序。標籤數用來表示層級。

對文本,名稱和編碼體系使用字符串能夠用正則表達式表示。例如,郵政編碼應該用CHAR(5)定義。不要嘗試計算它們;在COBOL裏能夠,在SQL裏不行。

對於日期數據使用日期數據類型。沒錯,聽起來太明顯,我都不必說。但其中一個最多見的設計錯誤是對於日期和時間數據使用字符串。固然這樣作的人不會寫約束來阻止像「2010-02-31」的日期或者進行簡單時間計算函數。他們已經犯了設計錯誤,把顯示格式放入數據庫,而不是前端。

實數和時間是連續的例子,其它數據類型是離散的。離散範圍是任何兩個不一樣值之間有限的數字(極可能0)。例如整數{4,9},在它們之間有{5,6,7,8}。有一期兒童電視節目iCarly裏,女主持人說服另外一個小孩,在5和6之間有一個叫Dirf的新的數字。這個笑話太滑稽了,由於很明顯它是錯誤的。

連續性是數學結構,在任何兩個不一樣值之間有無限個數據值。你總能夠添加愈來愈多的小數到實數或沒有任何限制的時間。浮點數有內建的功能來處理四捨五入和計算的問題,可是時間數據不行。一般這就是說你要用一對值來模式化事件(開始事時間,結束時間)。若是事件仍是當前的,那麼使用NULL做爲結束事件值。而後在應用程序裏你可使用COALESCE()函數來轉化NULL爲CURRENT_DATE或其它有意義的值。

約束是表裏的列像記錄裏的字段的一個緣由。約束是在列里約束值的陳述句。最重要的一個是NOT NULL。首次用它來申明每列,而後若是你決定容許NULL,註釋行申明來解釋說明在上下文裏的意思。例如:

sale_start_date DATE NOT NULL,
sale_end_date DATE, – sale is still in progress 

CHECK(<涉及列的謂語>)是最簡單的行級別約束。然和有效的謂語可使用,一些典型的使用會是:

 sex_code SMALLINT NOT NULL
 CHECK (sex_code IN (0,1,2,9)),

body_temperature DECIMAL (3,1) NOT NULL
 CHECK (body_temperature BETWEEN 0.0 AND 45.0),

airport_code CHAR(3) NOT NULL
 CHECK (airport_code = UPPER (airport_code)),

zip_code CHAR(5) NOT NULL
 CONSTRAINT Valid_ZIP_Code
 CHECK (zip_code LIKE '[0-9][0-9][0-9][0-9][0-9]'), 

你也能夠命名約束,如上所示。這是個很好的作法,由於它讓錯誤信息更容易閱讀。使用CASE表達式你會獲得不少有意思的東西,對於其它複雜規則使用if-then邏輯的其它謂語,例如:

 floob_score INTEGER CHAR(5) NOT NULL
CHECK (CASE WHEN floob_score NOT BETWEEN 1 AND 99 THEN 'F'
            WHEN floob_score = 42
             AND fuzz_nbr = 17
            THEN 'T' ELSE 'F' END = T'), 

做爲練習,寫一個CASE表達式來驗證是否爲數字。表達式會很長但不難。

除了數據完整性,約束還會爲你作其它好事。優化器能夠用到它們來提升你的查詢,插入,更新和刪除。它們幫你節約不少前端代碼;在這裏一次搞定,就不用在不少應用程序裏反覆作,無論如今仍是未來。它們保證全部的前端程序使用一樣定義的數據元。

CHECK()約束也能夠放在表級別。例如:

sale_start_date DATE NOT NULL,
sale_end_date DATE, – sale is still in progress
CONSTRAINT Validate_Sale_Duration
CHECK (sale_start_date <= sale_end_date), 

表約束涉及到兩個或更多的列。在標準SQL裏,你也能夠有引用其它表的CHECK()約束,但如今我會跳過這個;它沒有普遍應用,或不是SQL Server的組合。

SQL的策略是它咱們用NULL進行比較時,邏輯值的結果是UNKNOWN,不是TRUE或FALSE。但在CHECK()約束裏,TRUE和UNKNOWN同等對待。咱們把質疑的好處給UNKNOW。

在行裏定義裏的最後選項是DEFAULT子句(默認值)。技術上來講它不是域定義的組成,它很是有用。在標準SQL裏,它會在數據類型後當即出現,在行定義裏,大多數對於它的放置位置都是自由的。它是個常量值或者調用合適數據類型的系統值。最多見的例子是,對於數字和字符串數據在編碼架構裏的默認值,對於日期數據的CURRENT_TIMESTAMP和CURRENT_DATE。例如ISO的性別編碼使用0做爲「未知」,1爲」男性「,2爲」女性「,9做爲合法的人(lawful person )(企業和其它組織會在上下文裏識別爲」法人(legal persons)「)。例如,咱們會像這樣申明行:

 sex_code SMALLINT NOT NULL
 DEFAULT 0
 CHECK (sex_code IN (0,1,2,9)),
 sale_start_date DATE DEFAULT CURRENT_DATE NOT NULL

默認值的目的是在沒有直接給值的地方提供值。這一般使用INSERT INTO語句完成,在這裏你必須構建整個行,但你不想暴露全部行給用戶,或者你想節省一些編程工做。老是提供默認不太可能,當你有的時候再這樣作。

如今咱們會建立行,在第3篇裏咱們須要組合行到表裏。那裏咱們約束的其它類型,它會應用到列,不止單行。

原文連接:

http://www.sqlservercentral.com/articles/Database+Design/69926/

相關文章
相關標籤/搜索