【问题标题】:Oracle: unique constraint on column with value, which represents multiple valuesOracle:对具有值的列的唯一约束,表示多个值
【发布时间】:2019-05-06 19:29:23
【问题描述】:

我们想检查 oracle 表上的唯一数据。但是一个值可以代表唯一约束的多个值。这个唯一性超过两列,键和类型。 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
+----+------+------+

是否可以使用唯一/检查约束来检查这个?

编辑:

此外,还保存了其他数据。 L 和 R 可以有不同的数据。 B是在L和R相同的情况下。所以只保存了一行。

如果可能的话,我想在没有触发器的情况下尝试这个。

【问题讨论】:

  • 听起来你需要一个触发器。您尝试做的事情只能通过运行代码来限制

标签: oracle unique-constraint


【解决方案1】:

如果可能,请考虑重新建模。创建一个新表。使用旧表的键列,并应用 PK 约束(这将强制唯一性和 NOT NULL)。为您正在处理的每种(子)类型(L,R)设置一列。使用仅允许表示(子)类型的单字母缩写的 CHECK 约束。包括一个虚拟列,如果两个子类型列都已填充,则该列将“包含”字母“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 

【讨论】:

  • PK 是“ID”。 key 只是一个值,它需要与类型一起唯一。 :) 但是谢谢,我想我会尝试在列中拆分类型并使用带有一些逻辑的检查约束。也许这会有所帮助。
  • 在您的帮助下找到了解决方案:dbfiddle.uk/… - 我还添加了一个检查约束,即至少填充一列。而且我能够删除“B”,因为我知道如果 L =1 和 R = 1 它是“B” :)
  • 太棒了!谢谢你的评论。很高兴看到您的解决方案。祝你好运。
【解决方案2】:

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

id Type
1   1
2   2
3   3

然后将 yourTable.Typeforeign 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
}

【讨论】:

  • 是的,我也认为当前的设计不适合我想做的事情。我可以改变它。但有一件事是错误的:我可以有 L 和 R,或者 B。忘了说保存了额外的数据。所以 AAA+L 和 AAA+R 可能有不同的数据。
  • @Jukkales 的所有 BLR 的数据是否相同?
  • 不,L 和 R 可以不同。 B 是保存一行的“简单”方法如果数据相等,但 B 也用于稍后处理它与 L 和 R 不同。也许触发器是唯一的方法。
  • @Jukkales 可能是这样:使Key 成为一个独特的字段。在类型中,使用(字母或数字表示)LRB。将数据存储在单独的表中 - id, key, type, data,其中 type 只能是 1 个字母,UQ 将是 key, type。它给了你什么? - 您现在只更改原始表格中的 1 个数据 - type。虽然这个新表包含R,B,L 的数据。您的应用必须将 LR 翻译成 Where type = 'R' or Type = 'L'
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多