1. 程式人生 > >表中已存重複資料的情況,如何增加唯一性約束?

表中已存重複資料的情況,如何增加唯一性約束?

這周某系統上線,有一個需求就是,為一張表修改唯一性約束,原因就是之前發現,由於唯一性約束設定不當,導致業務處理出現異常。

舉例來說,如下測試表,原先唯一性約束是a和b倆欄位,但發現實際業務中,a和b的組合是可能重複的,加上c欄位才會是唯一,

SQL> create table test(                                                     
  2  id number,
  3  a  varchar2(10),
  4  b  varchar2(10),
  5  c  varchar2(10));
Table created.

SQL> insert into test values(1, 'a', 'a', 'a');
1 row created.

SQL> insert into test values(2, 'b', 'b', 'b');
1 row created.

SQL> commit;
Commit complete.

SQL> select * from test;
    ID A          B      C
---------- ---------- ---------- ----------
     1 a          a      a
     2 b          b      b

基於以上資料,新建唯一性約束,可以看出,對於唯一性約束,Oracle會自動建立一個,普通的唯一索引,索引名稱預設採用約束名。

SQL> alter table test add constraint unq_test_01 unique(a, b, c);
Table altered.

SQL> select table_name, constraint_name, constraint_type from user_constraints where table_name='TEST';
TABLE_NAME CONSTRAINT_NAME    CONSTRAINT_TYPE
---------- ------------------------------ ------------------------------
TEST                UNQ_TEST_01              U

SQL> select table_name, index_name, index_type, uniqueness from user_indexes where table_name = 'TEST';
TABLE_NAME INDEX_NAME              INDEX_TYPE      UNIQUENESS
---------- ------------------------------ -------------------- ----------
TEST               UNQ_TEST_01             NORMAL            UNIQUE

Because the database enforces a unique constraint by implicitly creating or reusing an index on the key columns, the term unique key is sometimes incorrectly used as a synonym for unique key constraint or unique index.

確實插入(a, b, c)相同的資料,就會報唯一性約束錯誤,

SQL> insert into test values(3, 'a', 'a', 'a');
insert into test values(3, 'a', 'a', 'a')
*
ERROR at line 1:
ORA-00001: unique constraint (BISAL.UNQ_TEST_01) violated

這就完了麼?

需要注意一點,上述建立過程的前提,是表中已存在資料,沒有違反唯一性約束的,如果表中已存在資料,已經有重複資料,該如何處理?

我們刪除剛才建立的約束,插入重複記錄,此時表中存在(a, b, c)相同的記錄,

SQL> alter table test drop constraint unq_test_01;
Table altered.

SQL> insert into test values(3, 'a', 'a', 'a');
1 row created.

SQL> commit;
Commit complete.

SQL> select * from test;
    ID A          B      C
---------- ---------- ---------- ----------
     1 a          a      a
     2 b          b      b
     3 a          a      a

我們再用上面的方法,建立唯一性約束,可以看出,報了錯誤,提示資訊很明確,由於存在重複的鍵值,因此無法生效唯一性約束,

SQL> alter table test add constraint unq_test_01 unique(a, b, c);
alter table test add constraint unq_test_01 unique(a, b, c)
                                *
ERROR at line 1:
ORA-02299: cannot validate (BISAL.UNQ_TEST_01) - duplicate keys found

是否可以直接建立一個,唯一性索引?

SQL> create unique index idx_test_01 on test(a, b, c);
create unique index idx_test_01 on test(a, b, c)
                                   *
ERROR at line 1:
ORA-01452: cannot CREATE UNIQUE INDEX; duplicate keys found

這種情況下,最簡單的方法,就是刪除重複的記錄,這樣就可以按照正常流程,建立唯一性約束。但往往這些重複資料,有實際的業務意義,因此不能刪除,所以就需要其他方法workaround一下。

其實,Oracle官方手冊,就給了我們選擇,我們是可以控制,約束生效的狀態,

As part of constraint definition, you can specify how and when Oracle Database should enforce the constraint, thereby determining the constraint state.

