条件唯一约束 SQL
Conditional Unique Constraint SQL
有没有办法在给定条件为真时忽略唯一约束?
例如,我的数据库中有 3 列构成了唯一约束:
create table example_table
(
column_primarykey RAW(16) default NULL not null,
column_a number(8) not null,
column_b number(8) not null,
column_c number(8) not null,
constraint constraint_1
unique(column_a, column_b, column_c)
constraint constraint_2
primary key (column_primarykey)
现在我添加第四列:
alter table example_table
add column_d number(8) not null,
我想要实现的是,如果 column_d 的值已经存在于 table 中,那么唯一约束将被忽略。如果 column_d 在 table 中不是唯一的,则忽略唯一约束,您可以将行添加到 table。例如,这是我 table 中的现有数据(忽略主键原因不相关):
column_a
column_a
column_c
column_d
1
2
3
1
3
4
5
2
我想要的是您可以添加例如 (1, 2, 3, 1) 但不能添加 (1, 2, 3, 2),因为已经有包含前三个值的行。仅当 column_d 中的值已经存在并且其他值等于现有行时才可能。
更多有助于理解的示例:
Example insert
result
reason
(1, 2, 3, 1)
accepted
d is not unique and a, b, c got same values as the existing row with value 1 for column_d
(1, 2, 3, 4)
rejected
a, b ,c exists already in the table
(5,6,7,1)
rejected
1 exists but with different values for a b and c
(3,4,5, 2)
accepted
d exists and a, b, c, have the same values
(7,8,9,3)
accepted
a, b, c are unique and d does not exist
听起来您试图将两个或更多 table 压缩为一个 table。
- 没有更多的背景很难说
例如,如果您制作了一个大的平面文件,您可能会有这个?
a
b
c
d
x
y
z
1
2
3
1
1
3
1
1
2
3
1
2
8
7
1
2
3
1
5
9
2
4
5
6
2
9
8
7
4
5
6
2
4
5
6
4
5
6
2
3
2
1
4
5
6
2
2
1
0
虽然数据库不是电子表格或平面文件,但它们是关系结构。
上面的文件在数据库中可能会更好地表示为两个 tables...
a
b
c
d
1
2
3
1
4
5
6
2
d
x
y
z
1
1
3
1
1
2
8
7
1
5
9
2
2
9
8
7
2
4
5
6
2
3
2
1
2
2
1
0
如果你想要一行新的“数据”,你可以在第二行中添加一行table。
如果要在 (a,b,c)
和 (d)
之间创建新关系,请向第一个 table 添加一行。
可以按如下方式实施和强制执行...
CREATE TABLE map (
column_a NUMBER(8) NOT NULL,
column_b NUMBER(8) NOT NULL,
column_c NUMBER(8) NOT NULL,
column_d NUMBER(8) NOT NULL,
UNIQUE(column_a, column_b, column_c),
UNIQUE(column_d)
)
CREATE TABLE fact (
column_pk RAW(16) NOT NULL,
column_d NUMBER(8) NOT NULL,
column_x NUMBER(8) NOT NULL,
column_y NUMBER(8) NOT NULL,
column_z NUMBER(8) NOT NULL,
PRIMARY KEY (column_pk),
FOREIGN KEY (column_d) REFERENCES map(column_d)
)
据我所知,这个结构可以包含您想要允许的所有内容,以及禁止您想要禁止的所有内容。
据我了解,您有两个独特的约束(在 A,B,C
和 D
上),如果在 A,B,C,D
.[=19 上有重复项,您希望同时抑制这两个约束=]
这是 AFAIK 不可能在一个 table 中完成的,因此您必须将您的设置分成两个 table。
第一个 ABCD
检查约束,但不允许重复,第二个 TAB
存储实际数据并引用第一个 table。
在 插入触发器 中,A,B,C,D
上的唯一值在第一个 table 中 合并 。
create table abcd
(a int,
b int,
c int,
d int,
constraint abcd unique (a,b,c,d),
constraint abc unique (a,b,c),
constraint d unique(d));
drop table tab;
create table tab
(pk int,
a int,
b int,
c int,
d int,
primary key(pk),
foreign key(a,b,c,d) references abcd(a,b,c,d)
);
CREATE OR REPLACE TRIGGER tab_trigger
BEFORE INSERT ON tab
FOR EACH ROW
BEGIN
merge into abcd using
(select :new.a a, :new.b b, :new.c c, :new.d d from dual) src
on (abcd.a = src.a and abcd.b = src.b and abcd.c = src.c and abcd.d = src.d)
when not matched then insert (a,b,c,d) values (src.a, src.b, src.c, src.d)
;
END;
/
测试按预期运行
insert into tab(PK, A, B, C, D) values (1, 1,2,3,1);
1 row inserted.
insert into tab(PK, A, B, C, D) values (2, 3,4,5,2);
1 row inserted.
insert into tab(PK, A, B, C, D) values (3, 1,2,3,1);
1 row inserted.
insert into tab(PK, A, B, C, D) values (4, 1,2,3,4);
ORA-00001: unique constraint (ZZZ.ABC) violated
insert into tab(PK, A, B, C, D) values (5, 5,6,7,1);
ORA-00001: unique constraint (ZZZ.D) violated
insert into tab(PK, A, B, C, D) values (6, 3,4,5,2);
1 row inserted.
insert into tab(PK, A, B, C, D) values (7, 7,8,9,3);
1 row inserted.
无论如何我必须承认我不喜欢基于触发器的解决方案,我更喜欢带有检查视图的延迟验证.
即您可以插入任何行,但在视图中您会看到哪些行无效并且您可以处理它。
验证查询相当简单,它使用分析函数来获取键的重复计数。
with abcd as (
select PK, A, B, C, D,
count(*) over (partition by A, B, C order by PK) cnt_abc,
count(*) over (partition by D order by PK) cnt_d,
count(*) over (partition by A, B, C, D order by PK) cnt_abcd
from tab)
select
PK, A, B, C, D, CNT_ABC, CNT_D, CNT_ABCD,
case when (CNT_ABC > 1 or CNT_D > 1) and CNT_ABCD = 1 then 'rejected'
else 'accepted' end as status
from abcd
order by PK;
ID A B C D CNT_ABC CNT_D CNT_ABCD STATUS
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- --------
1 1 2 3 1 1 1 1 accepted
2 3 4 5 2 1 1 1 accepted
3 1 2 3 1 2 2 2 accepted
4 1 2 3 4 3 1 1 rejected
5 5 6 7 1 1 3 1 rejected
6 3 4 5 2 2 2 2 accepted
7 7 8 9 3 1 1 1 accepted
有没有办法在给定条件为真时忽略唯一约束?
例如,我的数据库中有 3 列构成了唯一约束:
create table example_table
(
column_primarykey RAW(16) default NULL not null,
column_a number(8) not null,
column_b number(8) not null,
column_c number(8) not null,
constraint constraint_1
unique(column_a, column_b, column_c)
constraint constraint_2
primary key (column_primarykey)
现在我添加第四列:
alter table example_table
add column_d number(8) not null,
我想要实现的是,如果 column_d 的值已经存在于 table 中,那么唯一约束将被忽略。如果 column_d 在 table 中不是唯一的,则忽略唯一约束,您可以将行添加到 table。例如,这是我 table 中的现有数据(忽略主键原因不相关):
column_a | column_a | column_c | column_d |
---|---|---|---|
1 | 2 | 3 | 1 |
3 | 4 | 5 | 2 |
我想要的是您可以添加例如 (1, 2, 3, 1) 但不能添加 (1, 2, 3, 2),因为已经有包含前三个值的行。仅当 column_d 中的值已经存在并且其他值等于现有行时才可能。
更多有助于理解的示例:
Example insert | result | reason |
---|---|---|
(1, 2, 3, 1) | accepted | d is not unique and a, b, c got same values as the existing row with value 1 for column_d |
(1, 2, 3, 4) | rejected | a, b ,c exists already in the table |
(5,6,7,1) | rejected | 1 exists but with different values for a b and c |
(3,4,5, 2) | accepted | d exists and a, b, c, have the same values |
(7,8,9,3) | accepted | a, b, c are unique and d does not exist |
听起来您试图将两个或更多 table 压缩为一个 table。
- 没有更多的背景很难说
例如,如果您制作了一个大的平面文件,您可能会有这个?
a | b | c | d | x | y | z | ||
---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 1 | 1 | 3 | 1 | ||
1 | 2 | 3 | 1 | 2 | 8 | 7 | ||
1 | 2 | 3 | 1 | 5 | 9 | 2 | ||
4 | 5 | 6 | 2 | 9 | 8 | 7 | ||
4 | 5 | 6 | 2 | 4 | 5 | 6 | ||
4 | 5 | 6 | 2 | 3 | 2 | 1 | ||
4 | 5 | 6 | 2 | 2 | 1 | 0 | ||
虽然数据库不是电子表格或平面文件,但它们是关系结构。
上面的文件在数据库中可能会更好地表示为两个 tables...
a | b | c | d | |
---|---|---|---|---|
1 | 2 | 3 | 1 | |
4 | 5 | 6 | 2 |
d | x | y | z | |
---|---|---|---|---|
1 | 1 | 3 | 1 | |
1 | 2 | 8 | 7 | |
1 | 5 | 9 | 2 | |
2 | 9 | 8 | 7 | |
2 | 4 | 5 | 6 | |
2 | 3 | 2 | 1 | |
2 | 2 | 1 | 0 | |
如果你想要一行新的“数据”,你可以在第二行中添加一行table。
如果要在 (a,b,c)
和 (d)
之间创建新关系,请向第一个 table 添加一行。
可以按如下方式实施和强制执行...
CREATE TABLE map (
column_a NUMBER(8) NOT NULL,
column_b NUMBER(8) NOT NULL,
column_c NUMBER(8) NOT NULL,
column_d NUMBER(8) NOT NULL,
UNIQUE(column_a, column_b, column_c),
UNIQUE(column_d)
)
CREATE TABLE fact (
column_pk RAW(16) NOT NULL,
column_d NUMBER(8) NOT NULL,
column_x NUMBER(8) NOT NULL,
column_y NUMBER(8) NOT NULL,
column_z NUMBER(8) NOT NULL,
PRIMARY KEY (column_pk),
FOREIGN KEY (column_d) REFERENCES map(column_d)
)
据我所知,这个结构可以包含您想要允许的所有内容,以及禁止您想要禁止的所有内容。
据我了解,您有两个独特的约束(在 A,B,C
和 D
上),如果在 A,B,C,D
.[=19 上有重复项,您希望同时抑制这两个约束=]
这是 AFAIK 不可能在一个 table 中完成的,因此您必须将您的设置分成两个 table。
第一个 ABCD
检查约束,但不允许重复,第二个 TAB
存储实际数据并引用第一个 table。
在 插入触发器 中,A,B,C,D
上的唯一值在第一个 table 中 合并 。
create table abcd
(a int,
b int,
c int,
d int,
constraint abcd unique (a,b,c,d),
constraint abc unique (a,b,c),
constraint d unique(d));
drop table tab;
create table tab
(pk int,
a int,
b int,
c int,
d int,
primary key(pk),
foreign key(a,b,c,d) references abcd(a,b,c,d)
);
CREATE OR REPLACE TRIGGER tab_trigger
BEFORE INSERT ON tab
FOR EACH ROW
BEGIN
merge into abcd using
(select :new.a a, :new.b b, :new.c c, :new.d d from dual) src
on (abcd.a = src.a and abcd.b = src.b and abcd.c = src.c and abcd.d = src.d)
when not matched then insert (a,b,c,d) values (src.a, src.b, src.c, src.d)
;
END;
/
测试按预期运行
insert into tab(PK, A, B, C, D) values (1, 1,2,3,1);
1 row inserted.
insert into tab(PK, A, B, C, D) values (2, 3,4,5,2);
1 row inserted.
insert into tab(PK, A, B, C, D) values (3, 1,2,3,1);
1 row inserted.
insert into tab(PK, A, B, C, D) values (4, 1,2,3,4);
ORA-00001: unique constraint (ZZZ.ABC) violated
insert into tab(PK, A, B, C, D) values (5, 5,6,7,1);
ORA-00001: unique constraint (ZZZ.D) violated
insert into tab(PK, A, B, C, D) values (6, 3,4,5,2);
1 row inserted.
insert into tab(PK, A, B, C, D) values (7, 7,8,9,3);
1 row inserted.
无论如何我必须承认我不喜欢基于触发器的解决方案,我更喜欢带有检查视图的延迟验证.
即您可以插入任何行,但在视图中您会看到哪些行无效并且您可以处理它。
验证查询相当简单,它使用分析函数来获取键的重复计数。
with abcd as (
select PK, A, B, C, D,
count(*) over (partition by A, B, C order by PK) cnt_abc,
count(*) over (partition by D order by PK) cnt_d,
count(*) over (partition by A, B, C, D order by PK) cnt_abcd
from tab)
select
PK, A, B, C, D, CNT_ABC, CNT_D, CNT_ABCD,
case when (CNT_ABC > 1 or CNT_D > 1) and CNT_ABCD = 1 then 'rejected'
else 'accepted' end as status
from abcd
order by PK;
ID A B C D CNT_ABC CNT_D CNT_ABCD STATUS
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- --------
1 1 2 3 1 1 1 1 accepted
2 3 4 5 2 1 1 1 accepted
3 1 2 3 1 2 2 2 accepted
4 1 2 3 4 3 1 1 rejected
5 5 6 7 1 1 3 1 rejected
6 3 4 5 2 2 2 2 accepted
7 7 8 9 3 1 1 1 accepted