RDBMS 是關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng)(Relational DataBase Management System)的縮寫,它是 SQL 以及所有現(xiàn)代數(shù)據(jù)庫(kù)系統(tǒng),例如 MS SQL Server、IBM DB2、Oracle、MySQL 和 MS Access等的基礎(chǔ)。
關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng)(RDBMS)是一種基于 E.F. 科德提出的關(guān)系模型的數(shù)據(jù)庫(kù)管理系統(tǒng)。
RDBMS 中的數(shù)據(jù)存儲(chǔ)在被稱作表的數(shù)據(jù)庫(kù)對(duì)象中。表是相互關(guān)聯(lián)的數(shù)據(jù)記錄的集合,由一系列的行和列組成。
謹(jǐn)記,表是關(guān)系型數(shù)據(jù)庫(kù)中最常見(jiàn)也是最簡(jiǎn)單的數(shù)據(jù)存儲(chǔ)形式。下面是一個(gè)客戶信息表的例子:
+----+----------+-----+-----------+----------+
| ID | NAME | AGE | ADDRESS | SALARY |
+----+----------+-----+-----------+----------+
| 1 | Ramesh | 32 | Ahmedabad | 2000.00 |
| 2 | Khilan | 25 | Delhi | 1500.00 |
| 3 | kaushik | 23 | Kota | 2000.00 |
| 4 | Chaitali | 25 | Mumbai | 6500.00 |
| 5 | Hardik | 27 | Bhopal | 8500.00 |
| 6 | Komal | 22 | MP | 4500.00 |
| 7 | Muffy | 24 | Indore | 10000.00 |
+----+----------+-----+-----------+----------+
每張表都能夠劃分成更小的實(shí)體——字段。例如,上面的客戶信息表中有 ID、NAME、AGE、ADDRESS 和 SALARY 五個(gè)字段。
一個(gè)字段限定了數(shù)據(jù)表中的列,被用來(lái)維護(hù)表中所有記錄的特定信息。
記錄或者說(shuō)數(shù)據(jù)行是存在于數(shù)據(jù)表中的獨(dú)立條目。例如,上面的客戶信息表中有 7 條記錄。下面是客戶信息表中的一條記錄:
+----+----------+-----+-----------+----------+
| 1 | Ramesh | 32 | Ahmedabad | 2000.00 |
+----+----------+-----+-----------+----------+
記錄就是表中水平排列的數(shù)據(jù)構(gòu)成的實(shí)體。
列是表中豎直排列的實(shí)體,它包含了表中與某一特定字段相關(guān)的所有信息。
例如,上面的客戶信息表中有字段為 ADDRESS 的列,存儲(chǔ)了客戶的地址,其內(nèi)容如下所示:
+-----------+
| ADDRESS |
+-----------+
| Ahmedabad |
| Delhi |
| Kota |
| Mumbai |
| Bhopal |
| MP |
| Indore |
+----+------+
NULL 值是表中以空白形式出現(xiàn)的值,表示該記錄在此字段處沒(méi)有設(shè)值。
一定要明白 NULL 值同 0 值或者包含空格的字段是不同的。值為 NULL 的字段是在記錄創(chuàng)建的時(shí)候就被留空的字段。
約束是表中的數(shù)據(jù)列必須遵守的規(guī)則,用于限制表中數(shù)據(jù)的類型。約束保證了數(shù)據(jù)庫(kù)中數(shù)據(jù)的精確性和可靠性。
約束可以限制列或者表。列級(jí)的約束只限制單一的列,而表級(jí)的約束作用于整個(gè)表。
以下是 SQL 中常見(jiàn)的約束:
默認(rèn)情況下,數(shù)據(jù)表中的字段接受 NULL 值。如果你不想讓某個(gè)字段接受 NULL 值,那么請(qǐng)為該字段定義此約束,以指明該字段不接受 NULL 值。
NULL 并不是指沒(méi)有數(shù)據(jù),而是指該字段數(shù)據(jù)未知。
例如,下述 SQL 語(yǔ)句創(chuàng)建了一個(gè)新的數(shù)據(jù)表 CUSTOMERS,并添加了五個(gè)字段,其中三個(gè)字段——ID、NAME 和 AGE——被指定為 NOT NULL:
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR (25) ,
SALARY DECIMAL (18, 2),
PRIMARY KEY (ID)
);
對(duì)于 Oracle 和 MySQL 來(lái)說(shuō),如果 CUSTOMERS 表已經(jīng)存在,此時(shí)再要給 SALARY 字段添加 NOT NULL 約束的話,SQL 語(yǔ)句應(yīng)當(dāng)如下:
ALTER TABLE CUSTOMERS
MODIFY SALARY DECIMAL (18, 2) NOT NULL;
DEFAULT 約束在 INSERT INTO 語(yǔ)句沒(méi)有提供的情況下,為指定字段設(shè)置默認(rèn)值。
例如,下述 SQL 語(yǔ)句創(chuàng)建了一個(gè)名為 CUSTOMERS 的新表,并添加了五個(gè)字段。這里,SALARY 字段的默認(rèn)值為 5000。因此,如果 INSERT INTO 沒(méi)有為該字段提供值的話,該字段就為默認(rèn)值 5000。
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR (25) ,
SALARY DECIMAL (18, 2) DEFAULT 5000.00,
PRIMARY KEY (ID)
);
如果 CUSTOMERS 表已經(jīng)存在,此時(shí)再要給 SALARY 字段添加 DEFAULT 約束的話,你需要類似下面的語(yǔ)句:
ALTER TABLE CUSTOMERS
MODIFY SALARY DECIMAL (18, 2) DEFAULT 5000.00;
要?jiǎng)h除 DEFAULT 約束的話,請(qǐng)使用下面的 SQL 語(yǔ)句:
ALTER TABLE CUSTOMERS
ALTER COLUMN SALARY DROP DEFAULT;
UNIQUE 約束使得某一字段對(duì)任意兩條記錄來(lái)說(shuō)都不能相同。例如,在 CUSTOMERS 表中,你或許想讓任何人的年齡(age)都不相同。
例如,下述 SQL 語(yǔ)句創(chuàng)建了一個(gè)名為 CUSTOMERS 的新表,并添加了五個(gè)字段,其中 AGE 字段被設(shè)為 UNIQUE,于是任意兩條記錄的 AGE 都不同:
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL UNIQUE,
ADDRESS CHAR (25) ,
SALARY DECIMAL (18, 2),
PRIMARY KEY (ID)
);
如果 CUSTOMERS 表已經(jīng)存在,再要為 AGE 字段添加 UNIQUE 約束的話,你需要像下面這樣寫 SQL 語(yǔ)句:
ALTER TABLE CUSTOMERS
MODIFY AGE INT NOT NULL UNIQUE;
還可以使用如下所示的語(yǔ)法,該語(yǔ)法還支持對(duì)作用于多個(gè)字段的約束進(jìn)行命名:
ALTER TABLE CUSTOMERS
ADD CONSTRAINT myUniqueConstraint UNIQUE(AGE, SALARY);
要?jiǎng)h除 UNIQUE 約束的話,請(qǐng)使用如下 SQL 語(yǔ)句:
ALTER TABLE CUSTOMERS
DROP CONSTRAINT myUniqueConstraint;
如果你在使用 MySQL,那么下面的語(yǔ)法也是可行的:
ALTER TABLE CUSTOMERS
DROP INDEX myUniqueConstraint;
主鍵是數(shù)據(jù)表中唯一確定一條記錄的字段。主鍵必須包含唯一值,并且不能為 NULL。
每張數(shù)據(jù)表只能有一個(gè)主鍵,不過(guò)一個(gè)主鍵可以包含一個(gè)或者多個(gè)字段。如果主鍵由多個(gè)字段組合而成,這些字段就被稱作組合鍵。
如果一個(gè)字段被定義為了某表的主鍵,則任意兩條記錄在該字段處不能相同。
注意:在創(chuàng)建數(shù)據(jù)表的時(shí)候,需要用到這些概念。
如下是將 ID 定義為 CUSTOMERS 表主鍵的語(yǔ)法:
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR (25) ,
SALARY DECIMAL (18, 2),
PRIMARY KEY (ID)
);
如果 CUSTOMERS 表已經(jīng)存在了,再要將 ID 定義為主鍵的話,請(qǐng)使用下面的語(yǔ)句:
ALTER TABLE CUSTOMER ADD PRIMARY KEY (ID);
注意:如果你要使用 ALTER TABLE 語(yǔ)句來(lái)添加主鍵,那么主鍵所在的列必須已經(jīng)被聲明為 NOT NULL 了。
要用多個(gè)字段來(lái)定義主鍵的話,請(qǐng)使用如下 SQL 語(yǔ)法:
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR (25) ,
SALARY DECIMAL (18, 2),
PRIMARY KEY (ID, NAME)
);
如果 CUSTOMERS 表已經(jīng)存在,此時(shí)再要將 ID 和 NAMES 字段定義為主鍵的話,請(qǐng)使用如下 SQL 語(yǔ)法:
ALTER TABLE CUSTOMERS
ADD CONSTRAINT PK_CUSTID PRIMARY KEY (ID, NAME);
你可以將主鍵約束從數(shù)據(jù)表中刪除,語(yǔ)法如下:
ALTER TABLE CUSTOMERS DROP PRIMARY KEY ;
外鍵用于將兩個(gè)數(shù)據(jù)表連接在一起,有時(shí)候也被稱作“參照鍵”。
外鍵為單一字段或者多個(gè)字段的組合,并與另外一個(gè)數(shù)據(jù)表的主鍵相匹配。
兩個(gè)表之間的關(guān)系是:一個(gè)表的主鍵與另一個(gè)表的外鍵相匹配。
考慮如下兩個(gè)表的結(jié)構(gòu):
CUSTOMERS 表:
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR (25) ,
SALARY DECIMAL (18, 2),
PRIMARY KEY (ID)
);
ORDERS 表:
CREATE TABLE ORDERS (
ID INT NOT NULL,
DATE DATETIME,
CUSTOMER_ID INT references CUSTOMERS(ID),
AMOUNT double,
PRIMARY KEY (ID)
);
如果 ORDERS 表已經(jīng)存在,并且沒(méi)有設(shè)置外鍵,那么可以使用下面的語(yǔ)法來(lái)修改數(shù)據(jù)表以指定外鍵。
ALTER TABLE ORDERS
ADD FOREIGN KEY (Customer_ID) REFERENCES CUSTOMERS (ID);
要?jiǎng)h除外鍵約束的話,語(yǔ)法如下所示:
ALTER TABLE ORDERS
DROP FOREIGN KEY;
CHECK 約束使用某一條件來(lái)對(duì)記錄中的值進(jìn)行檢查。如果條件最終為假(false),即約束條件不能得到滿足,則該記錄不能寫入數(shù)據(jù)表中。
例如,下述 SQL 語(yǔ)句創(chuàng)建了一個(gè)名為 CUSTOMERS 的新表,并為其添加了五個(gè)字段。在此,我們?yōu)?AGE 字段設(shè)置了 CHECK 約束,以拒絕任何年齡低于 18 的顧客:
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL CHECK (AGE >= 18),
ADDRESS CHAR (25) ,
SALARY DECIMAL (18, 2),
PRIMARY KEY (ID)
);
如果 CUSTOMERS 表已經(jīng)存在,再要為 AGE 字段設(shè)置 CHECK 約束的話,就需要像下面這樣寫 SQL 語(yǔ)句:
ALTER TABLE CUSTOMERS
MODIFY AGE INT NOT NULL CHECK (AGE >= 18 );
或者也可以使用下面的語(yǔ)法,該語(yǔ)法還支持對(duì)作用于多個(gè)字段的約束命名:
ALTER TABLE CUSTOMERS
ADD CONSTRAINT myCheckConstraint CHECK(AGE >= 18);
要?jiǎng)h除 CHECK 約束的話,請(qǐng)使用下面的 SQL 語(yǔ)句,不過(guò)該語(yǔ)句在 MySQL 中不起作用:
ALTER TABLE CUSTOMERS
DROP CONSTRAINT myCheckConstraint;
索引用于在數(shù)據(jù)庫(kù)中快速地創(chuàng)建和檢索數(shù)據(jù)。索引可以由表中的一個(gè)或者多個(gè)字段創(chuàng)建。創(chuàng)建索引時(shí),每一行都會(huì)獲得一個(gè) ROWID(在數(shù)據(jù)進(jìn)行排序之前)。
合理運(yùn)用索引可以提高大型數(shù)據(jù)庫(kù)的性能。但是,創(chuàng)建索引之前還是要三思而后行。為哪些字段創(chuàng)建索引,則取決于 SQL 查詢最常用到到哪些字段。
例如,下面的 SQL 語(yǔ)句創(chuàng)建了一個(gè)名為 CUSTOMERS 的新表,并為其添加了五個(gè)字段:
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR (25) ,
SALARY DECIMAL (18, 2),
PRIMARY KEY (ID)
);
現(xiàn)在,你就可以使用下面的語(yǔ)法來(lái)為一個(gè)或者多個(gè)字段創(chuàng)建索引了:
CREATE INDEX index_name
ON table_name ( column1, column2.....);
例如,可以在 AGE 字段上創(chuàng)建索引,以優(yōu)化對(duì)特定年齡的顧客的查詢,其語(yǔ)法如下所示:
CREATE INDEX idx_age
ON CUSTOMERS ( AGE );
要?jiǎng)h除索引約束的話,可以使用下面的 SQL 語(yǔ)句:
ALTER TABLE CUSTOMERS
DROP INDEX idx_age;
下面幾類數(shù)據(jù)完整性存在于各個(gè) RDBMS 中:
數(shù)據(jù)庫(kù)規(guī)范化指的是對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行有效組織的過(guò)程。對(duì)數(shù)據(jù)庫(kù)進(jìn)行規(guī)范化主要有兩個(gè)目的:
這兩個(gè)目標(biāo)都值得我們努力,因?yàn)樗鼈兛梢詼p少數(shù)據(jù)的空間占用,并確保了數(shù)據(jù)的邏輯完備。規(guī)范化包含一系列的指導(dǎo)方針,以幫助你創(chuàng)建出優(yōu)良的數(shù)據(jù)庫(kù)結(jié)構(gòu)。
規(guī)范化指導(dǎo)方針?lè)譃閹追N范式(form),你可以把范式想做是數(shù)據(jù)庫(kù)的格式或者其結(jié)構(gòu)的布局方式。使用范式的目標(biāo)是對(duì)數(shù)據(jù)庫(kù)結(jié)構(gòu)進(jìn)行整理,從而使其遵循第一范式,接著是第二范式,最終遵循第三范式。
要不要更進(jìn)一步到達(dá)第四范式、第五范式甚至更高的范式取決于你。一般來(lái)說(shuō),第三范式足矣。
第一范式設(shè)定了對(duì)數(shù)據(jù)庫(kù)進(jìn)行組織的最基本的規(guī)范:
你必須定義所需的數(shù)據(jù)項(xiàng)。這意味著查看要存儲(chǔ)的數(shù)據(jù),按照字段對(duì)其進(jìn)行組織,定義各個(gè)字段的數(shù)據(jù)類型,最終將相關(guān)的字段放在同一個(gè)表中。
例如,將所有與會(huì)議地點(diǎn)相關(guān)的字段放在 Location 表中,將所有同與會(huì)成員相關(guān)的字段放在 MemberDetails 表中等等。
下一步是保證不存在重復(fù)的數(shù)據(jù)集合??紤]如下的數(shù)據(jù)表:
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR (25),
ORDERS VARCHAR(155)
);
如果我們用同一個(gè)顧客的多筆訂單來(lái)填充該表,將會(huì)得到類似下面的數(shù)據(jù)表:
ID | NAME | AGE | ADDRESS | ORDERS |
100 | Sachin | 36 | Lower West Side | Cannon XL-200 |
100 | Sachin | 36 | Lower West Side | Battery XL-200 |
100 | Sachin | 36 | Lower West Side | Tripod Large |
但是,按照 1NF 我們必須保證沒(méi)有重復(fù)的數(shù)據(jù)集合。所以,可以將上表分成兩部分,然后使用一個(gè)鍵將兩個(gè)表連接起來(lái)。
CUSTOMERS 表:
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR (25),
PRIMARY KEY (ID)
);
表中記錄如下:
ID | NAME | AGE | ADDRESS |
100 | Sachin | 36 | Lower West Side |
ORDERS 表:
CREATE TABLE ORDERS(
ID INT NOT NULL,
CUSTOMER_ID INT NOT NULL,
ORDERS VARCHAR(155),
PRIMARY KEY (ID)
);
表中記錄如下:
ID | CUSTOMER_ID | ORDERS |
10 | 100 | Cannon XL-200 |
11 | 100 | Battery XL-200 |
12 | 100 | Tripod Large |
第一范式的最后一條規(guī)則是,為每一個(gè)數(shù)據(jù)表創(chuàng)建一個(gè)主鍵。
第二范式規(guī)定,數(shù)據(jù)表必須符合第一范式,并且所有字段與主鍵之間不存在部分依賴關(guān)系。
考慮顧客與訂單之間的關(guān)系,你可能會(huì)想要存儲(chǔ)顧客 ID、顧客姓名、訂單 ID、訂單明細(xì)以及購(gòu)買日期:
CREATE TABLE CUSTOMERS(
CUST_ID INT NOT NULL,
CUST_NAME VARCHAR (20) NOT NULL,
ORDER_ID INT NOT NULL,
ORDER_DETAIL VARCHAR (20) NOT NULL,
SALE_DATE DATETIME,
PRIMARY KEY (CUST_ID, ORDER_ID)
);
該表符合第一范式,因?yàn)樗鼭M足第一范式的所有規(guī)則。表中的主鍵有 CUST_ID 和 ORDER_ID。二者一起作為主鍵,我們假定同一個(gè)顧客不會(huì)購(gòu)買相同的東西。
然而,該表不符合第二范式,因?yàn)楸碇械淖侄魏椭麈I之間存在部分依賴關(guān)系。CUST_NAME 依賴于 CUST_ID,而 CUST_NAME 和所購(gòu)物品之間沒(méi)有直接的聯(lián)系。訂單明細(xì)和購(gòu)買日期依賴于 ORDER_ID,但是他們并不依賴于 CUST_ID,因?yàn)?CUST_ID 和 ORDER_DETAIL 以及 SALE_DATE 之間并不存在聯(lián)系。
要使該表遵守第二范式,你需要將其分為三個(gè)數(shù)據(jù)表。
首先,創(chuàng)建如下的數(shù)據(jù)表來(lái)保存客戶詳情:
CREATE TABLE CUSTOMERS(
CUST_ID INT NOT NULL,
CUST_NAME VARCHAR (20) NOT NULL,
PRIMARY KEY (CUST_ID)
);
接著創(chuàng)建一個(gè)表來(lái)存儲(chǔ)每個(gè)訂單的詳細(xì)信息:
CREATE TABLE ORDERS(
ORDER_ID INT NOT NULL,
ORDER_DETAIL VARCHAR (20) NOT NULL,
PRIMARY KEY (ORDER_ID)
);
最后,創(chuàng)建一個(gè)表來(lái)存儲(chǔ) CUST_ID 和 ORDER_ID 來(lái)記錄同一顧客的所有訂單:
CREATE TABLE CUSTMERORDERS(
CUST_ID INT NOT NULL,
ORDER_ID INT NOT NULL,
SALE_DATE DATETIME,
PRIMARY KEY (CUST_ID, ORDER_ID)
);
一個(gè)數(shù)據(jù)表符合第三范式,當(dāng)其滿足:
非主鍵字段之間的依賴關(guān)系存在于數(shù)據(jù)之中。例如下表中,街道(street)、城市(city)和省份(state)顯然與郵政編碼(zip Code)之間存在密不可分的關(guān)系。
CREATE TABLE CUSTOMERS(
CUST_ID INT NOT NULL,
CUST_NAME VARCHAR (20) NOT NULL,
DOB DATE,
STREET VARCHAR(200),
CITY VARCHAR(100),
STATE VARCHAR(100),
ZIP VARCHAR(12),
EMAIL_ID VARCHAR(256),
PRIMARY KEY (CUST_ID)
);
郵政編碼和地址之間的關(guān)系稱作傳遞相關(guān)性(transitive dependency)。要使得數(shù)據(jù)表符合第三范式,需要將街道、城市、省份等字段移到另一張表中,可以稱其為 Zip Code 表:
CREATE TABLE ADDRESS(
ZIP VARCHAR(12),
STREET VARCHAR(200),
CITY VARCHAR(100),
STATE VARCHAR(100),
PRIMARY KEY (ZIP)
);
接著,按照如下方式更改 CUSTOMERS 表:
CREATE TABLE CUSTOMERS(
CUST_ID INT NOT NULL,
CUST_NAME VARCHAR (20) NOT NULL,
DOB DATE,
ZIP VARCHAR(12),
EMAIL_ID VARCHAR(256),
PRIMARY KEY (CUST_ID)
);
移除傳遞相關(guān)性可以起到事半功倍的效果。首先是數(shù)據(jù)冗余度降低了,數(shù)據(jù)庫(kù)體積因此縮小。第二個(gè)好處是保證數(shù)據(jù)完整性。當(dāng)重復(fù)數(shù)據(jù)改變的時(shí)候,很有可能只更新部分?jǐn)?shù)據(jù),尤其是當(dāng)其分布在數(shù)據(jù)庫(kù)的各個(gè)地方的情況下。例如,如果地址和郵政編碼分別存儲(chǔ)在三個(gè)或者四個(gè)不同的數(shù)據(jù)表中,那么任何對(duì)郵編的改變,都需要對(duì)這三個(gè)或者四個(gè)表同時(shí)進(jìn)行更改。
更多建議: