Правильный способ иметь внешние ключи для подклассов в SQL? - PullRequest
0 голосов
/ 19 мая 2018

Медсестра заказывает медикаменты через заявку на один из трех разных расходных материалов, поставляемых поставщиком.

Медсестра> Заявка <Расходные материалы (3 вида) <Поставщик </p>

Поскольку товары могутбыть одним из трех видов, и заявка может еще не существовать для элемента, таблица заявок имеет внешние ключи трех типов поставки.

Проблема: все мои правильно перечисленные внешние ключи указывают на 3 разные таблицы,у всех, кроме одного, не будет соответствующего внешнего ключа для каждой записи.

Я получаю следующую ошибку:

ОШИБКА в строке 1: ORA-02091: откат транзакции
ORA-02291: ограничение целостности (MMM1339.ITEMNO_PHAR_FK) нарушено - родительский ключ не найден

CREATE TABLE SUPPLIER
(SUPPLIERNO   INT,
 SUPPLIERNAME VARCHAR2(100),
 PHONENO VARCHAR2(12),
 ADDRESS VARCHAR(100),
 FAXNO VARCHAR(12),
 CONSTRAINT SUPPLIERNO_SSPL_PK PRIMARY KEY(SUPPLIERNO));

CREATE TABLE SUPPLIES_PHARMACEUTICAL
(ITEMNO         INT,
SUPPLIERNO      INT,
NAME            VARCHAR2(25),
DESCRIPTION     VARCHAR2(25),
QUANTITYINSTOCK INT,
REORDERLEVEL    INT,
COSTPERUNIT     DECIMAL(6,2),
DOSAGE          VARCHAR2(12),
CONSTRAINT ITEMNO_PHAR_PK PRIMARY KEY(ITEMNO));

CREATE TABLE SUPPLIES_SURGICAL
(ITEMNO         INT,
NAME            VARCHAR2(25),
DESCRIPTION     VARCHAR2(25),
QUANTITYINSTOCK INT,
REORDERLEVEL    INT,
COSTPERUNIT     DECIMAL(6,2),
SUPPLIERNO      INT,
CONSTRAINT ITEMNO_SUP_PK PRIMARY KEY(ITEMNO));

CREATE TABLE SUPPLIES_NONSURGICAL
(ITEMNO         INT,
NAME            VARCHAR2(25),
DESCRIPTION     VARCHAR2(25),  
QUANTITYINSTOCK INT,
REORDERLEVEL    INT,
COSTPERUNIT     DECIMAL(6,2),
SUPPLIERNO      INT,
CONSTRAINT ITEMNO_NONSURG_PK PRIMARY KEY(ITEMNO));

CREATE TABLE STAFF_CHARGENURSE
(STAFFNO   INT,
 ADDRESS   VARCHAR2(25),
 POSITION  VARCHAR2(12),
 BUDGET    DECIMAL(6,2),
 SPECIALTY VARCHAR2(12),
 CONSTRAINT STAFFNO_CHNURSE_PK PRIMARY KEY(STAFFNO));

 CREATE TABLE REQUISITION
 (REQNO INT,
  STAFFNO INT,
  STAFFNAME VARCHAR2(25),
  WARDNO INT,
  ITEMNO INT,
  QUANTITY INT,
  DATEORDERED DATE,
  DATERECIEVED DATE,
  CONSTRAINT REQ_PK PRIMARY KEY(REQNO));

Внешние ключи:

 ALTER TABLE SUPPLIES_PHARMACEUTICAL
     ADD CONSTRAINT SUPPLIERNO_PHA_FK FOREIGN KEY(SUPPLIERNO) REFERENCES SUPPLIER(SUPPLIERNO)
     DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE SUPPLIES_SURGICAL
     ADD CONSTRAINT SUPPLIERNO_SURG_FK FOREIGN KEY(SUPPLIERNO) REFERENCES SUPPLIER(SUPPLIERNO)
     DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE SUPPLIES_NONSURGICAL
     ADD CONSTRAINT SUPPLIERNO_NONSURG_FK FOREIGN KEY(SUPPLIERNO) REFERENCES SUPPLIER(SUPPLIERNO)
     DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE REQUISITION
     ADD CONSTRAINT STAFFNO_REQ_FK FOREIGN KEY(STAFFNO) REFERENCES STAFF_CHARGENURSE(STAFFNO)
     DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE REQUISITION
     ADD CONSTRAINT ITEMNO_PHAR_FK FOREIGN KEY(ITEMNO) REFERENCES SUPPLIES_PHARMACEUTICAL(ITEMNO)
     DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE REQUISITION
     ADD CONSTRAINT ITEMNO_SURG_FK FOREIGN KEY(ITEMNO) REFERENCES SUPPLIES_SURGICAL(ITEMNO)
     DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE REQUISITION
     ADD CONSTRAINT ITEMNO_NONSURG_FK FOREIGN KEY(ITEMNO) REFERENCES SUPPLIES_NONSURGICAL(ITEMNO)
     DEFERRABLE INITIALLY DEFERRED;

