很多人比較糾結的約束和索引的關係
最近有不少朋友留言或者微信問我一個問題,出乎我的意料,問題竟然都是很相似的,所以我統一答覆一下。
之前寫了一篇文章 一個清理和查詢都要兼顧的簡單方案,看來很多朋友還是很關心這個分割槽索引中的唯一性約束是怎麼建立的。我舉個例子,當然內容中會貫穿說到你們的疑問,而且很可能你麼對於約束和索引還是存在這一些誤解。
首先我們建立一個測試表,為了簡單起見,也沒有注意很多的命名規則了。
create table test (id number,name varchar2(30))
partition by range(id)
(
partition p1 values less than(1),
partition p100 values less than (100),
partition p200 values less than(200)
);
我們就舉unique約束的例子吧。
新增約束有很多種語法來實現。比如下面的三種
alter table test modify(id unique);
alter table test add constraint con_test_id_uq unique(id);
alter table test add (constraint con_test_id_uq unique(id));
首先我們來簡單說明一個地方,就是generated的含義。
建立一個唯一性約束
alter table test modify(id unique);
這個時候檢視約束的時候,發現約束名是系統自動生成的。
SQL> select constraint_name,table_name from user_constraints where table_name='TEST';
CONSTRAINT_NAME TABLE_NAME
------------------------------ ------------------------------
SYS_C0031909 TEST
那麼對應的index的情況如何呢。發現也是自動生成的,這個時候欄位generated的含義就是約束名是系統自動生成的。
SELECT OWNER,CONSTRAINT_NAME,TABLE_NAME,GENERATED,INDEX_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='TEST';
OWNER CONSTRAINT_NAME TABLE_NAME GENERATED INDEX_NAME
------------------------------ ------------------------------ ------------------------------ -------------------- ------------------------------
TEST SYS_C0031909 TEST GENERATED NAME SYS_C0031909
好了,我們刪除這個約束,繼續測試,刪除的同時會把約束也一併刪除。
SQL> ALTER TABLE TEST DROP CONSTRAINT SYS_C0031909 CASCADE;
Table altered.
這個時候我們建立一個約束,指定約束名。當然下面兩種語法都沒有問題的。這就體現了Oracle語法解析器的強大之處。
alter table test add constraint con_test_id_uq unique(id);
--alter table test add (constraint con_test_id_uq unique(id));
當然建立出來約束之後,再來看看約束udev情況。
SQL> select constraint_name,table_name from user_constraints where table_name='TEST';
CONSTRAINT_NAME TABLE_NAME
------------------------------ ------------------------------
CON_TEST_ID_UQ TEST
檢視索引的情況,發現這個時候generated是user name,也就是使用者指定的名字。
SQL> SELECT OWNER,CONSTRAINT_NAME,TABLE_NAME,GENERATED,INDEX_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='TEST';
OWNER CONSTRAINT_NAME TABLE_NAME GENERATED INDEX_NAME
------------------------------ ------------------------------ ------------------------------ -------------------- ------------------------------
TEST CON_TEST_ID_UQ TEST USER NAME CON_TEST_ID_UQ
好了,到了大家關注的地方了,這個時候對這個分割槽表建立唯一性約束,預設是會建立一個唯一性索引,但是這個索引是一個全域性索引。檢視分割槽索引的資料字典檢視,沒有任何資訊可以佐證。
SQL> SELECT INDEX_NAME,PARTITION_NAME FROM USER_IND_PARTITIONS WHERE INDEX_NAME='CON_TEST_ID_UQ';
no rows selected
好了,這個時候我來矯正一些知識點,首先來說上面兩種建立約束的方式,從規範的角度來說應該是需要使用第二種的,約束名也是望名知義。小規則還是包含著大智慧。
然後對於建立Unique的本地約束,帶有本地索引該怎麼做呢,這個時候需要先建立索引,然後建立約束繫結起來。
首先刪除建立的測試約束,開始正式的建立。alter table test drop constraint con_test_id_uq;
建立本地索引。
create unique index ind_test_uq on test(id) local;
這個時候還沒有唯一性約束生成。
SQL> SELECT OWNER,CONSTRAINT_NAME,TABLE_NAME,GENERATED,INDEX_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='TEST';
no rows selected
然後建立約束,這個時候仔細觀察,其實會發現約束名和索引名是不同的。也是各司其職。
alter table test add constraint con_test_uq unique(id) using index ind_test_uq ;
SQL> SELECT OWNER,CONSTRAINT_NAME,TABLE_NAME,GENERATED,INDEX_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='TEST';
OWNER CONSTRAINT_NAME TABLE_NAME GENERATED INDEX_NAME
-------------------- ------------------------------ ------------------------------ -------------- ------------------------------
TEST CON_TEST_UQ TEST USER NAME IND_TEST_UQ
這個時候索引約束就對映起來了。如果直接刪除索引,會有下面的報錯。
SQL> DROP INDEX IND_TEST_UQ;
DROP INDEX IND_TEST_UQ
*
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key
對於這類的錯誤,尤其是生產環境中還是讓人非常頭痛的。
我們可以drop或者disable約束,然後刪除即可。
SQL> ALTER TABLE TEST DROP CONSTRAINT CON_TEST_UQ;
Table altered.
SQL> DROP INDEX IND_TEST_UQ;
Index dropped.
如果你覺得這種方式有些特別,那麼我們來看看tom怎麼說。在這一點上,其實Oracle也是建議做一個權衡,還是做到索引和約束的分離。
Oracle recommends that unique indexes be created explicitly, and not through enabling a unique constraint on a table.
Alternatively, you can define UNIQUE integrity constraints on the desired columns. Oracle enforces UNIQUE integrity constraints by automatically defining a unique index on the unique key. However, it is advisable that any index that exists for query performance, including unique indexes, be created explicitly.
當然如果覺得上面的描述有些長,來兩句簡單的。
A unique constraint does not necessarily create an index.
A unique constraint does not necessarily create a UNIQUE index.
如果還覺得不好理解,還有方法。
a) unique did not create an index
b) unique does not need a unique index
如果還不理解,我們做個小的實驗。
SQL> create table t( x int, y int );
Table created.
SQL> create index t_idx on t(x,y);
Index created.
SQL> alter table t add constraint t_unique unique(x);
Table altered.
SQL> select index_name,uniqueness,index_type from user_indexes where table_name ='T';
INDEX_NAME UNIQUENES INDEX_TYPE
------------------------------ --------- ---------------------------
T_IDX NONUNIQUE NORMAL
可以看到我們建立的壓根不是唯一性索引,但是可以和普通索引對映起來。
SQL> SELECT OWNER,CONSTRAINT_NAME,TABLE_NAME,GENERATED,INDEX_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='T'
OWNER CONSTRAINT_NAME TABLE_NAME GENERATED INDEX_NAME
------------------------------ ------------------------------ ------------------------------ -------------- ------------------------------
TEST T_UNIQUE T USER NAME T_IDX
明白了這一點,其實對於理解約束和索引的關係和關聯還是很有幫助的。
個人覺得為什麼索引和約束要分開,有一個很大的原因就是因為約束disable的情況下,索引會連帶刪除。
SQL> create table test2(id number,name varchar2(30));
Table created.
SQL> alter table test2 add constraint con_test2_id_uq unique(id);
Table altered.
插入一些資料。
SQL> insert into test2 select object_id,object_name from all_objects;
72888 rows created.
SQL> commit;
Commit complete.
索引和約束的關係如下:
SQL> select constraint_name,status,index_name from user_constraints where table_name='TEST2';
CONSTRAINT_NAME STATUS INDEX_NAME
------------------------------ -------- ------------------------------
CON_TEST2_ID_UQ ENABLED CON_TEST2_ID_UQ
把約束置為失效
SQL> alter table test2 disable constraint con_test2_id_uq;
Table altered.
這個時候再次檢視,索引已經沒有了蹤影,對應的段也不存在了。
SQL> select constraint_name,status,index_name from user_constraints where table_name='TEST2';
CONSTRAINT_NAME STATUS INDEX_NAME
------------------------------ -------- ------------------------------
CON_TEST2_ID_UQ DISABLED
如果這個表非常大,這種操作的影響還是非常的大的,如果是分割槽表的全域性索引那麼影響也是全域性性的。簡單點說還是殺傷力太大。個人見解而已。
之前寫了一篇文章 一個清理和查詢都要兼顧的簡單方案,看來很多朋友還是很關心這個分割槽索引中的唯一性約束是怎麼建立的。我舉個例子,當然內容中會貫穿說到你們的疑問,而且很可能你麼對於約束和索引還是存在這一些誤解。
首先我們建立一個測試表,為了簡單起見,也沒有注意很多的命名規則了。
create table test (id number,name varchar2(30))
partition by range(id)
(
partition p1 values less than(1),
partition p100 values less than (100),
partition p200 values less than(200)
);
我們就舉unique約束的例子吧。
新增約束有很多種語法來實現。比如下面的三種
alter table test modify(id unique);
alter table test add constraint con_test_id_uq unique(id);
alter table test add (constraint con_test_id_uq unique(id));
首先我們來簡單說明一個地方,就是generated的含義。
建立一個唯一性約束
alter table test modify(id unique);
這個時候檢視約束的時候,發現約束名是系統自動生成的。
SQL> select constraint_name,table_name from user_constraints where table_name='TEST';
CONSTRAINT_NAME TABLE_NAME
------------------------------ ------------------------------
SYS_C0031909 TEST
那麼對應的index的情況如何呢。發現也是自動生成的,這個時候欄位generated的含義就是約束名是系統自動生成的。
SELECT OWNER,CONSTRAINT_NAME,TABLE_NAME,GENERATED,INDEX_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='TEST';
OWNER CONSTRAINT_NAME TABLE_NAME GENERATED INDEX_NAME
------------------------------ ------------------------------ ------------------------------ -------------------- ------------------------------
TEST SYS_C0031909 TEST GENERATED NAME SYS_C0031909
好了,我們刪除這個約束,繼續測試,刪除的同時會把約束也一併刪除。
SQL> ALTER TABLE TEST DROP CONSTRAINT SYS_C0031909 CASCADE;
Table altered.
這個時候我們建立一個約束,指定約束名。當然下面兩種語法都沒有問題的。這就體現了Oracle語法解析器的強大之處。
alter table test add constraint con_test_id_uq unique(id);
--alter table test add (constraint con_test_id_uq unique(id));
當然建立出來約束之後,再來看看約束udev情況。
SQL> select constraint_name,table_name from user_constraints where table_name='TEST';
CONSTRAINT_NAME TABLE_NAME
------------------------------ ------------------------------
CON_TEST_ID_UQ TEST
檢視索引的情況,發現這個時候generated是user name,也就是使用者指定的名字。
SQL> SELECT OWNER,CONSTRAINT_NAME,TABLE_NAME,GENERATED,INDEX_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='TEST';
OWNER CONSTRAINT_NAME TABLE_NAME GENERATED INDEX_NAME
------------------------------ ------------------------------ ------------------------------ -------------------- ------------------------------
TEST CON_TEST_ID_UQ TEST USER NAME CON_TEST_ID_UQ
好了,到了大家關注的地方了,這個時候對這個分割槽表建立唯一性約束,預設是會建立一個唯一性索引,但是這個索引是一個全域性索引。檢視分割槽索引的資料字典檢視,沒有任何資訊可以佐證。
SQL> SELECT INDEX_NAME,PARTITION_NAME FROM USER_IND_PARTITIONS WHERE INDEX_NAME='CON_TEST_ID_UQ';
no rows selected
好了,這個時候我來矯正一些知識點,首先來說上面兩種建立約束的方式,從規範的角度來說應該是需要使用第二種的,約束名也是望名知義。小規則還是包含著大智慧。
然後對於建立Unique的本地約束,帶有本地索引該怎麼做呢,這個時候需要先建立索引,然後建立約束繫結起來。
首先刪除建立的測試約束,開始正式的建立。alter table test drop constraint con_test_id_uq;
建立本地索引。
create unique index ind_test_uq on test(id) local;
這個時候還沒有唯一性約束生成。
SQL> SELECT OWNER,CONSTRAINT_NAME,TABLE_NAME,GENERATED,INDEX_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='TEST';
no rows selected
然後建立約束,這個時候仔細觀察,其實會發現約束名和索引名是不同的。也是各司其職。
alter table test add constraint con_test_uq unique(id) using index ind_test_uq ;
SQL> SELECT OWNER,CONSTRAINT_NAME,TABLE_NAME,GENERATED,INDEX_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='TEST';
OWNER CONSTRAINT_NAME TABLE_NAME GENERATED INDEX_NAME
-------------------- ------------------------------ ------------------------------ -------------- ------------------------------
TEST CON_TEST_UQ TEST USER NAME IND_TEST_UQ
這個時候索引約束就對映起來了。如果直接刪除索引,會有下面的報錯。
SQL> DROP INDEX IND_TEST_UQ;
DROP INDEX IND_TEST_UQ
*
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key
對於這類的錯誤,尤其是生產環境中還是讓人非常頭痛的。
我們可以drop或者disable約束,然後刪除即可。
SQL> ALTER TABLE TEST DROP CONSTRAINT CON_TEST_UQ;
Table altered.
SQL> DROP INDEX IND_TEST_UQ;
Index dropped.
如果你覺得這種方式有些特別,那麼我們來看看tom怎麼說。在這一點上,其實Oracle也是建議做一個權衡,還是做到索引和約束的分離。
Oracle recommends that unique indexes be created explicitly, and not through enabling a unique constraint on a table.
Alternatively, you can define UNIQUE integrity constraints on the desired columns. Oracle enforces UNIQUE integrity constraints by automatically defining a unique index on the unique key. However, it is advisable that any index that exists for query performance, including unique indexes, be created explicitly.
當然如果覺得上面的描述有些長,來兩句簡單的。
A unique constraint does not necessarily create an index.
A unique constraint does not necessarily create a UNIQUE index.
如果還覺得不好理解,還有方法。
a) unique did not create an index
b) unique does not need a unique index
如果還不理解,我們做個小的實驗。
SQL> create table t( x int, y int );
Table created.
SQL> create index t_idx on t(x,y);
Index created.
SQL> alter table t add constraint t_unique unique(x);
Table altered.
SQL> select index_name,uniqueness,index_type from user_indexes where table_name ='T';
INDEX_NAME UNIQUENES INDEX_TYPE
------------------------------ --------- ---------------------------
T_IDX NONUNIQUE NORMAL
可以看到我們建立的壓根不是唯一性索引,但是可以和普通索引對映起來。
SQL> SELECT OWNER,CONSTRAINT_NAME,TABLE_NAME,GENERATED,INDEX_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='T'
OWNER CONSTRAINT_NAME TABLE_NAME GENERATED INDEX_NAME
------------------------------ ------------------------------ ------------------------------ -------------- ------------------------------
TEST T_UNIQUE T USER NAME T_IDX
明白了這一點,其實對於理解約束和索引的關係和關聯還是很有幫助的。
個人覺得為什麼索引和約束要分開,有一個很大的原因就是因為約束disable的情況下,索引會連帶刪除。
SQL> create table test2(id number,name varchar2(30));
Table created.
SQL> alter table test2 add constraint con_test2_id_uq unique(id);
Table altered.
插入一些資料。
SQL> insert into test2 select object_id,object_name from all_objects;
72888 rows created.
SQL> commit;
Commit complete.
索引和約束的關係如下:
SQL> select constraint_name,status,index_name from user_constraints where table_name='TEST2';
CONSTRAINT_NAME STATUS INDEX_NAME
------------------------------ -------- ------------------------------
CON_TEST2_ID_UQ ENABLED CON_TEST2_ID_UQ
把約束置為失效
SQL> alter table test2 disable constraint con_test2_id_uq;
Table altered.
這個時候再次檢視,索引已經沒有了蹤影,對應的段也不存在了。
SQL> select constraint_name,status,index_name from user_constraints where table_name='TEST2';
CONSTRAINT_NAME STATUS INDEX_NAME
------------------------------ -------- ------------------------------
CON_TEST2_ID_UQ DISABLED
如果這個表非常大,這種操作的影響還是非常的大的,如果是分割槽表的全域性索引那麼影響也是全域性性的。簡單點說還是殺傷力太大。個人見解而已。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23718752/viewspace-1975057/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- set unused column和檢視,約束,同義詞和索引的關係索引
- SQL Server約束和DML觸發器的比較SQLServer觸發器
- NULL和唯一約束UNIQUE的對應關係Null
- 索引的分析和比較索引
- Unique約束,Primary Key約束與索引的關係學習與測試_20091213.doc索引
- MySQL中的 UNIQUE約束和UNIQUE索引MySql索引
- 全域性索引和本地索引的比較索引
- 【索引】oracle查詢使用索引和不使用索引的比較索引Oracle
- 資料關係比較:相關性 vs 因果關係
- Oracle - 約束、索引等相關常用操作語句Oracle索引
- 理清JS中等於(==)和全等(===)那些糾纏不清的關係JS
- Oracle主鍵約束、唯一鍵約束、唯一索引的區別(轉)Oracle索引
- 用函式索引構造特殊的約束函式索引
- Oracle如何管理帶約束的B樹索引Oracle索引
- 建約束(Constraint)時隱式建立索引(Index)及先建立索引後建立約束的區別AI索引Index
- [轉]Oracle分割槽索引--本地索引和全域性索引比較Oracle索引
- Oracle分割槽索引--本地索引和全域性索引比較(轉)Oracle索引
- 唯一性約束和唯一性索引的區別索引
- Mysql中的Btree與Hash索引比較MySql索引
- 比較有索引和無索引的查詢速度(在mysql資料庫中)索引MySql資料庫
- 關係型資料庫和非關係型資料庫介紹及優劣勢比較資料庫
- 資料庫——一些復健(主要是約束和索引相關練習)資料庫索引
- 查詢oracle表的資訊(表,欄位,約束,索引)Oracle索引
- SQL SERVER中找出拙劣的約束,索引,外來鍵SQLServer索引
- MYSQL order by排序與索引關係總結MySql排序索引
- multi-key索引和wildCard索引場景比較索引
- MySQL 中的約束及相關操作MySql
- 支援向量機(SVM)的約束和無約束優化、理論和實現優化
- query rewrite和基於函式的索引有關係?函式索引
- SQL優化--多表連線和走索引的關係SQL優化索引
- oracle外來鍵約束的總結Oracle
- rman中RETENTION POLICY和BACKUP OPTIMIZATION的制約關係!
- 【索引】Bitmap點陣圖索引與普通的B-Tree索引鎖的比較索引
- 解決資料庫的索引亂碼問題,先刪除外來鍵約束,再刪除主鍵約束及其索引資料庫索引
- Oracle中檢視無效的物件、約束、觸發器和索引(Helloblock寫作)Oracle物件觸發器索引BloC
- 系統關鍵程式的檢視和比較
- 關於糾結的recycle pool的設定
- c#中結構體和類的比較C#結構體