四、UNIQUE ---- 唯一約束
唯一鍵可以是單個字段,也可以是多個字段的組合,設置唯一約束后,INSERT或UPDATE時如果表中唯一鍵字段中已存在該數據,則拒絕該行數據的INSERT或UPDATE。但是數據庫中NULL並不等於NULL,所以唯一鍵中如果沒有NOT NULL約束,則可以在唯一鍵中INSERT或UPDATE任意多個NULL。
1.創建測試表
唯一約束為組合鍵(a,b),即a和b的組合必須是唯一的。
create table tbl_unique( a int not null, b int, c varchar(10) not null default 'catch u', constraint uk_tbl_unique_a_b unique(a,b) );
向tbl_unique表中寫入數據(1,,1,'test')
test=# insert into tbl_unique (a,b,c) values(1,1,'test'); INSERT 0 1
再次寫入(a,b)組合(1,1)時,則會返回錯誤。
test=# insert into tbl_unique (a,b,c) values(1,1,'u see'); ERROR: duplicate key value violates unique constraint "uk_tbl_unique_a_b" DETAIL: Key (a, b)=(1, 1) already exists.
那么唯一鍵中出現NULL呢?唯一鍵中可以寫入任意多個NULL!
test=# insert into tbl_unique (a) values(2); INSERT 0 1 test=# insert into tbl_unique (a) values(2); INSERT 0 1 test=# insert into tbl_unique (a) values(2); INSERT 0 1 test=# \pset null 'NULL' Null display is "NULL". test=# select * from tbl_unique ; a | b | c ---+------+--------- 1 | 1 | test 2 | NULL | catch u 2 | NULL | catch u 2 | NULL | catch u (4 rows)
2.唯一鍵約束刪除
test=# alter table tbl_unique drop constraint uk_tbl_unique_a_b ; ALTER TABLE
3.唯一鍵約束增加
如果你想向表中增加唯一約束,必須要考慮表中已存在的數據可能存在重復數據。重復的數據有兩種理解方式:
方式一:嚴格意義上的唯一,NULL不等於NULL,即(1,NULL)和(1,NULL)不是重復數據。
方式二:非嚴格意義上的唯一,NULL等於NULL,即(1,NULL)和(1,NULL)是重復數據。
所以向表中增加唯一約束必須要刪除這些重復數據,或者將重復數據刪除到唯一。
情況一:刪除嚴格意義上的重復
第一步:清空測試表,寫入一些測試數據。
test=# delete from tbl_unique ; DELETE 4 test=# insert into tbl_unique (a,b) values (1,1),(1,1),(1,1); INSERT 0 3 test=# insert into tbl_unique (a) values (2),(2),(2); INSERT 0 3 test=# select * from tbl_unique ; a | b | c ---+------+--------- 1 | 1 | catch u 1 | 1 | catch u 1 | 1 | catch u 2 | NULL | catch u 2 | NULL | catch u 2 | NULL | catch u (6 rows)
從結果中看,嚴格意義上的唯一有1個(1,1,'catch u')和3個(2,NULL,'catch u'),刪除重復數據即是要刪除所有的(1,1,'catch u')。這種情況下只要使用下面的語句刪除即可。
delete from tbl_unique where a= 1 and b = 1;
但是如果表中存在成千上萬個這種重復數據,這么一條一條的刪除豈不顯得低級?!
第二步:查詢(a,b)存在重復的數據
test=# select a,b from tbl_unique where a is not null and b is not null group by a,b having count(*) > 1; a | b ---+--- 1 | 1 (1 row)
第三步:查詢所有(a,b)重復的數據
test=# select * from tbl_unique where exists(select null from (select a,b from tbl_unique where a is not null and b is not null group by a,b having count(*) > 1)tbl_temp where tbl_temp.a=tbl_unique.a and tbl_temp.b=tbl_unique.b) ; a | b | c ---+---+--------- 1 | 1 | catch u 1 | 1 | catch u 1 | 1 | catch u (3 rows)
第四步:刪除所有(a,b)重復的數據
把上面的語句中select *替換成 delete就可以了。
test=# delete from tbl_unique where exists(select null from (select a,b from tbl_unique where a is not null and b is not null group by a,b having count(*) > 1)tbl_temp where tbl_temp.a=tbl_unique.a and tbl_temp.b=tbl_unique.b) ; DELETE 3 test=# select * from tbl_unique ; a | b | c ---+------+--------- 2 | NULL | catch u 2 | NULL | catch u 2 | NULL | catch u (3 rows)
第五步:增加唯一約束
test=# alter table tbl_unique add constraint uk_tbl_unique_a_b unique (a,b); ALTER TABLE
情況二:刪除非嚴格意義重復數據
第一步:刪除約束,清空數據,寫入測試數據
test=# alter table tbl_unique drop constraint uk_tbl_unique_a_b ; ALTER TABLE test=# delete from tbl_unique ; DELETE 3 test=# insert into tbl_unique (a,b) values (1,1),(1,1),(1,1); INSERT 0 3 test=# insert into tbl_unique (a) values (2),(2),(2); INSERT 0 3 test=# select * from tbl_unique ; a | b | c ---+------+--------- 1 | 1 | catch u 1 | 1 | catch u 1 | 1 | catch u 2 | NULL | catch u 2 | NULL | catch u 2 | NULL | catch u (6 rows)
非嚴格意義上該表中的數據全部是重復數據,和情況一比只需要把NOT NULL過濾條件去掉即可。
第二步:查詢(a,b)重復數據
test=# select a,b from tbl_unique group by a,b having count(*) > 1; a | b ---+------ 2 | NULL 1 | 1 (2 rows)
第三步:查詢所有(a,b)重復數據
test=# select * from tbl_unique where exists(select null from (select a,b from tbl_unique group by a,b having count(*) > 1)tbl_temp where (tbl_temp.a=tbl_unique.a and tbl_temp.b=tbl_unique.b) or (tbl_temp.a is null and tbl_unique.a is null) or (tbl_temp.b is null and tbl_unique.b is null)) ; a | b | c ---+------+--------- 1 | 1 | catch u 1 | 1 | catch u 1 | 1 | catch u 2 | NULL | catch u 2 | NULL | catch u 2 | NULL | catch u (6 rows)
第四步:刪除所有(a,b)重復數據
同樣把上面語句的select * 替換成delete即可。
test=# delete from tbl_unique where exists(select null from (select a,b from tbl_unique group by a,b having count(*) > 1)tbl_temp where (tbl_temp.a=tbl_unique.a and tbl_temp.b=tbl_unique.b) or (tbl_temp.a is null and tbl_unique.a is null) or (tbl_temp.b is null and tbl_unique.b is null)) ; DELETE 6 test=# select * from tbl_unique ; a | b | c ---+---+--- (0 rows)
第五步:增加唯一鍵約束
test=# alter table tbl_unique add constraint uk_tbl_unique_a_b unique (a,b); ALTER TABLE
如果表中沒有主鍵或NOT NULL的唯一鍵,那么可以利用表的OID屬性,將表的oid列顯示出來,該列類似主鍵的功能。利用該列,可以將重復數據刪除到只剩一條,先使用下面的SQL語句,修改表的屬性。
test=# alter table tbl_unique set with oids; ALTER TABLE
情況三:將嚴格意義上重復數據刪除到只有一條
第一步:刪除表約束,清空表,寫入測試數據
test=# alter table tbl_unique drop constraint uk_tbl_unique_a_b ; ALTER TABLE test=# delete from tbl_unique ; DELETE 0 test=# insert into tbl_unique (a,b) values (1,1),(1,1),(1,1); INSERT 0 3 test=# insert into tbl_unique (a) values (2),(2),(2); INSERT 0 3 test=# select oid,* from tbl_unique ; oid | a | b | c -------+---+------+--------- 16399 | 1 | 1 | catch u 16400 | 1 | 1 | catch u 16401 | 1 | 1 | catch u 16402 | 2 | NULL | catch u 16403 | 2 | NULL | catch u 16404 | 2 | NULL | catch u (6 rows)
嚴格意義上的重復數據是3條(1,1,'catch u'),現在要將三條的重復數據,刪除到只剩一條。
第二步:查詢重復數據的最小oid
test=# select min(oid) from tbl_unique where a is not null and b is not null group by a,b; min ------- 16399 (1 row)
第三步:查詢oid不是最小的重復數據
test=# select oid,* from tbl_unique where oid not in(select min(oid) from tbl_unique where a is not null and b is not null group by a,b) and a is not null and b is not null; oid | a | b | c -------+---+---+--------- 16400 | 1 | 1 | catch u 16401 | 1 | 1 | catch u (2 rows)
第四步:刪除oid不是最小的重復數據
把上面的SQL語句中select替換成delete即可。
test=# delete from tbl_unique where oid not in(select min(oid) from tbl_unique where a is not null and b is not null group by a,b) and a is not null and b is not null; DELETE 2 test=# select oid,* from tbl_unique ; oid | a | b | c -------+---+------+--------- 16399 | 1 | 1 | catch u 16402 | 2 | NULL | catch u 16403 | 2 | NULL | catch u 16404 | 2 | NULL | catch u (4 rows)
第五步:增加唯一鍵約束
test=# alter table tbl_unique add constraint uk_tbl_unique_a_b unique (a,b); ALTER TABLE
情況四:將非嚴格意義上重復數據刪除到只有一條
第一步:刪除唯一約束,清空表,寫入測試數據
test=# alter table tbl_unique drop constraint uk_tbl_unique_a_b ; ALTER TABLE test=# delete from tbl_unique ; DELETE 4 test=# insert into tbl_unique (a,b) values (1,1),(1,1),(1,1); INSERT 0 3 test=# insert into tbl_unique (a) values (2),(2),(2); INSERT 0 3 test=# select oid,* from tbl_unique ; oid | a | b | c -------+---+------+--------- 16407 | 1 | 1 | catch u 16408 | 1 | 1 | catch u 16409 | 1 | 1 | catch u 16410 | 2 | NULL | catch u 16411 | 2 | NULL | catch u 16412 | 2 | NULL | catch u (6 rows)
第二步:查詢重復數據的最小oid
test=# select min(oid) from tbl_unique group by a,b; min ------- 16410 16407 (2 rows)
第三步:查詢oid不是最小的重復數據
test=# select oid,* from tbl_unique where oid not in(select min(oid) from tbl_unique group by a,b); oid | a | b | c -------+---+------+--------- 16408 | 1 | 1 | catch u 16409 | 1 | 1 | catch u 16411 | 2 | NULL | catch u 16412 | 2 | NULL | catch u (4 rows)
第四步:刪除oid不是最小的重復數據
把上面的SQL語句中select替換成delete即可。
test=# delete from tbl_unique where oid not in(select min(oid) from tbl_unique group by a,b); DELETE 4 test=# select oid,* from tbl_unique ; oid | a | b | c -------+---+------+--------- 16407 | 1 | 1 | catch u 16410 | 2 | NULL | catch u (2 rows)
第五步:增加唯一鍵約束
test=# alter table tbl_unique add constraint uk_tbl_unique_a_b unique (a,b); ALTER TABLE