Тестовые данные:

 INSERT INTO REQUISITION VALUES(1, 20, 'Julie Wood', 8, 888520, 2, '27-FEB-2018', '15-MAR-2018');
 INSERT INTO REQUISITION VALUES(2, 20, 'Julie Wood', 8, 923956, 1, '25-FEB-2018', '28-FEB-2018');
 INSERT INTO REQUISITION VALUES(3, 21, 'Sarah Michaels', 7, 054802, 3, '20-FEB-2018', '22-FEB-2018');

 INSERT INTO SUPPLIES_PHARMACEUTICAL VALUES (823456, 100001, 'Zanax', 'Anti Depressant', 8, 2, 100.50, '50mg');
 INSERT INTO SUPPLIES_PHARMACEUTICAL VALUES (923956, 100001, 'Zupridol', 'Blood Pressure Treatment', 12, 5, 50, '20mg');
 INSERT INTO SUPPLIES_PHARMACEUTICAL VALUES (3952, 200001, 'Amibreezax', 'Artificial Ear Wax', 2, 1, 200, '5g');
 INSERT INTO SUPPLIES_PHARMACEUTICAL VALUES (4955, 200001, 'Ambridax', 'Skin Treatment', 5, 10, 20, '2mg');

 INSERT INTO SUPPLIES_SURGICAL VALUES (54802, 'Scalpel', 'Surgical Tool', 20, 10, 200.42, 100001);
 INSERT INTO SUPPLIES_SURGICAL VALUES (634520, 'Stitches', 'Suture Tool', 100, 10, 2.50, 200001);

 INSERT INTO SUPPLIES_NONSURGICAL VALUES (888520, 'Cart', '5ftx2ftx3ft', 2, 0, 200.00, 100001);
 INSERT INTO SUPPLIES_NONSURGICAL VALUES (423, 'Tool Holder', 'Holds Inspection Equip.', 4, 2, 50.00, 100001);

 INSERT INTO STAFF_CHARGENURSE VALUES(20, '32 Stark St. Portland, OR', 'Charge Nurse', 8000.99, 'Head Trauma');
 INSERT INTO STAFF_CHARGENURSE VALUES(21, '18 Wilson Rd Portland, OR', 'Charge Nurse', 6000, 'Epidermus');

 INSERT INTO SUPPLIER VALUES (100001,'Company A', '503-222-3333', '100 SE Stark Rd Portland, OR', '503-666-4444');
 INSERT INTO SUPPLIER VALUES (200001,'Company B', '666-333-4444', '500 SE Bilerica Rd Akron, OH', '666-444-3333');

 COMMIT;

Ответы [ 2 ]

0 голосов
/ 21 мая 2018

Вот ваш измененный исходный код (оригинал в верхнем регистре, строчные буквы: модификации), используя @William Robertson's ideas : {1} с использованием одной таблицы SUPPLIES, {2} со столбцом is_surgical и {3} таблица pharma_dosage.Может быть, вам это нравится ...

CREATE TABLE SUPPLIER
(SUPPLIERNO   integer,
 SUPPLIERNAME VARCHAR2(100),
 PHONENO VARCHAR2(12),
 ADDRESS VARCHAR(100),
 FAXNO VARCHAR(12),
 CONSTRAINT SUPPLIERNO_SSPL_PK PRIMARY KEY(SUPPLIERNO));

