Oracle: уникальное ограничение на столбец со значением, представляющим несколько значений - PullRequest
0 голосов
/ 06 мая 2019

Мы хотим проверить уникальные данные в таблице оракулов. Но значение может представлять несколько значений для уникального ограничения. Эта уникальность состоит из двух столбцов: ключ и тип. Ключ - это просто строка, а тип может содержать три разных значения:

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

Можно ли проверить это с помощью уникального ограничения / проверки?

Edit:

Вместе с этим сохраняются дополнительные данные. L и R могут иметь разные данные. В случае, если L и R одинаковы. Таким образом, сохраняется только одна строка.

Я хотел бы попробовать это, если это возможно, без триггера.

Ответы [ 2 ]

1 голос
/ 07 мая 2019

Подумав еще немного о вашей проблеме, я вижу реальный ответ здесь - ваш дизайн плохой. Если у вас может быть L или R, или LR, это действительно означает, что вы можете иметь только 1 значение. В этом случае у вас должно быть Key уникально. И Type должно быть 1, 2 или 3. Просто не сохраняйте вторую строку, а обновляйте существующее значение. Создать таблицу TypeValues

id Type
1   1
2   2
3   3

и сделайте yourTable.Type a foreign key к вашему новому столу. А если вы связываете БД с каким-либо приложением, вы создаете соответствующий 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
}
1 голос
/ 07 мая 2019

Если возможно, рассмотрите возможность повторного моделирования.Создать новую таблицу.Используйте ключевой столбец старой таблицы и примените ограничение 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 могут иметь разные данные.В случае, если 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 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...