【问题标题】:SQL database is not starting because primary keysSQL 数据库未启动,因为主键
【发布时间】:2022-01-14 16:30:12
【问题描述】:

SQL 数据库不会初始化,因为所有表都依赖于不同表中的主键/外键。有没有办法让它编译而不破坏?

附上代码和ERD架构

ERD

CREATE TABLE boat 
(
    huID int NOT NULL,
    manufacturer varchar(50) NOT NULL,
    Model varchar(20) NOT NULL,
    Year int NOT NULL,
    Price decimal(5,2) NOT NULL,
    color varchar(30) NOT NULL,
    condition varchar(20),
    CHECK (condition = 'New' OR condition = 'Used') ,

    CONSTRAINT boat_huID_fk 
        FOREIGN KEY (huID) REFERENCES Holding(huID),
    CONSTRAINT boat_Price_pk PRIMARY KEY (Price)
);

CREATE TABLE Customer 
(
    customerID int,
    Name varchar(50) PRIMARY KEY,
    Address varchar(50) NOT NULL,
    Phone int NOT NULL,

    CONSTRAINT Customer_customerID_fk 
        FOREIGN KEY (customerID) REFERENCES invoice(customerID)
);

CREATE TABLE Holding
(
    huID int NOT NULL,

    CONSTRAINT boat_huID_pk PRIMARY KEY (huID)
);

CREATE TABLE Salesperson 
(
    salespersonID int PRIMARY KEY,
    Name varchar(50) NOT NULL,

    CONSTRAINT Customer_Name_fk 
        FOREIGN KEY (Name) REFERENCES Customer (Name) 
);

