標籤:

談談唯一約束和唯一索引

最近在看資料庫相關知識,感覺唯一約束和唯一索引好像有點類似,於是研究了一番,於是就有了這篇文章。

概念

開始之前,先解釋一下約束和索引。

約束 全稱完整性約束,它是關係資料庫中的對象,用來存放插入到一個表中一列數據的規則,用來確保數據的準確性和一致性。

索引 資料庫中用的最頻繁的操作是數據查詢,索引就是為了加速表中數據行的檢索而創建的一種分散的數據結構。可以把索引類比成書的目錄,有目錄的肯定比沒有目錄的書,更方便查找。

唯一約束 保證在一個欄位或者一組欄位里的數據都與表中其它行的對應數據不同。和主鍵約束不同,唯一約束允許為 NULL,只是只能有一行。

唯一索引 不允許具有索引值相同的行,從而禁止重複的索引或鍵值。

唯一約束和唯一索引都是為了保證表中被限制的欄位不允許有重複的值,看起來功能是一樣的,那為什麼要設計這樣兩種一樣的功能呢?

探究

帶著這個問題,我在網上搜索了一番。

最開始,在 Oracle 的社區里看到了下面這個對話,原問題鏈接:

Unique Index vs. Unique Constraint

大概意思是說,他看了一本關於 Oracle 的書,書中說,唯一約束和唯一索引是不同的,但是書中沒解釋這兩個有什麼不同。

然後下面跟了一個答案如下

大意是說,約束和索引是不同的,約束為優化提供了更多信息,並且允許在唯一約束上建立外鍵,而唯一索引是不行的,然後還提供了一個小例子。

不能聽他說兩句就相信了,本著實踐出真理的原則,我做了下面的求證。儘管文章中提到的是 Oracle 資料庫,而我本地的是 MySQL,不過還是決定試一遍,按照他給出的例子,在本地做了如下測試。

首先創建兩個欄位值一樣的表 t1,t2,並為 t1 表中的 col1 列設置唯一約束。

CREATE TABLE t1 ( col1 INT(11), col2 VARCHAR(20), CONSTRAINT t1_uq UNIQUE (col1));CREATE TABLE t2 ( col1 INT(11), col2 VARCHAR(20));

運行結果

然後為表 t2 表中的 col1 列設置唯一索引

CREATE UNIQUE INDEX t2_idx ON t2 (col1);

運行結果

創建表 t3,並將 t1 表中的 col1 列設置為 t3 表中 col2 列的外鍵

CREATE TABLE t3 ( col1 INT(11), col2 INT(11), col3 VARCHAR(20), CONSTRAINT t3_fk FOREIGN KEY (col2) REFERENCES t1 (col1));

運行結果

創建表 t4,並將 t2 表中的 col1 列設置為 t4 表中 col2 列的外鍵

CREATE TABLE t4 ( col1 INT(11), col2 INT(11), col3 VARCHAR(20), CONSTRAINT t4_fk FOREIGN KEY (col2) REFERENCES t2 (col1));

重點來了,根據上面回答唯一約束和唯一索引的區別,t4 表應該是建不成功的,因為 t4 表中 col2 列依賴於 t2 表中 col1 列,而 t2 表中的 col1 列建立了唯一索引,並沒有建立唯一約束,因此 t4 表應該建立失敗。

然而,運行結果如下

是的,沒有看錯,表 t4 建立成功了,並沒有報錯,也沒有出現上面回答中提到的結果。

為什麼會這樣呢,首先想到的就是不同的資料庫對這一點的實現方式不同,Oracle 資料庫下會是這樣的區別,其它資料庫就不一定了。

正好,電腦上裝的有 SQL Server 2008,在 SQL Server 依次執行了一遍,也都成功了,沒出現上面提到的問題。

難道只有 Oracle 資料庫里才有那樣的區別,如果你電腦上剛好有 Oracle,可以幫我試一下。

再探求

難道唯一約束和唯一索引,在 MySQL 和 SQL Server 里真的一點區別都沒有嗎?

用 Navicat 打開剛剛在 MySQL 資料庫里建好的表,看下錶定義

表 t1 DDL

表 t2 DDL

表 t1 是直接在建表時對 col1 列定義唯一約束的,而表 t2 是建立完成後,通過修改表才對 col1 列建立唯一索引的。但是最終兩個表的 DDL 完全一樣,說明在 MySQL 資料庫里唯一約束和唯一索引只是概念不同,在不同的功能中叫法不同罷了,其實現方式是完全一樣的。

再次用 Navicat 打開剛剛在 SQL Server 資料庫里建好的表,看下錶定義

表 t1 DDL

表 t2 DDL

可以看出,和 MySQL 資料庫不同,SQL Server 資料庫下,表 t1 為 col1 列建立了唯一約束,表 t2 為 col1 列建立了唯一索引,但是表 t3 和 t4 也被成功建立了,可見最終的結果還是一樣,也即沒有證明上面那個回答。至於 SQL Server 下除此之外,還有沒有其它的區別,在我搜索的答案中暫時還沒發現,如果你發現了,歡迎回復交流。

總結

到此為止,基本上就能得出,唯一約束和唯一索引在 MySQL 資料庫里區別了

  1. 概念上不同,約束是為了保證數據的完整性,索引是為了輔助查詢;
  2. 創建唯一約束時,會自動的創建唯一索引;
  3. 在理論上,不一樣,在實際使用時,基本沒有區別。

關於第二條,MySQL 中唯一約束是通過唯一索引實現的,為了保證沒有重複值,在插入新記錄時會再檢索一遍,怎樣檢索快,當然是建索引了,所以,在創建唯一約束的時候就創建了唯一索引。


推薦閱讀:

MySQL中的 MyISAM 讀的效率高,InnoDB 寫的效率高,原理是什麼?(只針對這兩種存儲引擎的對比)
Mysql 知識點
知識布局-grafana-mysql
MySQL查詢優化器帶來的潛在問題
哪些互聯網公司的資料庫是選擇Oracle的,為什麼?

TAG:資料庫 | MySQL |