The database enables you to specify whether a constraint applies to existing data or future data. If a constraint is enabled, then the database checks new data as it is entered or updated. Data that does not conform to the constraint cannot enter the database. If a constraint is disabled, then the table can contain rows that violate the constraint.

You can set constraints to validate (VALIDATE) or not validate (NOVALIDATE) existing data. If VALIDATE is specified, then existing data must conform to the constraint. You can set constraints to validate (VALIDATE) or not validate (NOVALIDATE) existing data. If VALIDATE is specified, then existing data must conform to the constraint.

The behavior of VALIDATE and NOVALIDATE always depends on whether the constraint is enabled or disabled.

簡言之,

如果約束設定enabled,則會檢查新插入或更新的資料是否符合約束條件。

如果約束設定disabled,則表中可以包含,違反約束的記錄。

如果約束設定validate,則表中存在的資料,必須符合約束。

如果約束設定novalidate,則表中存在的資料,不必符合約束。

validate和novalidate的行為,依賴於是否設定了enabled/disabled。

相應地可以設定組合,如下所示,

針對上面的需求,我們採用enable和novalidate的組合,是不是就可以解決問題了?

我們直接建立唯一性約束,報的相同錯誤,原因就是雖然此時,不檢查存在資料,是否符合約束,但由於需要自動建立,唯一性索引,卻發現存在重複的值,因此報錯。

SQL> alter table test add constraint unq_test_01 unique(a, b, c) enable novalidate;
alter table test add constraint unq_test_01 unique(a, b, c) enable novalidate
                                *
ERROR at line 1:
ORA-02299: cannot validate (BISAL.UNQ_TEST_01) - duplicate keys found

既然表中存在重複資料,就不能建立唯一性索引,只能是普通索引,但使用enable novalidate組合,可以設定約束,換句話說,利用唯一性約束,限制資料唯一性,同時有相應的非唯一索引,達到相同效果,

SQL> create index idx_test_01 on test(a, b, c);
Index created.

SQL> alter table test add constraint unq_test_01 unique(a, b, c) enable novalidate;
Table altered.

SQL> select table_name, constraint_name, constraint_type from user_constraints where table_name='TEST';

TABLE_NAME CONSTRAINT_NAME          CONSTRAINT_TYPE
---------- ------------------------------ ------------------------------
TEST       UNQ_TEST_01              U

SQL> select table_name, index_name, index_type, uniqueness from user_indexes where table_name = 'TEST';
TABLE_NAME INDEX_NAME             INDEX_TYPE             UNIQUENESS
---------- ------------------------- ------------------------------ ----------
TEST       IDX_TEST_01             NORMAL                NONUNIQU

可以看出,雖然表中存在重複資料,但新增資料,需要符合唯一性約束條件,符合我們的最初需求,

SQL> select * from test;
    ID A          B      C
---------- ---------- ---------- ----------
     1 a          a      a
     2 b          b      b
     3 a          a      a

SQL> insert into test values(4, 'b', 'b', 'b');
insert into test values(4, 'b', 'b', 'b')
*
ERROR at line 1:
ORA-00001: unique constraint (BISAL.UNQ_TEST_01) violated

再進一步,提一個問題,

存在唯一性約束的情況下,是否可以插入相同的空值?

看著好像簡單的一個問題,是不是有些猶豫?我們測試一下,就可以知道了。

測試表現在有(a, b, c)唯一性約束,此時插入兩條記錄,且三個欄位均為空值,分別用null和''兩種方法,插入空值資料,是可以插入的,並未違反唯一性約束,

SQL> insert into test values(4, null, null, null);
1 row created.

SQL> insert into test values(5, '', '', '');
1 row created.

SQL> select * from test;
    ID A          B      C
---------- ---------- ---------- ----------
     1 a          a      a
     2 b          b      b
     3 a          a      a
     4
     5

其實很容易理解,空值是一種特殊的值,表示不確定、未知,因此空值和空值比較,結果不會是true,唯一性約束,不認為兩個空值相等,所以可以插入兩個空值。

如果三欄位中有一個、兩個空值,可以看出,會報錯誤,

