SQL主键斗争

时间:2015-11-11 18:17:40

标签: sql oracle

我正在尝试为供应商组创建一个表,当我尝试创建它时会抛出错误。

以下是代码:

CREATE TABLE GROUPS_PLUS_SUPPLIERS  
(
    PRODUCT_GROUP_SUPPLIER_ID NUMBER(3),
    GROUP_ID NUMBER(4),    
    GROUP_NAME VARCHAR2(255),   
    SUPPLIER_ID NUMBER(4),    

    CONSTRAINT PRODUCT_GROUP_SUPPLIER_ID_PK PRIMARY KEY (PRODUCT_GROUP_SUPPLIER_ID),   
    CONSTRAINT FK_SUPPLIER_ID FOREIGN KEY (SUPPLIER_ID) REFERENCES SUPPLIERS (SUPPLIER_ID)
);

CREATE TABLE PRODUCTS
(
    PRODUCT_ID NUMBER (4),
    PRODUCT_DESCRIPTION VARCHAR2 (255),
    PRODUCT_SIZE VARCHAR2 (10),
    PRODUCT_GROUP NUMBER (4),
    PRODUCT_PRICE NUMBER(4),
    NO_IN_STOCK NUMBER (4),
    REORDER_LEVEL NUMBER (4),

    CONSTRAINT PRODUCTS_ID_PK PRIMARY KEY (PRODUCT_ID),
    CONSTRAINT FK_GROUP_PLUS_SUPPLIERS_ID FOREIGN KEY (PRODUCT_GROUP) REFERENCES GROUPS_PLUS_SUPPLIERS(GROUP_ID)
);

这是我收到的错误消息:

  

ORA-02270:此列列表没有匹配的唯一键或主键

以下是我要在群组加供应商中添加的内容:

INSERT INTO GROUPS_PLUS_SUPPLIERS VALUES (1,705,'flavoured oil',5588);
INSERT INTO GROUPS_PLUS_SUPPLIERS VALUES (2,705,'flavoured oil',5509);
INSERT INTO GROUPS_PLUS_SUPPLIERS VALUES (3,800,'spice',5543);
INSERT INTO GROUPS_PLUS_SUPPLIERS VALUES (4,800,'spice',5579);
INSERT INTO GROUPS_PLUS_SUPPLIERS VALUES (5,800,'spice',5584);

任何帮助将不胜感激!

4 个答案:

答案 0 :(得分:1)

错误消息显示您缺少的内容。 GROUPS_PLUS_SUPPLIERS(GROUP_ID)的外键意味着您需要在此列上添加唯一索引。

CREATE TABLE GROUPS_PLUS_SUPPLIERS  
(
    PRODUCT_GROUP_SUPPLIER_ID NUMBER(3),
    GROUP_ID NUMBER(4),    
    GROUP_NAME VARCHAR2(255),   
    SUPPLIER_ID NUMBER(4),    

    CONSTRAINT PRODUCT_GROUP_SUPPLIER_ID_PK PRIMARY KEY (PRODUCT_GROUP_SUPPLIER_ID),   
    CONSTRAINT FK_SUPPLIER_ID FOREIGN KEY (SUPPLIER_ID) REFERENCES SUPPLIERS (SUPPLIER_ID),
    CONSTRAINT AK_GROUP_ID UNIQUE(GROUP_ID)
);

答案 1 :(得分:1)

FK_GROUP_PLUS_SUPPLIERS_ID表上的约束PRODUCTS不应引用GROUPS_PLUS_SUPPLIERS(GROUP_ID)。它应该引用GROUPS_PLUS_SUPPLIERS(PRODUCT_GROUP_SUPPLIER_ID)

...
CONSTRAINT FK_GROUP_PLUS_SUPPLIERS_ID FOREIGN KEY (PRODUCT_GROUP) REFERENCES GROUPS_PLUS_SUPPLIERS(PRODUCT_GROUP_SUPPLIER_ID)
...

如果你真的想要成为GROUP_ID的FK,那么应该有一个GROUP表,其中GROUP_ID是主键,你应该参考。< / p>

...
CONSTRAINT FK_GROUP_PLUS_SUPPLIERS_ID FOREIGN KEY (PRODUCT_GROUP) REFERENCES GROUP(GROUP_ID)
...

答案 2 :(得分:1)

由于您的GROUP_ID列不是唯一的,并且无法对非唯一值进行外键约束(它与哪一行有关?),并且您的GROUPS_PLUS_SUPPLIERS表也使用了SUPPLIER_ID,那么你必须有一个复合外键:

CREATE TABLE GROUPS_PLUS_SUPPLIERS (
    PRODUCT_GROUP_SUPPLIER_ID NUMBER(3), -- better not to have this column
    GROUP_ID NUMBER(4),
    GROUP_NAME VARCHAR2(255), -- violates 2nd normal form!
    SUPPLIER_ID NUMBER(4),

    CONSTRAINT PRODUCT_GROUP_SUPPLIER_ID_PK PRIMARY KEY (PRODUCT_GROUP_SUPPLIER_ID),
    CONSTRAINT FK_SUPPLIER_ID FOREIGN KEY (SUPPLIER_ID) REFERENCES SUPPLIERS (SUPPLIER_ID)
    -- A new constraint
    CONSTRAINT UQ_PRODUCT_GROUP_SUPPLIER_ID_GROUP_ID UNIQUE (SUPPLIER_ID, GROUP_ID)
);