-- one table instead of 3
create table supplies (
  ITEMNO          integer,
  SUPPLIERNO      integer,
  NAME            VARCHAR2(25),
  DESCRIPTION     VARCHAR2(25),
  QUANTITYINSTOCK integer,
  REORDERLEVEL    integer,
  COSTPERUNIT     number(6,2),
  is_surgical     varchar2(1) not null, 
  constraint supplies_surgical_yn_chk check( is_surgical in ('Y','N') )
, constraint supplies_pk primary key( itemno )
, constraint supplies_fk foreign key( supplierno )
  references supplier( supplierno )
);

create table pharma_dosage ( 
  itemno          integer
, dosage          varchar2( 64 ) not null 
, constraint pharma_supplies_fk foreign key( itemno )
  references supplies( itemno )
, constraint pharma_supplies_pk primary key( itemno )
);

Еще две таблицы и, закомментировано, вещи, которые нам не нужны:

-- not needed
-- CREATE TABLE SUPPLIES_PHARMACEUTICAL
-- CREATE TABLE SUPPLIES_SURGICAL
-- CREATE TABLE SUPPLIES_NONSURGICAL
CREATE TABLE STAFF_CHARGENURSE
(STAFFNO   integer,          
 ADDRESS   VARCHAR2(25),
 POSITION  VARCHAR2(12),
 BUDGET    DECIMAL(6,2),
 SPECIALTY VARCHAR2(12),
 CONSTRAINT STAFFNO_CHNURSE_PK PRIMARY KEY(STAFFNO));

 CREATE TABLE REQUISITION
 (REQNO integer,             
  STAFFNO integer,           
  STAFFNAME VARCHAR2(25),
  WARDNO integer,            
  ITEMNO integer,            
  QUANTITY integer,         
  DATEORDERED DATE,
  DATERECIEVED DATE,
  CONSTRAINT REQ_PK PRIMARY KEY(REQNO),
  constraint req_fk foreign key ( itemno ) references supplies ( itemno )
);
-- not needed
-- ALTER TABLE SUPPLIES_PHARMACEUTICAL ADD CONSTRAINT SUPPLIERNO_PHA_FK 
-- ALTER TABLE SUPPLIES_SURGICAL ADD CONSTRAINT SUPPLIERNO_SURG_FK
-- ALTER TABLE SUPPLIES_NONSURGICAL ADD CONSTRAINT SUPPLIERNO_NONSURG_FK 
ALTER TABLE REQUISITION ADD CONSTRAINT STAFFNO_REQ_FK 
FOREIGN KEY(STAFFNO) REFERENCES STAFF_CHARGENURSE(STAFFNO) DEFERRABLE INITIALLY DEFERRED;
-- not needed
-- ALTER TABLE REQUISITION ADD CONSTRAINT ITEMNO_PHAR_FK 
-- ALTER TABLE REQUISITION ADD CONSTRAINT ITEMNO_SURG_FK 
-- ALTER TABLE REQUISITION ADD CONSTRAINT ITEMNO_NONSURG_FK 

INSERTs

-- parents first
begin
  INSERT INTO STAFF_CHARGENURSE VALUES(20, '32 Stark St. Portland, OR', 'Charge Nurse', 8000.99, 'Head Trauma');
  INSERT INTO STAFF_CHARGENURSE VALUES(21, '18 Wilson Rd Portland, OR', 'Charge Nurse', 6000, 'Epidermus');

  INSERT INTO SUPPLIER VALUES (100001,'Company A', '503-222-3333', '100 SE Stark Rd Portland, OR', '503-666-4444');
  INSERT INTO SUPPLIER VALUES (200001,'Company B', '666-333-4444', '500 SE Bilerica Rd Akron, OH', '666-444-3333');
end;
/
PL/SQL procedure successfully completed.

Дополнительные тестовые данные

