Sql 不直接相关的表的完整性约束

Sql 不直接相关的表的完整性约束,sql,oracle,database-design,constraints,Sql,Oracle,Database Design,Constraints,我是一名SQL初学者,我不知道如何正确地为以下情况创建完整性约束: 该模式描述了一个交付系统——每个餐厅都提供一些可以交付给客户的物品(在可见模式之外) 问题出现在in_delivery表中-菜单中的项目通过此表与交货一起注册。根据当前情况,可以将菜单项添加到由餐厅完成的配送,但该餐厅可能不提供菜单项 当在配送中插入时,我需要以某种方式检查菜单项\u MenuItem\u ID是否存在于offers中,该菜单项与表关联的RestaurantID等于RestaurantID 我不知道这里是否可以

我是一名SQL初学者,我不知道如何正确地为以下情况创建完整性约束:

该模式描述了一个交付系统——每个餐厅都提供一些可以交付给客户的物品(在可见模式之外)

问题出现在
in_delivery
表中-菜单中的项目通过此表与交货一起注册。根据当前情况,可以将
菜单项
添加到由
餐厅
完成的
配送
,但该餐厅可能不提供菜单项

当在配送中插入
时,我需要以某种方式检查
菜单项\u MenuItem\u ID
是否存在于
offers
中,该菜单项与表关联的
RestaurantID
等于
RestaurantID

我不知道这里是否可以使用外键,因为表不是“相邻的”

我们想到的是在
的配送中有一家
餐厅
,这将是
餐厅
配送
的外键。然后我可以在
中找到它。有更好的办法吗


感谢您的帮助