SQL> insert into test values(4, 'a', '', 'a');
1 row created.

SQL> insert into test values(5, 'a', '', 'a');
insert into test values(5, 'a', '', 'a')
*
ERROR at line 1:
ORA-00001: unique constraint (BISAL.UNQ_TEST_01) violated

SQL> insert into test values(4, '', '', 'a');
1 row created.

SQL> insert into test values(5, '', '', 'a');
insert into test values(5, '', '', 'a')
*
ERROR at line 1:
ORA-00001: unique constraint (BISAL.UNQ_TEST_01) violated

如果我們仔細看官方文件,就可以找出答案,

Unless a NOT NULL constraint is also defined, a null always satisfies a unique key constraint. Thus, columns with both unique key constraints and NOT NULLconstraints are typical. This combination forces the user to enter values in the unique key and eliminates the possibility that new row data conflicts with existing row data.

除非指定了非空約束,否則null值滿足唯一性約束。(準確地說,唯一性約束欄位均為null)

Because of the search mechanism for unique key constraints on multiple columns, you cannot have identical values in the non-null columns of a partially null composite unique key constraint.

含有部分空值的複合唯一性約束的非空列上不能有相同的值。

總結:

1. 表中不存在重複的資料,可以直接建立唯一性約束,Oracle會自動建立唯一性索引,索引名稱預設為約束名。

2. 表中已存在重複的資料,此時若需要建立唯一性約束,可以按照“建立非唯一索引”-“建立唯一性約束”的順序來實現。

3. 表中有唯一性約束的限制,若所有欄位均為null,則可以插入相同的空值,不違反唯一性約束,若複合唯一性約束,包含部分空值,且非空列上有相同的值,則違反唯一性約束。

如果您覺得此篇文章對您有幫助,歡迎關注微信公眾號:bisal的個人雜貨鋪,您的支援是對我最大的鼓勵!共同學習,共同進步:)

相關推薦

重複資料情況如何增加唯一性約束

這周某系統上線,有一個需求就是,為一張表修改唯一性約束,原因就是之前發現,由於唯一性約束設定不當,導致業務處理出現異常。舉例來說,如下測試表,原先唯一性約束是a和b倆欄位,但發現實際業務中,a和b的組合

sql 怎麼將A插入B去除兩張 含有的重複資料

insert into B(欄位列表) select 欄位列表 from A  where not exists(select * from B where a.keycol1 = b.keycol1) keycol1為A表和B 表中的欄位,可能帶有主鍵,可以此欄位來判斷A表和B表中是

將兩個非遞減的有序連結串列合併為一個非遞增的有序連結串列。要求結果連結串列仍使用原來兩個連結串列的儲存空間不另外佔用其他的儲存空間。允許有重複資料

語言:C++ 1、忽略了不另外佔用其他的儲存空間>< #include <iostream> using namespace std; typedef struct LNode { int data; LNode *next; }LNode,*LinkList;

建立和測試觸發器:向SC插入資料檢查插入資料的課程號是否在於Course(建立之前刪除Cno的外來鍵約束比較外來鍵約束和觸發器之間的不同)

alter table SC Drop constraint FK_SC--刪除外來鍵約束 CREATE TRIGGER trig_insert ON SC--在SC表中建立trig_insert觸發器 AFTER INSERT--insert為觸發事件,after則為觸發的時機

根據id在同一張查出兩條資料並將這兩條資料用“-”拼起來作為一條資料返回

  area表 id areaname 25 雲南省 417 玉溪市   要求:在area表中根據id查出兩條資料,將這兩條資料用"-"拼起

JDBC批量加密mysql有的密碼欄位資料

前言:       公司要求把mysql資料庫的user表中,把表中已有的使用者註冊的密碼資訊給加密處理,原本是沒有加密的,是明文。今天腦子也是迷迷糊糊的,所以程式碼呢,寫得有些low,也懶得改了,不過功能是實現了。 專案結構: 將原本的user表增加了兩個欄位,t

Mybatis需要返回的資料引數資料沒有對應的欄位自定義實體類和resultmap作為返回值型別

