Oracle:对具有值的列的唯一约束,它代表多个值
Oracle: unique constraint on column with value, which represents multiple values
我们想检查 oracle table 上的唯一数据。但是一个值可以表示唯一约束的多个值。这个唯一性超过两列,key 和 type。键只是一个字符串,类型可以包含三个不同的值:
L, R 和 B 即 L+R
+----+------+------+
| ID | Key | Type |
+----+------+------+
| 1 | AAA | L |
| 2 | AAA | R |
| 3 | BBB | B | B = L+R
| 4 | CCC | L |
| 5 | CCC | B | Not possible because L and/or R exisits
| 6 | BBB | L | Not possible because B exists
+----+------+------+
是否可以使用 unique/check 约束来检查它?
编辑:
与此同时,还会保存其他数据。 L 和 R 可以有不同的数据。 B是在L和R相同的情况下。所以只保存了一行。
我想试试这个,如果可能的话,不用触发器。
如果可能,考虑重新建模。创建一个新的 table。使用旧的 table 的键列,并应用 PK 约束(这将强制执行唯一性和 NOT NULL)。为您正在处理的每个(子)类型(L,R)都有一列。使用仅允许表示(子)类型的单字母缩写的 CHECK 约束。如果两个子类型列都已填充,则包含一个虚拟列,该列将 "contain" 字母 'B'。 DDL代码:
create table kt2 (
key varchar2( 64 ) primary key
, typeL varchar2( 1 )
, typeR varchar2( 1 )
, typeB varchar2( 1 ) generated always as (
case when typeL = 'L' and typeR = 'R' then 'B' else null end
) virtual
, constraint types_check check (
( typeL = 'L' and typeR = 'R' )
or
( typeL = 'L' and typeR is null )
or
( typeL is null and typeR = 'R' )
)
) ;
测试
insert into kt2 ( key, typeL ) values ( 'AAA', 'L' ) ;
SQL> select * from kt2 ;
KEY TYPEL TYPER TYPEB
AAA L NULL NULL
-- fails (key value must be unique), needs update
insert into kt2 ( key, typeR ) values ( 'AAA', 'R' ) ;
update kt2 set typeR = 'R' where key = 'AAA' ;
SQL> select * from kt2;
KEY TYPEL TYPER TYPEB
AAA L R B
-- cannot insert into B ("generated")
insert into kt2 ( key, typeB ) values ( 'BBB', 'B' ) ;
-- ORA-54013: INSERT operation disallowed on virtual columns
如果您决定走这条路,您可以将存储在旧 table(此处名称:KT)中的所有数据转移到新 table,如下所示:
insert into kt2 ( key )
select unique key from kt -- KT: the old table ;
update kt2
set typeL = 'L'
where key = ( select key from kt where key = kt2.key and type = 'L' )
;
update kt2
set typeR = 'R'
where key = ( select key from kt where key = kt2.key and type = 'R' )
;
编辑(问题更新后)
添加到原始问题的要求:
Together with this, additional data is saved. L and R can have
different data. B is in case that L and R are the same. So only one
row is saved.
新建议:
Table 和约束
create table kt2 (
id number generated always as identity start with 1000 primary key
, key varchar2( 64 )
-- columns for values of type L
, L1 varchar2( 3 ), L2 varchar2( 3 ), L3 varchar2( 3 )
-- columns for values of type R
, R1 varchar2( 3 ), R2 varchar2( 3 ), R3 varchar2( 3 )
-- values for types L and R are identical -> type B
, typeB varchar2( 1 ) generated always as (
case when L1 = R1 and L2 = R2 and L3 = R3 then 'B' else null end
) virtual
, constraint key_typeL_unique unique ( key, L1, L2, L3 )
, constraint key_typeR_unique unique ( key, R1, R2, R3 )
) ;
测试
-- testing: AAA has attribute values for type L and for type R
-- type: L
insert into kt2 ( key, L1, L2, L3 )
values ( 'AAA', 11, 12, 13 ) ;
-- type: R
insert into kt2 ( key, R1, R2, R3 )
values ( 'AAA', 51, 52, 53 ) ;
-- type B: L and R "are the same"
insert into kt2 ( key, L1, L2, L3, R1, R2, R3 )
values ( 'BBB', 14, 15, 16, 14, 15, 16) ;
-- type: L
insert into kt2 ( key, L1, L2, L3 )
values ( 'CCC', 17, 18, 19 ) ;
-- key CCC, type L
-- insert not possible because L exists
insert into kt2 ( key, L1, L2, L3 )
values ( 'CCC', 17, 18, 19 ) ;
-- ORA-00001: unique constraint (...KEY_TYPEL_UNIQUE) violated
-- key BBB type L
-- Not possible because B exists
insert into kt2 ( key, L1, L2, L3 )
values ( 'BBB', 14, 15, 16 ) ;
-- ORA-00001: unique constraint (...KEY_TYPEL_UNIQUE) violated
插入后,table 包含...
SQL> select * from kt2;
ID KEY L1 L2 L3 R1 R2 R3 TYPEB
1000 AAA 11 12 13 NULL NULL NULL NULL
1001 AAA NULL NULL NULL 51 52 53 NULL
1002 BBB 14 15 16 14 15 16 B
1003 CCC 17 18 19 NULL NULL NULL NULL
进一步思考您的问题,我在这里看到了真正的答案 - 您的设计很糟糕。如果你可以有 L
或 R
,或 LR
,这实际上意味着你只能有 1 个值。在这种情况下,您应该具有 Key
唯一性。 Type
应该是 1、2 或 3。只是不要保存第二行,而是更新现有值。创建tableTypeValues
id Type
1 1
2 2
3 3
并使 yourTable.Type
成为新的 table 的 foreign key
。如果您将 DB 与某些应用程序连接,则会创建相应的 enum Types
。在 C# 中它看起来像这样
[Flags]
enum Types
{
None = 0x0,
L = 0x1,
R = 0x2
}
public static void Main()
{
var x = Types.L | Types.R;
Console.WriteLine((int)Types.L); // Prints 1
Console.WriteLine((int)Types.R); // Prints 2
Console.WriteLine((int)x); // Prints 3
}
我们想检查 oracle table 上的唯一数据。但是一个值可以表示唯一约束的多个值。这个唯一性超过两列,key 和 type。键只是一个字符串,类型可以包含三个不同的值:
L, R 和 B 即 L+R
+----+------+------+
| ID | Key | Type |
+----+------+------+
| 1 | AAA | L |
| 2 | AAA | R |
| 3 | BBB | B | B = L+R
| 4 | CCC | L |
| 5 | CCC | B | Not possible because L and/or R exisits
| 6 | BBB | L | Not possible because B exists
+----+------+------+
是否可以使用 unique/check 约束来检查它?
编辑:
与此同时,还会保存其他数据。 L 和 R 可以有不同的数据。 B是在L和R相同的情况下。所以只保存了一行。
我想试试这个,如果可能的话,不用触发器。
如果可能,考虑重新建模。创建一个新的 table。使用旧的 table 的键列,并应用 PK 约束(这将强制执行唯一性和 NOT NULL)。为您正在处理的每个(子)类型(L,R)都有一列。使用仅允许表示(子)类型的单字母缩写的 CHECK 约束。如果两个子类型列都已填充,则包含一个虚拟列,该列将 "contain" 字母 'B'。 DDL代码:
create table kt2 (
key varchar2( 64 ) primary key
, typeL varchar2( 1 )
, typeR varchar2( 1 )
, typeB varchar2( 1 ) generated always as (
case when typeL = 'L' and typeR = 'R' then 'B' else null end
) virtual
, constraint types_check check (
( typeL = 'L' and typeR = 'R' )
or
( typeL = 'L' and typeR is null )
or
( typeL is null and typeR = 'R' )
)
) ;
测试
insert into kt2 ( key, typeL ) values ( 'AAA', 'L' ) ;
SQL> select * from kt2 ;
KEY TYPEL TYPER TYPEB
AAA L NULL NULL
-- fails (key value must be unique), needs update
insert into kt2 ( key, typeR ) values ( 'AAA', 'R' ) ;
update kt2 set typeR = 'R' where key = 'AAA' ;
SQL> select * from kt2;
KEY TYPEL TYPER TYPEB
AAA L R B
-- cannot insert into B ("generated")
insert into kt2 ( key, typeB ) values ( 'BBB', 'B' ) ;
-- ORA-54013: INSERT operation disallowed on virtual columns
如果您决定走这条路,您可以将存储在旧 table(此处名称:KT)中的所有数据转移到新 table,如下所示:
insert into kt2 ( key )
select unique key from kt -- KT: the old table ;
update kt2
set typeL = 'L'
where key = ( select key from kt where key = kt2.key and type = 'L' )
;
update kt2
set typeR = 'R'
where key = ( select key from kt where key = kt2.key and type = 'R' )
;
编辑(问题更新后)
添加到原始问题的要求:
Together with this, additional data is saved. L and R can have different data. B is in case that L and R are the same. So only one row is saved.
新建议:
Table 和约束
create table kt2 (
id number generated always as identity start with 1000 primary key
, key varchar2( 64 )
-- columns for values of type L
, L1 varchar2( 3 ), L2 varchar2( 3 ), L3 varchar2( 3 )
-- columns for values of type R
, R1 varchar2( 3 ), R2 varchar2( 3 ), R3 varchar2( 3 )
-- values for types L and R are identical -> type B
, typeB varchar2( 1 ) generated always as (
case when L1 = R1 and L2 = R2 and L3 = R3 then 'B' else null end
) virtual
, constraint key_typeL_unique unique ( key, L1, L2, L3 )
, constraint key_typeR_unique unique ( key, R1, R2, R3 )
) ;
测试
-- testing: AAA has attribute values for type L and for type R
-- type: L
insert into kt2 ( key, L1, L2, L3 )
values ( 'AAA', 11, 12, 13 ) ;
-- type: R
insert into kt2 ( key, R1, R2, R3 )
values ( 'AAA', 51, 52, 53 ) ;
-- type B: L and R "are the same"
insert into kt2 ( key, L1, L2, L3, R1, R2, R3 )
values ( 'BBB', 14, 15, 16, 14, 15, 16) ;
-- type: L
insert into kt2 ( key, L1, L2, L3 )
values ( 'CCC', 17, 18, 19 ) ;
-- key CCC, type L
-- insert not possible because L exists
insert into kt2 ( key, L1, L2, L3 )
values ( 'CCC', 17, 18, 19 ) ;
-- ORA-00001: unique constraint (...KEY_TYPEL_UNIQUE) violated
-- key BBB type L
-- Not possible because B exists
insert into kt2 ( key, L1, L2, L3 )
values ( 'BBB', 14, 15, 16 ) ;
-- ORA-00001: unique constraint (...KEY_TYPEL_UNIQUE) violated
插入后,table 包含...
SQL> select * from kt2;
ID KEY L1 L2 L3 R1 R2 R3 TYPEB
1000 AAA 11 12 13 NULL NULL NULL NULL
1001 AAA NULL NULL NULL 51 52 53 NULL
1002 BBB 14 15 16 14 15 16 B
1003 CCC 17 18 19 NULL NULL NULL NULL
进一步思考您的问题,我在这里看到了真正的答案 - 您的设计很糟糕。如果你可以有 L
或 R
,或 LR
,这实际上意味着你只能有 1 个值。在这种情况下,您应该具有 Key
唯一性。 Type
应该是 1、2 或 3。只是不要保存第二行,而是更新现有值。创建tableTypeValues
id Type
1 1
2 2
3 3
并使 yourTable.Type
成为新的 table 的 foreign key
。如果您将 DB 与某些应用程序连接,则会创建相应的 enum Types
。在 C# 中它看起来像这样
[Flags]
enum Types
{
None = 0x0,
L = 0x1,
R = 0x2
}
public static void Main()
{
var x = Types.L | Types.R;
Console.WriteLine((int)Types.L); // Prints 1
Console.WriteLine((int)Types.R); // Prints 2
Console.WriteLine((int)x); // Prints 3
}