违反唯一性约束的两种可能:唯一约束or唯一索引
ORA-00001: 违反唯一约束条件 这一个报错相信大家在插入数据时还是经常遇到的,尤其是在测试环境。
但是今天我在处理一个生产问题的时候再次遇到这个报错时有点奇怪:
1.该表(记为表A)的主键是数据库序列生成的,不存在测试环境常见的因为插入测试数据导致后续通过数据库序列生成的主键冲突的问题
2.检查了数据库表结构的约束,发现只有主键约束。
然后检查了插入数据的SQL,如下:
MERGE INTO EIS_INSU_FILE_CABIN A
USING (SELECT BUSI_ID, APPLY_ID, MD5_KEY, CAFILE_PATH, CAFILE_NAME
FROM EIS_INSU_SIGN_INFO A
WHERE A.SIGN_CATE = 31
AND A.BUSI_ID = '5800'
AND A.APPLY_ID = '5800'
AND A.SIGN_STATUS IN (3, 4)) B
ON (A.BUSI_ID = B.BUSI_ID AND A.APPLY_ID = B.APPLY_ID AND A.FILE_TYPE = 220)
WHEN MATCHED THEN
UPDATE
SET A.FILE_PATH = B.CAFILE_PATH,
A.FILE_NAME = B.CAFILE_NAME,
A.MD5_KEY = B.MD5_KEY
WHEN NOT MATCHED THEN
INSERT
(FILE_ID,
BUSI_CATE,
BUSI_ID,
APPLY_ID,
FILE_TYPE,
PAGE_NO,
FILE_PATH,
FILE_NAME,
MD5_KEY)
VALUES
(SEIS_INSU_FILE_CABIN_ID.NEXTVAL,
6,
B.BUSI_ID,
B.APPLY_ID,
220,
1,
B.CAFILE_PATH,
B.CAFILE_NAME,
B.MD5_KEY);
因为这个SQL还涉及到对MERGE INTO语法的理解,简单来说就是下面这个查询脚本的查询结果如果不为空就对查询结果进行更新,如果查询结果为空就进行插入,但是插入的条数实际是与B中的结果条数有关(此处为2条)
SELECT A.*
FROM EIS_INSU_FILE_CABIN A,
(SELECT BUSI_ID, APPLY_ID, MD5_KEY, CAFILE_PATH, CAFILE_NAME
FROM EIS_INSU_SIGN_INFO A
WHERE A.SIGN_CATE = 31
AND A.BUSI_ID = '5800'
AND A.APPLY_ID = '5800'
AND A.SIGN_STATUS IN (3, 4)) B
WHERE A.BUSI_ID = B.BUSI_ID
AND A.APPLY_ID = B.APPLY_ID
AND A.FILE_TYPE = 220;
尽管要插入两条数据,但从脚本可以看出,主键是序列生成的。对于这样的主键策略,如果有主键冲突的话,通常是查询到序列后,在应用程序中做了处理,然后再插入数据库中可能会存在问题。
直接在数据库中进行insert应该是不会有这个问题的。
再进一步排查,发现在索引上除了主键索引的类型是Unique之外,还有一个索引也是Unique(这个时候才发现日志其实已经提示很明确了,unique constraint (EINSU_MAIN.UNI_EIS_EIS_INSU_APP_FILE__BUSI2)violated 我还自以为是的以为是主键冲突)
以前我添加表约束中的唯一性约束都是通过类似下面这样的语句去添加,或者在建表的时候添加。
ALTER TABLE table ADD CONSTRAINT uq_col UNIQUE(col);
注:若唯一性约束涉及多列,则只能通过表级约束添加
经百度,才知道除了有唯一约束还有唯一索引
为对比二者的差别,做了一个简单的实验
CREATE TABLE TEMP001(SID NUMBER(8, 0),
NAME VARCHAR2(10),
SEX CHAR(2),
BIRTHDAY DATE,
EMAIL VARCHAR2(20),
DEPID NUMBER(5, 0));
ALTER TABLE TEMP001 ADD CONSTRAINT TEMP001_UQ UNIQUE(SID);
CREATE UNIQUE INDEX UNI_IDX_EMAIL ON TEMP001(EMAIL);
可以看出,建立唯一约束后默认会建立一个同名的唯一索引,也就是唯一约束实际是通过唯一索引实现的。
禁用唯一约束ALTER TABLE TEMP001 DISABLE CONSTRAINT TEMP001_UQ;
后:
可以看出禁用唯一约束后唯一索引被删除了。
其他在满足列值唯一的效果上没有什么区别