CREATE TABLE PRODUCTS (
    PRODUCT_ID NUMBER (4),
    PRODUCT_DESCRIPTION VARCHAR2 (255),
    PRODUCT_SIZE VARCHAR2 (10),
    PRODUCT_GROUP NUMBER (4),
    PRODUCT_PRICE NUMBER(4),
    NO_IN_STOCK NUMBER (4),
    REORDER_LEVEL NUMBER (4),

    CONSTRAINT PRODUCTS_ID_PK PRIMARY KEY (PRODUCT_ID),
    -- a changed constraint
    CONSTRAINT FK_GROUPS_PLUS_SUPPLIERS_SUPPLIER_ID_GROUP_ID 
       FOREIGN KEY ( SUPPLIER_ID, PRODUCT_GROUP)
       REFERENCES GROUPS_PLUS_SUPPLIERS(SUPPLIER_ID, GROUP_ID)
);

我可能不在此建议的基础上,而FK需要指向GROUPS表,如果它不存在,您需要创建该表。这也是让你的FK指向PRODUCT_GROUP_SUPPLIER_ID列的可能解决方案,但我保证你这样做会给你带来严重的问题,当你发现要逐个查询你的产品时,你将永远被迫加入另一张桌子。我非常自信地预测,如果你这样做,你会深感遗憾。

数据库设计也存在一些严重问题。

  1. GROUPS_PLUS_SUPPLIERS表中查看您想要的更新示例,GROUP_NAME位于该表中非常糟糕,因为这违反了second normal form。您需要一个GROUPS表,其中包含GROUP_IDGROUP_NAME列。

  2. GROUPS_PLUS_SUPPLIERS表似乎是一个多对多连接表,几乎可以肯定不需要自己的ID列。我保证在99%的情况下这是真的,并且对于使用复合键,引用此逻辑关系(SUPPLIER_IDGROUP_ID之间的唯一关系)的任何其他表更好。你会为此感谢我。

  3. NUMBER(4)似乎非常低。您确定永远不会有超过9999种产品吗?即使是供应商,这听起来也太低了。以后要让自己头疼不已,并使其足够大,以适应真实的企业级场景。

  4. 另外,请原谅我,如果我不是免费的,但你的命名方案需要一些工作。我知道一些旧版本的Oracle需要全部大写,并且不能处理小写,所以我想如果你正在使用它,那就忽略那部分。

    1. 请勿使用带有下划线的所有大写字母。在至少使用Pascal Case,或使用带下划线的所有小写。最好的是GroupIDGroupId等等。
    2. 在所有表中将列命名为相同。不要在一个表中将其称为GROUP_ID,而在另一个表中将其称为PRODUCT_GROUP。坦率地说,不这样做是荒谬的,并且会导致未来的开发者对你产生混淆和仇恨。在你最好不知道你的地址后,下一个开发人员可以使用这个数据库!
    3. GROUPS_PLUS_SUPPLIERS是不必要的罗嗦。只需使用SUPPLIERS_GROUPS
    4. 不要在每个可能的列之前放置表名。只是不要。我认为可以使用许多表中常见的列名,例如Description,但不要为其他表执行此操作。 (事实上​​,这就是为什么在我自己的表中我完全停止使用DescrDescription,现在只需将列称为表名的单数 - 尽管我的表也是单数的,例如, ProductStatus表格的名称列为ProductStatus,而不是ProductStatusDescriptionDescription或其他一些怪物。)
    5. 以下是我认为更明智的命名方案和数据库设计的样子:

      CREATE TABLE Groups (
         GroupID NUMBER(4),
         GroupName VARCHAR2(255), -- I would normally call this Group but that's reserved
      
         CONSTRAINT PK_Groups PRIMARY KEY (GroupID),
         CONSTRAINT UQ_Groups_GroupName UNIQUE (GroupName)
      );
      
      CREATE TABLE SupplierGroups (
          GroupID NUMBER(4),    
          SupplierID NUMBER(4),
      
          CONSTRAINT PK_SupplierGroups PRIMARY KEY (SUPPLIER_ID, GROUP_ID),   
          CONSTRAINT FK_SupplierID FOREIGN KEY (SupplierID) REFERENCES Suppliers (SupplierID),
          CONSTRAINT FK_GroupID FOREIGN KEY (GroupID) REFERENCES Groups (GroupID)
      );
      
      CREATE TABLE Products (
          ProductID NUMBER (4),
          ProductDescription VARCHAR2 (255),
          Size VARCHAR2 (10),
          GroupID NUMBER (4),
          Price NUMBER(4),
          NoInStock NUMBER (4),
          ReorderLevel NUMBER (4),
      
          CONSTRAINT PK_Products PRIMARY KEY (ProductID),
          CONSTRAINT FK_Products_SupplierID_GroupID FOREIGN KEY (SupplierID, GroupID)
             REFERENCES SupplierGroups (SupplierID, GroupID)
      );
      

答案 3 :(得分:-1)

如果缺乏唯一性是导致错误的真正原因,那么您必须使用复合PK来确保唯一性:

CREATE TABLE GROUPS_PLUS_SUPPLIERS  
(
    PRODUCT_GROUP_SUPPLIER_ID NUMBER(3),
    GROUP_ID NUMBER(4),    
    GROUP_NAME VARCHAR2(255),   
    SUPPLIER_ID NUMBER(4),    

    CONSTRAINT GROUPS_PLUS_SUPPLIERS_PK PRIMARY KEY (PRODUCT_GROUP_SUPPLIER_ID,GROUP_ID,GROUP_NAME,SUPPLIER_ID),   
    CONSTRAINT FK_SUPPLIER_ID FOREIGN KEY (SUPPLIER_ID) REFERENCES SUPPLIERS (SUPPLIER_ID)
);

这将允许您添加重复的GROUP_ID值,但仍满足所需的匹配唯一性。