Oracle:具有值的列的唯一约束,该约束表示多个值

时间:2019-05-06 19:29:24

标签: oracle unique-constraint

我们要检查oracle表上的唯一数据。但是一个值可以代表唯一约束的多个值。这个唯一性超过两列,即键和类型。键只是一个字符串,类型可以包含三个不同的值:

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
+----+------+------+

是否可以通过唯一/检查约束条件对此进行检查?

编辑:

与此同时,将保存其他数据。 L和R可以具有不同的数据。 B在L和R相同的情况下。因此只保存了一行。

如果可能的话,我想尝试此操作而无需触发。

2 个答案:

答案 0 :(得分:1)

如果可能,请考虑重新建模。创建一个新表。使用旧表的键列,并应用PK约束(这将强制执行唯一性和NOT NULL)。对于要处理的(L,R)的每种(子)类型都有一列。使用CHECK约束,该约束仅允许代表(sub)types的单字母缩写。如果两个子类型列均已填写,则包括一个虚拟列,它将“包含”字母“ 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' )  
  )  
) ;

测试

DBfiddle

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

如果您决定沿这条路线走,可以将存储在旧表(此处为KT)中的所有数据转移到新表中,如下所示:

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' )
;

编辑(问题更新后)

原始问题中添加了要求:

  

与此同时,将保存其他数据。 L和R可以有   不同的数据。 B在L和R相同的情况下。所以只有一个   行已保存。

新建议:

表和约束

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

插入后,表格中包含...

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 

答案 1 :(得分:1)

更多地考虑您的问题,我在这里看到了真正的答案-您的设计不好。如果您可以拥有LRLR,则实际上意味着您只能拥有1个值。在这种情况下,您应该具有唯一的KeyType应该是1、2或3。只是不要保存第二行,而是更新现有值。创建表TypeValues

id Type
1   1
2   2
3   3

并将yourTable.Type设为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
}