begin
-- pharmaceutical
INSERT INTO SUPPLIES( itemno, supplierno, name, description, quantityinstock
, reorderlevel, costperunit, is_surgical )
VALUES (823456, 100001, 'Zanax', 'Anti Depressant', 8, 2, 100.50, 'N');
INSERT INTO SUPPLIES( itemno, supplierno, name, description, quantityinstock
, reorderlevel, costperunit, is_surgical )
VALUES (923956, 100001, 'Zupridol', 'Blood Pressure Treatment', 12, 5, 50, 'N');
INSERT INTO SUPPLIES( itemno, supplierno, name, description, quantityinstock
, reorderlevel, costperunit, is_surgical )
VALUES (3952, 200001, 'Amibreezax', 'Artificial Ear Wax', 2, 1, 200, 'N');
INSERT INTO SUPPLIES( itemno, supplierno, name, description, quantityinstock
, reorderlevel, costperunit, is_surgical )
VALUES (4955, 200001, 'Ambridax', 'Skin Treatment', 5, 10, 20, 'N');
-- pharma_dosage
insert into pharma_dosage ( itemno, dosage ) values ( 823456, '50mg' ) ;
insert into pharma_dosage ( itemno, dosage ) values ( 923956, '20mg' ) ;
insert into pharma_dosage ( itemno, dosage ) values ( 3952, '5g' ) ;
insert into pharma_dosage ( itemno, dosage ) values ( 4955, '2mg' ) ;
-- surgical
INSERT INTO SUPPLIES( itemno, supplierno, name, description, quantityinstock
, reorderlevel, costperunit, is_surgical )
VALUES (54802, 100001, 'Scalpel', 'Surgical Tool', 20, 10, 200.42, 'Y');
INSERT INTO SUPPLIES( itemno, supplierno, name, description, quantityinstock
, reorderlevel, costperunit, is_surgical ) 
VALUES (634520, 200001, 'Stitches', 'Suture Tool', 100, 10, 2.50, 'Y');
-- nonsurgical
INSERT INTO SUPPLIES( itemno, supplierno, name, description, quantityinstock
, reorderlevel, costperunit, is_surgical)
VALUES (888520, 100001, 'Cart', '5ftx2ftx3ft', 2, 0, 200.00,  'N');
INSERT INTO SUPPLIES( itemno, supplierno, name, description, quantityinstock
, reorderlevel, costperunit, is_surgical )
VALUES (423, 100001,'Tool Holder', 'Holds Inspection Equip.', 4, 2, 50.00,  'N');
-- requisition
INSERT INTO REQUISITION VALUES(1, 20, 'Julie Wood', 8, 888520, 2, '27-FEB-2018', '15-MAR-2018');
INSERT INTO REQUISITION VALUES(2, 20, 'Julie Wood', 8, 923956, 1, '25-FEB-2018', '28-FEB-2018');
INSERT INTO REQUISITION VALUES(3, 21, 'Sarah Michaels', 7, 054802, 3, '20-FEB-2018', '22-FEB-2018');
end ;
/
PL/SQL procedure successfully completed.

Теперь некоторые SELECTs ...

SQL> select * from supplier ;
SUPPLIERNO  SUPPLIERNAME  PHONENO       ADDRESS                       FAXNO         
100001      Company A     503-222-3333  100 SE Stark Rd Portland, OR  503-666-4444  
200001      Company B     666-333-4444  500 SE Bilerica Rd Akron, OH  666-444-3333  

SQL> select * from supplies ;
ITEMNO  SUPPLIERNO  NAME         DESCRIPTION               QUANTITYINSTOCK  REORDERLEVEL  COSTPERUNIT  IS_SURGICAL  
823456  100001      Zanax        Anti Depressant           8                2             100.5        N            
923956  100001      Zupridol     Blood Pressure Treatment  12               5             50           N            
3952    200001      Amibreezax   Artificial Ear Wax        2                1             200          N            
4955    200001      Ambridax     Skin Treatment            5                10            20           N            
54802   100001      Scalpel      Surgical Tool             20               10            200.42       Y            
634520  200001      Stitches     Suture Tool               100              10            2.5          Y            
888520  100001      Cart         5ftx2ftx3ft               2                0             200          N            
423     100001      Tool Holder  Holds Inspection Equip.   4                2             50           N             

SQL> select * from requisition;
REQNO  STAFFNO  STAFFNAME       WARDNO  ITEMNO  QUANTITY  DATEORDERED  DATERECIEVED  
1      20       Julie Wood      8       888520  2         27-FEB-18    15-MAR-18     
2      20       Julie Wood      8       923956  1         25-FEB-18    28-FEB-18     
3      21       Sarah Michaels  7       54802   3         20-FEB-18    22-FEB-18     