自定義實體類:因為需要做相關記錄的統計,而表中沒有統計欄位 public class TrafficJeevesDistrictCount { //施工top5+1 按區域 private String districtInfo; private

sql 刪除多餘的重複記錄(多個欄位)只保留一條記錄

在網上呢~自己收集了一些關於這方面的知識~  自己整理一下 1.查詢重複記錄 select * from 表名 where 重複欄位 in (select 重複欄位 from 表名 group

Mysql增加新列從另一個插入對應的資料

有測試表A(test_door)如下,新增doorName列,需要從測試表表B(tset_name)中更新對應的資料 測試表B如下 測試表A與doorId與表B Id欄位對應 如何更新?網

刪除多餘的重複記錄重複記錄是根據單個欄位(peopleId)來判斷只留有rowid最大的記錄

delete from bizdata_cwbj.cm_fin_fb1 j where j.syear = 2009 and (SCOMPANYCODE, SYEAR, SMONTH, SRPTYPE, S

開發過程遇到的內泄露情況如何解決的?

成了 內存 瀏覽器中 分享圖片 引用 處理 回收 內部 alt 1、定義和用法: 內存泄露是指一塊被分配的內存既不能使用,又不能回收,直到瀏覽器進程結束。C#和Java等語言采用了自動垃圾回收方法管理內存,幾乎不會發生內存泄露。我們知道,瀏覽器中也是采用自動垃圾回收方法管理

在Virtualbox的linux客戶端共享window的資料出現“mount:未知的檔案系統 `vboxsf'”情況

如果在linux 終端中輸入sudo mount -t  vboxsf “共享檔名” “linux下共享路徑”後出現如下提示 先在virtualbox的客戶端環境下安裝virtualbox addition 詳見http://blog.csdn.net/tjzhaome

jquery驗證後臺資料庫是否有重複資料比如註冊的時候需要

js // 定義績效專案新增表單的驗證規則 $("#itemAddForm").validate({ignore : "",rules : {"obj.name" : {required : true,maxlength: 10,remote : {async : fal

sqlserver數據庫 字段值有空格如何去除空格(例如char (5) 存入數據不足5位時sqlserver會自動補空格)

ltrim blog class 有時 找到 logs head 字段 div 普通的空格 前後的空格,使用LTrim和RTrim即可,例如:LTrim(RTrim(Name)) 中間的空格,使用replace函數替換,例如:Replace(Name,‘ ‘,‘‘)

Oracle 函數 “判斷數據不存在的數據才允許通過”

eight ret har replace 通過 color logs 信息 count() 1 create or replace function mca_detail_material_val(p_material_code VARCHAR2, --實參 2

SQL將用戶存在的數據所有姓名(漢字)轉換為拼音首字母

var 語句 ont htm bold varchar 實現 where into 實現方法: --函數 Create function [dbo].[fn_GetPy](@str nvarchar(4000)) returns nvarchar(4000) --

sql自增序列的新增刪除與修改

直接 src tar 綁定 後綁定 style com 屬性 新的 首先我們應該對Sequences,有個基本的概念: 主要是用來唯一標識,這樣方便查詢整條數據信息,主要的屬性有:自增量,最大值,最小值,初始值,所有者,自身名稱,關聯表名 1、重新設置序列開始的數字sql,

SqlServer執行Insert命令同時判斷目標是否存在目標資料

原文: SqlServer執行Insert命令同時判斷目標表中是否存在目標資料 針對於已查詢出資料結果, 且在程式中執行Sql命令, 而非資料庫中的儲存過程 INSERT INTO TableName (Column1, Column2, Column3, Column4, Column5)SELECT

MATLAB轉換TXT灰度值資料格式將灰度資料輸出為影象

在整理地圖測繪資料時,需要將資料先匯出,然後將資料轉化為影象資訊,使用MATLAB進行轉化遇到點問題,最後算是能出影象了,就將這一點經驗分享下。 首先看下匯出來的資料格式,匯出來的為TXT格式,其中橢圓是有效資料, 但是這種資料比較格魯的一點是,輸出的資料是40*400的