CREATE TABLE invoice 
(
    invoiceNumber int,
    customerID int NOT NULL,
    salespersonID int,
    Price int NOT NULL,
    huID int NOT NULL
        CONSTRAINT boat_huID_fk REFERENCES Holding(huID),

    CONSTRAINT boat_Price_pk 
        FOREIGN KEY (Price) REFERENCES boat(Price),
    CONSTRAINT Salesperson_salespersonID_fk 
        FOREIGN KEY (salespersonID) REFERENCES Salesperson (salespersonID),
    CONSTRAINT Customer_customerID_pk PRIMARY KEY(customerID)
);

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    您必须按顺序创建表,以便引用其他表的表不会在之前创建。

    在你的情况下,

    • 重新排序CREATE TABLE 语句,
    • salesperson 中删除外键约束(不完全;在脚本末尾使用alter table 创建它)和
    • 重命名salesperson 表中的约束(您以一种奇怪的方式命名它们,而不是唯一的)

    完成这项工作。

    SQL> CREATE TABLE holding (
      2      huid INT NOT NULL,
      3      CONSTRAINT boat_huid_pk PRIMARY KEY ( huid )
      4  );
    
    Table created.
    
    SQL>
    SQL> CREATE TABLE boat (
      2      huid         INT NOT NULL,
      3      manufacturer VARCHAR(50) NOT NULL,
      4      model        VARCHAR(20) NOT NULL,
      5      year         INT NOT NULL,
      6      price        DECIMAL(5, 2) NOT NULL,
      7      color        VARCHAR(30) NOT NULL,
      8      condition    VARCHAR(20),
      9      CHECK ( condition = 'New'
     10              OR condition = 'Used' ),
     11      CONSTRAINT boat_huid_fk FOREIGN KEY ( huid )
     12          REFERENCES holding ( huid ),
     13      CONSTRAINT boat_price_pk PRIMARY KEY ( price )
     14  );
    
    Table created.
    
    SQL>
    SQL> CREATE TABLE salesperson (
      2      salespersonid INT PRIMARY KEY,
      3      name          VARCHAR(50) NOT NULL
      4  --    CONSTRAINT Customer_Name_fk
      5  --        FOREIGN KEY (Name) REFERENCES Customer (Name)
      6  );
    
    Table created.
    
    SQL>
    SQL> CREATE TABLE invoice (
      2      invoicenumber INT,
      3      customerid    INT NOT NULL,
      4      salespersonid INT,
      5      price         INT NOT NULL,
      6      huid          INT NOT NULL
      7          CONSTRAINT inv_huid_fk
      8              REFERENCES holding ( huid ),
      9      CONSTRAINT inv_price_pk FOREIGN KEY ( price )
     10          REFERENCES boat ( price ),
     11      CONSTRAINT salesperson_salespersonid_fk FOREIGN KEY ( salespersonid )
     12          REFERENCES salesperson ( salespersonid ),
     13      CONSTRAINT customer_customerid_pk PRIMARY KEY ( customerid )
     14  );
    
    Table created.
    
    SQL>
    SQL> CREATE TABLE customer (
      2      customerid INT,
      3      name       VARCHAR(50) PRIMARY KEY,
      4      address    VARCHAR(50) NOT NULL,
      5      phone      INT NOT NULL,
      6      CONSTRAINT customer_customerid_fk FOREIGN KEY ( customerid )
      7          REFERENCES invoice ( customerid )
      8  );
    
    Table created.
    
    SQL>
    SQL> ALTER TABLE salesperson
      2      ADD CONSTRAINT customer_name_fk FOREIGN KEY ( name )
      3          REFERENCES customer ( name );
    
    Table altered.
    
    SQL>
    

    【讨论】:

      【解决方案2】:

      当您有循环外键约束时,使用ALTER 分别定义一个循环约束。

      模式是:

      CREATE TABLE T1 (
        ID INT PRIMARY KEY,
        ... -- other cols
      );
      
      CREATE TABLE T2 (
        ID INT PRIMARY KEY,
        ..., -- other cols
        CONSTRAINT T1_FK FOREIGN KEY (ID) REFERENCES T1(ID)
      );
      

      使用 FK 为先前定义的表创建其他表。

      然后通过ALTER从第一张桌子到最后一张桌子用FK完成圆圈:

      ALTER TABLE T1 ADD CONSTRAINT T2_FK FOREIGN KEY (ID) REFERENCES T2(ID);
      

      或者,如果很难跟踪什么取决于什么,您可以使用 ALTER 定义 all 外键。

      【讨论】:

      • 我想说解决这种循环依赖的更好方法是完全重新设计模型。因为这个相同的循环依赖将不允许在没有insert all 或延迟约束的情况下插入任何内容,或者不允许删除行。从salesperson 到(非唯一)customer.name 的奇怪引用和从customerinvoice 的(非常混乱的)引用是另一个问题,但它们超出了循环依赖技术问题的范围
      • @astentx 公平评论 - ERM 似乎令人费解。该答案仅解决了所述问题。插入没问题 - 只需在单个事务中插入即可。
      【解决方案3】:

      此外,您可能希望在 cascade 子句中添加上述 cmets,因此如果您删除父行,所有相关的底层行也将被删除。下面是一个测试用例,慎用。

      此外,我还提供了一个查询,您可能想用它来告诉您相关的内容。在我的示例中,我对表名“PARENT”进行了硬编码,但您可能希望使用绑定变量。祝你好运。

      
      create table parent (
        id NUMBER(10),
        value      varchar2(30),
      constraint parent_pk primary key (id)
      );
      
      CREATE TABLE child
      ( id NUMBER(10) not null,
      value NUMBER(10) not null,
      constraint child_pk primary key (id,value),
      CONSTRAINT parent_child_fk
      FOREIGN KEY (id)
      REFERENCES parent(id)
      ON DELETE CASCADE
      );
      
      CREATE TABLE grandchild
      ( id NUMBER(10) not null,
      value NUMBER(10) not null,
      constraint grandchild_pk primary key (id,value),
      CONSTRAINT child_grandchild_fk
      FOREIGN KEY (id,value)
      REFERENCES child(id,value)
      ON DELETE CASCADE
      );
      
      
      insert into parent values (1,'a');
      insert into parent values (2,'b');
      insert into parent values (3,'c');
      
      insert into child  values (1,1);
      insert into child  values (1,2);
      insert into child  values (1,3);
      insert into child  values (2,1);
      insert into child  values (2,2);
      insert into child  values (2,3);
      insert into child  values (3,1);
      insert into child  values (3,2);
      insert into child  values (3,3);
      
      insert into grandchild  values (1,1);
      insert into grandchild  values (1,2);
      insert into grandchild  values (1,3);
      insert into grandchild  values (2,1);
      insert into grandchild  values (2,2);
      insert into grandchild  values (2,3);
      insert into grandchild  values (3,1);
      insert into grandchild  values (3,2);
      insert into grandchild  values (3,3);
      
      SELECT  (
              SELECT COUNT(*)
              FROM   parent 
              ) AS parent_cnt,
             (
              SELECT COUNT(*)
              FROM   child 
              ) AS child_cnt,
              (
              SELECT COUNT(*)
              FROM   grandchild
              ) AS grandchild_cnt
      FROM    dual
      
      PARENT_CNT    CHILD_CNT    GRANDCHILD_CNT
      3    9    9
      
      DELETE from parent where value = 'a';
      
      
      SELECT  (
              SELECT COUNT(*)
              FROM   parent 
              ) AS parent_cnt,
             (
              SELECT COUNT(*)
              FROM   child 
              ) AS child_cnt,
              (
              SELECT COUNT(*)
              FROM   grandchild
              ) AS grandchild_cnt
      FROM    dual
      
      PARENT_CNT    CHILD_CNT    GRANDCHILD_CNT
      2    6    6
      
      Here is a query you can use to show the relationships
      
      with f as (
              select constraint_name, table_name, r_constraint_name
              from   user_constraints
              where  constraint_type = 'R'
           ),
           p as (
              select constraint_name, table_name
              from   user_constraints
              where  constraint_type = 'P'
           ),
           j (child_table, f_key, parent_table, p_key) as (
              select f.table_name, f.constraint_name, p.table_name, f.r_constraint_name
              from   p join f on p.constraint_name = f.r_constraint_name
              union all
              select 'PARENT', (select constraint_name from p                                where table_name = 'PARENT'), null, null from dual
           )
      select level as lvl, j.*
      from j
      start with parent_table is null
      connect by nocycle parent_table = prior child_table
      order by lvl, parent_table, child_table;
      
      LVL    CHILD_TABLE    F_KEY    PARENT_TABLE    P_KEY
      1    PARENT    PARENT_PK     -      - 
      2    CHILD    PARENT_CHILD_FK    PARENT    PARENT_PK
      3    GRANDCHILD    CHILD_GRANDCHILD_FK    CHILD    CHILD_PK
      
      

      所以现在当我们从父表中删除时,Oracle 将检查是否有 FK 引用父表的表,并会找到表子表。然后它将一一检查要删除的表父行是否在表子中具有子行。如果不是,它将删除该表父行。如果有子级 if 将检查 FK 操作,并且由于该 FK 具有 ON DELETE CASCADE,Oracle 将尝试删除该表的子行。那时它将(与表父表相同)检查是否有 FK 引用表子表的表,并会找到表孙子表。同样,如果要删除的表子行在表孙子中是否有子项,它将一一检查。如果不是,它将删除该表子行。如果有子级 if 将检查 FK 操作,并且由于该 FK 具有 ON DELETE CASCADE,Oracle 将尝试删除该表的孙行。由于表grandchild 没有子表,Oracle 将删除表grandchild、child 和parent 中的相应行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-21
        • 1970-01-01
        相关资源
        最近更新 更多