表中已存重复数据的情况,如何增加唯一性约束?

这周某系统上线,有一个需求就是,为一张表修改唯一性约束,原因就是之前发现,由于唯一性约束设置不当,导致业务处理出现异常。

举例来说,如下测试表,原先唯一性约束是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的个人杂货铺,您的支持是对我最大的鼓励!共同学习,共同进步:)

时间: 2024-10-07 04:04:17

表中已存重复数据的情况,如何增加唯一性约束?的相关文章

oracle数据库表中在没有主键的情况下如何删除重复记录

问题描述 oracle数据库表中在没有主键的情况下如何删除重复记录 数据库表没有主键,没有唯一性约束,如何删除重复记录呢?求大神解答. 解决方案 http://www.cosdiv.com/page/M0/S505/505957.htmlhttp://www.jb51.net/article/35593.htmhttp://www.newhua.com/2012/0106/141377.shtml 上面几篇文章你可以点击进去看看. 如果回答对你有帮助请采纳 解决方案二: delete from

如何向购物车表中插入多条数据,如果购物车中存在该数据,就只更新数量

问题描述 购物车表,向其中添加多条数据(Oracle数据库),使用insert into tablename select 'aa','bb' from dual union allselect 'aa1','bb1' from dual 但是现在的需求是,添加多条数据, 1.如果表中不存在这些数据,就添加, 2.如果存在就更新数量.这样的需求如何高效的实现了.请教大侠门.问题补充:谢谢各位.我获得一个connection后,创建一个statment,循环使用stmt.executeQuery(

mysql 数据表合并并且只重复数据只读取一条

mysql教程 数据表合并并且只重复数据只读取一条 //首先我们来盾mysql sql结构了,用了union联合查询与insert into 来整表复制了. mysql_connect("localhost","root","root") or die('Database Server Uid or Password Error!'); mysql_select_db('ip') or die('data !'); mysql_query(&qu

xcode-获取表中列的全部数据

问题描述 获取表中列的全部数据 代码我想要获取所有引用数据,一共有320个引用.但是只得到了第一个引用. -(NSMutableArray *)getAllQuotesData { NSMutableArray *quotesArray = [[NSMutableArray alloc] init]; NSString *sqlStr = [NSString stringWithFormat:@"SELECT quote FROM quotes"]; sqlite3_stmt *Ret

复制数据库表中两个字段数据的SQL语句

复制数据库表中两个字段数据的SQL语句 今天为表新添加一个字段,但又想与表中的另一个字段值相同,由于数据过多想通过sql语句实现,经测试下面的这句话确实很好用.   复制代码 代码如下: update jb51_temp set B=A jb51_temp 代表表明 A是有数值的字段,B是新添加的字段 ,记住空值表放到前面,如果set a=b,那么你的数值都为空了,就麻烦了,建议操作前备份数据库. 如何将一个字段里的值复制添加到另一个字段中 比如,有二个字段A和B,A字段是值是"我",

mysql表关联只取关联表中最近一条数据

问题描述 mysql表关联只取关联表中最近一条数据 表A{id,userId,logTime} 表B{id,userId,departmentId,updateTime} 表A为数据记录 表B相当于历史记录表(userId在updateTime时间之前的departmentId) 现在表A与表B关联 但是只能让表B中符合条件的最近一记录关联上 (也是updateTime在logTime之前并且最近的一条) 我是这么做的 SELECT A.id,B.userId,B.departmentId FR

代码-C#程序修改Excel表中某列的数据

问题描述 C#程序修改Excel表中某列的数据 本人是编程草鸟,求各位大神欧巴给出完整代码,nopi什么的各种不会,搜索度娘大半天一点也看不懂.求大神谢谢! 解决方案 复制粘贴会么?http://zhidao.baidu.com/link?url=dTzKUW9sTVedc0JVDUBITHmDezaPJ6XBUQPSYjFNQX4ilqsu-yEuROWB8Deq4lvIc_2MUWloky2v6eIPldIZzc_BfHazRxSvxiXFIeNf3FS 解决方案二: 下载npoi的库,添加

struct-结构体初始化问题, 定义了结构体和结构体数组,结构体中想存的数据写在了txt文件中,我该怎么做初始化

问题描述 结构体初始化问题, 定义了结构体和结构体数组,结构体中想存的数据写在了txt文件中,我该怎么做初始化 定义了结构体和结构体数组,结构体中想存的数据写在了txt文件中,我该怎么做初始化//定义结构体//struct achievement{int number; char name[20]; char sex; float achievement1achievement2achievement3;}tab_achievement[N]; 解决方案 写一个循环,从txt中读取数据,然后赋值

java-mysql表中改变一列数据的编号怎么让同编号的一行数据跟着变化?

问题描述 mysql表中改变一列数据的编号怎么让同编号的一行数据跟着变化? mysql表中改变一列数据的编号怎么让同编号的一行数据跟着变化?就是例如每个时间对应一行数据 我把时间列的顺序改变后 每个时间对应的数据还是不变 解决方案 用修改触发器实现