【问题标题】:Proper way to have foreign keys to subclasses in SQL?在 SQL 中拥有子类的外键的正确方法?
【发布时间】:2018-05-19 19:43:35
【问题描述】:

一名护士通过对三种不同用品之一的申请订购医疗用品,所有用品均由供应商提供。

护士>申请

由于项目可以是三种类型之一,并且可能尚不存在项目的申请,因此申请表具有 3 种供应类型的外键。

问题:我正确列出的外键都指向 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;

【问题讨论】:

  • 如果这是您尝试运行的脚本,唯一的问题是您的插入顺序是错误的,首先您是 suplies_whatever,然后是供应商,然后是员工,最后是您的申请表,即链接表全部。要获得链接,链接的项目必须首先存在。
  • 已重新排序,但仍然存在同样的问题。

标签: sql oracle


【解决方案1】:

正如您在其他相关问题的回复中所述,没有超类和子类。 SUPPLIES_PHARMACEUTICALSUPPLIES_SURGICALSUPPLIES_NONSURGICAL 只是表,彼此没有联系。

所有启用的约束都被强制执行。将它们定义为可延迟只是将验证推迟到下一个commit,这并没有真正改变您的示例中的任何内容。如果您在列上定义三个约束,则没有语法或机制可以说“只需要强制执行这些约束中的一个”,我看不出这样的系统将如何工作.

在数据建模中,您可以使用任一定义子类型

  1. 带有category 或类似指示列的单个表(例如SUPPLIES),或
  2. 父表仅具有公共列,其下的子表仅具有特定于类型的列,通过外键约束链接回父表。子表可以在 FK 列上具有唯一键或主键,使其成为可选的 1:1 关系。

然后,其他表可以根据需要具有引用父级或其子级之一的 FK 约束。

【讨论】:

    【解决方案2】:

    这是您修改后的原始代码(大写原始,小写:修改),使用 @William Robertson 的 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 
    

    插入

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

    现在,一些 SELECT ...

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-06
      • 2017-07-10
      • 2014-05-31
      • 2014-08-22
      • 2010-10-05
      • 1970-01-01
      • 2023-04-06
      相关资源
      最近更新 更多