SQL> select * from staff_chargenurse;
STAFFNO  ADDRESS                    POSITION      BUDGET   SPECIALTY    
20       32 Stark St. Portland, OR  Charge Nurse  8000.99  Head Trauma  
21       18 Wilson Rd Portland, OR  Charge Nurse  6000     Epidermus    

SQL> select * from pharma_dosage;
ITEMNO  DOSAGE  
823456  50mg    
923956  20mg    
3952    5g      
4955    2mg   

Версия 2

Если вы все еще хотите иметь одну таблицу «супертип» и 3 «подтип», посмотрите следующий DDL.Вы, вероятно, можете найти решение, которое вроде как "между" этими двумя.(Далее следует просто код «доказательства концепции», несколько столбцов и некоторые исходные таблицы опущены.)

create table supplies (
  supplierno number primary key
, category varchar2( 16 ) 
, constraint unique_parentcategory unique ( supplierno, category )
, constraint check_category check (
    category in ( 'surgical', 'non-surgical', 'pharmaceutical' )
  )
);

Затем ...

create table supplies_pharmaceutical (
  supplierno number primary key
, category varchar2( 16 )
-- 
-- more "specific" columns here 
--
, constraint check_category1 check ( category in ( 'pharmaceutical' ) )
, constraint s_p_fk foreign key ( supplierno, category ) 
  references supplies ( supplierno, category )
) ; 

create table supplies_nonsurgical (
  supplierno number primary key
, category varchar2( 16 )
-- 
-- more "specific" columns here 
--
, constraint check_category2 check ( category in ( 'non-surgical' )  )
, constraint s_n_fk foreign key ( supplierno, category ) 
  references supplies ( supplierno, category )
) ; 

create table supplies_surgical (
  supplierno number primary key
, category varchar2( 16 )
-- 
-- more "specific" columns here 
--
, constraint check_category3 check ( category in ( 'surgical' )  )
, constraint s_s_fk foreign key ( supplierno, category ) 
  references supplies ( supplierno, category ) 
) ; 

Тестовые данные:

begin
  insert into supplies( supplierno, category ) values ( 1000, 'pharmaceutical' ) ;
  insert into supplies( supplierno, category ) values ( 2000, 'non-surgical' ) ;
  insert into supplies( supplierno, category ) values ( 3000, 'surgical' ) ;

  insert into supplies_pharmaceutical( supplierno, category ) 
    values ( 1000, 'pharmaceutical' ) ;
  insert into supplies_nonsurgical( supplierno, category ) 
    values ( 2000, 'non-surgical' ) ;
  insert into supplies_surgical( supplierno, category ) 
    values ( 3000, 'surgical' ) ;
end;
/

-- must fail:
insert into supplies ( supplierno, category ) values ( 1001, 'food' ) ;

insert into supplies_pharmaceutical( supplierno, category ) 
  values ( 2000, 'pharmaceutical' ) ;
0 голосов
/ 20 мая 2018

Как уже упоминалось в ответах на другие ваши связанные вопросы, нет ни суперкласса, ни подклассов.SUPPLIES_PHARMACEUTICAL, SUPPLIES_SURGICAL и SUPPLIES_NONSURGICAL - это просто таблицы, не имеющие связи друг с другом.

Все разрешенные ограничения применяются.Определение их как отложенных просто откладывает проверку до следующего commit, что на самом деле ничего не меняет в вашем примере.Если вы определяете три ограничения для столбца, нет синтаксиса или механизма, чтобы сказать 'только одно из этих ограничений должно быть применено' , и я не вижу, как такая система будет когда-либо работоспособной.

В моделировании данных вы определяете подтипы, используя либо

  1. одну таблицу (например, SUPPLIES) с category или аналогичным столбцом индикатора, либо
  2. Родительская таблица, имеющая только общие столбцы, а дочерние таблицы, имеющие только столбцы для конкретного типа, связаны с родителем через ограничения внешнего ключа.Дочерняя таблица может иметь уникальный или первичный ключ в столбце FK, что делает ее необязательным отношением 1: 1.

В других таблицах могут быть ограничения FK, ссылающиеся либо на родителя, либо на одного из его дочерних элементов.по мере необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...