您可以通过以下更改强制执行约束:

  • 在送货表的
    中添加
    餐厅id
  • 配送(配送id、餐厅id)
    上添加唯一约束(3需要)
  • 将外键从配送->配送中的
    更改为指向
    (配送标识,餐厅标识)
  • 将外键从配送->菜单项中的
    更改为配送->优惠中的

  • 或者,可以使用触发器检查约束:

    Oracle 11g R2架构设置

    CREATE TABLE Restaurants (
      RestaurantID NUMBER(2) PRIMARY KEY,
      Name         VARCHAR2(30) NOT NULL
    )
    /
    
    INSERT INTO Restaurants
              SELECT 1, 'Soylent Green Express' FROM DUAL
    UNION ALL SELECT 2, 'Helga''s House of Ribs' FROM DUAL
    /
    
    CREATE TABLE Menu_Items (
      Menu_Item_ID NUMBER(2) PRIMARY KEY,
      Name         VARCHAR2(20) NOT NULL
    )
    /
    
    INSERT INTO Menu_Items
              SELECT 1, 'Soylent Green' FROM DUAL
    UNION ALL SELECT 2, 'Ribs' FROM DUAL
    /
    
    CREATE TABLE Offers (
      RestaurantID NUMBER(2),
      Menu_Item_ID NUMBER(2),
      PRIMARY KEY ( RestaurantID, Menu_Item_ID ),
      FOREIGN KEY ( RestaurantID ) REFERENCES Restaurants ( RestaurantID ),
      FOREIGN KEY ( Menu_Item_ID ) REFERENCES Menu_Items ( Menu_Item_ID )
    )
    /
    
    INSERT INTO Offers
              SELECT 1, 1 FROM DUAL
    UNION ALL SELECT 2, 2 FROM DUAL
    /
    
    CREATE TABLE Deliveries (
      RestaurantID NUMBER(2) NOT NULL,
      Delivery_ID  NUMBER(2) PRIMARY KEY,
      FOREIGN KEY ( RestaurantID ) REFERENCES Restaurants ( RestaurantID )
    )
    /
    
    INSERT INTO Deliveries
              SELECT 1, 1 FROM DUAL
    UNION ALL SELECT 2, 2 FROM DUAL
    /
    
    CREATE TABLE in_delivery (
      Delivery_ID NUMBER(2),
      Menu_Item_ID NUMBER(2),
      PRIMARY KEY ( Delivery_ID, Menu_Item_ID ),
      FOREIGN KEY ( Delivery_ID ) REFERENCES Deliveries ( Delivery_ID ),
      FOREIGN KEY ( Menu_Item_ID ) REFERENCES Menu_Items ( Menu_Item_ID )
    )
    /
    
    SELECT * FROM in_delivery
    
    | DELIVERY_ID | MENU_ITEM_ID |
    |-------------|--------------|
    |           1 |            1 |
    |           2 |            2 |
    
    为了便于阅读,我创建了两个有用的函数(您可能希望其中包含一些异常处理):

    然后,只需在表中添加一个触发器,检查餐厅是否提供该项目,如果没有,则引发异常

    CREATE TRIGGER check_Valid_Delivery_Item
    BEFORE INSERT OR UPDATE OF Delivery_ID, Menu_Item_ID
    ON in_delivery
    FOR EACH ROW
    BEGIN
       IF does_restaurant_Offer_Item( get_Delivery_RestaurantID( :new.Delivery_ID ), :new.Menu_Item_ID ) = 0
       THEN
         RAISE_APPLICATION_ERROR (-20100, 'Invalid Delivery Item');
       END IF;
    END check_Valid_Delivery_Item;
    /
    
    INSERT INTO in_delivery VALUES( 1, 1 )
    /
    
    INSERT INTO in_delivery VALUES( 2, 2 )
    /
    
    查询1

    CREATE TABLE Restaurants (
      RestaurantID NUMBER(2) PRIMARY KEY,
      Name         VARCHAR2(30) NOT NULL
    )
    /
    
    INSERT INTO Restaurants
              SELECT 1, 'Soylent Green Express' FROM DUAL
    UNION ALL SELECT 2, 'Helga''s House of Ribs' FROM DUAL
    /
    
    CREATE TABLE Menu_Items (
      Menu_Item_ID NUMBER(2) PRIMARY KEY,
      Name         VARCHAR2(20) NOT NULL
    )
    /
    
    INSERT INTO Menu_Items
              SELECT 1, 'Soylent Green' FROM DUAL
    UNION ALL SELECT 2, 'Ribs' FROM DUAL
    /
    
    CREATE TABLE Offers (
      RestaurantID NUMBER(2),
      Menu_Item_ID NUMBER(2),
      PRIMARY KEY ( RestaurantID, Menu_Item_ID ),
      FOREIGN KEY ( RestaurantID ) REFERENCES Restaurants ( RestaurantID ),
      FOREIGN KEY ( Menu_Item_ID ) REFERENCES Menu_Items ( Menu_Item_ID )
    )
    /
    
    INSERT INTO Offers
              SELECT 1, 1 FROM DUAL
    UNION ALL SELECT 2, 2 FROM DUAL
    /
    
    CREATE TABLE Deliveries (
      RestaurantID NUMBER(2) NOT NULL,
      Delivery_ID  NUMBER(2) PRIMARY KEY,
      FOREIGN KEY ( RestaurantID ) REFERENCES Restaurants ( RestaurantID )
    )
    /
    
    INSERT INTO Deliveries
              SELECT 1, 1 FROM DUAL
    UNION ALL SELECT 2, 2 FROM DUAL
    /
    
    CREATE TABLE in_delivery (
      Delivery_ID NUMBER(2),
      Menu_Item_ID NUMBER(2),
      PRIMARY KEY ( Delivery_ID, Menu_Item_ID ),
      FOREIGN KEY ( Delivery_ID ) REFERENCES Deliveries ( Delivery_ID ),
      FOREIGN KEY ( Menu_Item_ID ) REFERENCES Menu_Items ( Menu_Item_ID )
    )
    /
    
    SELECT * FROM in_delivery
    
    | DELIVERY_ID | MENU_ITEM_ID |
    |-------------|--------------|
    |           1 |            1 |
    |           2 |            2 |
    

    CREATE TABLE Restaurants (
      RestaurantID NUMBER(2) PRIMARY KEY,
      Name         VARCHAR2(30) NOT NULL
    )
    /
    
    INSERT INTO Restaurants
              SELECT 1, 'Soylent Green Express' FROM DUAL
    UNION ALL SELECT 2, 'Helga''s House of Ribs' FROM DUAL
    /
    
    CREATE TABLE Menu_Items (
      Menu_Item_ID NUMBER(2) PRIMARY KEY,
      Name         VARCHAR2(20) NOT NULL
    )
    /
    
    INSERT INTO Menu_Items
              SELECT 1, 'Soylent Green' FROM DUAL
    UNION ALL SELECT 2, 'Ribs' FROM DUAL
    /
    
    CREATE TABLE Offers (
      RestaurantID NUMBER(2),
      Menu_Item_ID NUMBER(2),
      PRIMARY KEY ( RestaurantID, Menu_Item_ID ),
      FOREIGN KEY ( RestaurantID ) REFERENCES Restaurants ( RestaurantID ),
      FOREIGN KEY ( Menu_Item_ID ) REFERENCES Menu_Items ( Menu_Item_ID )
    )
    /
    
    INSERT INTO Offers
              SELECT 1, 1 FROM DUAL
    UNION ALL SELECT 2, 2 FROM DUAL
    /
    
    CREATE TABLE Deliveries (
      RestaurantID NUMBER(2) NOT NULL,
      Delivery_ID  NUMBER(2) PRIMARY KEY,
      FOREIGN KEY ( RestaurantID ) REFERENCES Restaurants ( RestaurantID )
    )
    /
    
    INSERT INTO Deliveries
              SELECT 1, 1 FROM DUAL
    UNION ALL SELECT 2, 2 FROM DUAL
    /
    
    CREATE TABLE in_delivery (
      Delivery_ID NUMBER(2),
      Menu_Item_ID NUMBER(2),
      PRIMARY KEY ( Delivery_ID, Menu_Item_ID ),
      FOREIGN KEY ( Delivery_ID ) REFERENCES Deliveries ( Delivery_ID ),
      FOREIGN KEY ( Menu_Item_ID ) REFERENCES Menu_Items ( Menu_Item_ID )
    )
    /
    
    SELECT * FROM in_delivery
    
    | DELIVERY_ID | MENU_ITEM_ID |
    |-------------|--------------|
    |           1 |            1 |
    |           2 |            2 |
    
    如果您尝试这样做:

    INSERT INTO in_delivery VALUES( 1, 2 );
    
    然后你会得到:

    ORA-20100: Invalid Delivery Item ORA-06512: at "USER_4_F9593.CHECK_VALID_DELIVERY_ITEM", line 4 ORA-04088: error during execution of trigger 'USER_4_F9593.CHECK_VALID_DELIVERY_ITEM' : INSERT INTO in_delivery VALUES( 1, 2 )
    

    这个解决方案并不完全可靠。例如,触发器可能会给出假阴性或假阳性结果,因为它不会“看到”其他事务同时进行的更新(例如,更改与配送或菜单项相关的餐厅)。为了保持健壮性,基于触发器的解决方案需要使用锁定来防止这种更新。但在我看来(基于我对数据集的假设),你不太可能得到这些变化,因为我不知道有哪家餐馆会送货,送货会突然转移到另一家餐馆@Vincent的答案是更好的选择,但是如果表的设计是不变的,那么这是不可能的,这是一个选择。