2013-11-29 3 views
3

Я начинающий SQL, и я не могу понять, как правильно создать ограничение целостности для таких ситуаций: enter image description hereIntegrity ограничения для таблиц не сразу связанные

Схема описывает систему доставки - каждый ресторан предлагает некоторые предметы, которые могут быть доставлены клиентам (вне видимой схемы).

Проблема возникает с таблицей in_delivery - элементы из меню регистрируются при доставке через эту таблицу. С текущим состоянием можно добавить menu_item в delivery, который делается restaurant, но этот ресторан может не предложить menu_item!

При вставке в in_delivery, мне нужно как-то проверить, если Menu_Item_MenuItem_ID присутствует в offers, что имеет Restaurant_RestaurantID равный RestaurantID в Delivery, связанных с таблицей.

Я не знаю, могу ли я использовать внешний ключ здесь, потому что таблицы не «смежные» ..

Что приходит на ум, чтобы иметь в in_delivery в RestaurantID, что бы внешний ключ оба - до Restaurant и Delivery. Тогда я мог найти это в offers. Есть ли способ лучше?

Спасибо за вашу помощь

ответ

2

Вы можете применять ваши ограничения со следующими изменениями:

  1. добавить restaurant_id колонки в in_delivery таблице
  2. добавить уникальное ограничение на delivery (delivery_id, restaurant_id) (требуется для 3.)
  3. изменить внешний ключ от in_delivery -> delivery до (delivery_id, restaurant_id)
  4. изменение внешнего ключа от in_delivery -> menu_item к in_delivery -> offers
0

В качестве альтернативы вы можете использовать триггер, чтобы проверить ограничение:

SQL Fiddle

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) 
) 
/

Просто для удобства чтения я создал две полезные функции (вы, вероятно, захотите ионная обработка в них):

CREATE OR REPLACE FUNCTION get_Delivery_RestaurantID (
    p_Delivery_ID Deliveries.Delivery_ID%TYPE 
) RETURN Restaurants.RestaurantID%TYPE 
AS 
    v_RestaurantID Restaurants.RestaurantID%TYPE; 
BEGIN 
    SELECT RestaurantID 
    INTO v_RestaurantID 
    FROM Deliveries 
    WHERE Delivery_ID = p_Delivery_ID; 

    RETURN v_RestaurantID; 
END get_Delivery_RestaurantID; 
/

CREATE OR REPLACE FUNCTION does_Restaurant_Offer_Item (
    p_RestaurantID Restaurants.RestaurantID%TYPE, 
    p_Menu_Item_ID Menu_Items.Menu_Item_ID%TYPE 
) RETURN NUMBER 
AS 
    v_exists NUMBER(1); 
BEGIN 
    SELECT CASE WHEN EXISTS (SELECT 1 
          FROM Offers 
          WHERE RestaurantID = p_RestaurantID 
          AND Menu_Item_ID = p_Menu_Item_ID 
         ) 
     THEN 1 
     ELSE 0 
     END 
    INTO v_exists 
    FROM DUAL; 

    RETURN v_exists; 
END does_Restaurant_Offer_Item; 
/

Затем просто добавить триггер на таблицу, чтобы проверить, что ресторан предлагает пункт и, если нет, сгенерирует исключение.

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:

SELECT * FROM in_delivery 

Results:

| 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) 
+2

Это решение не является полностью надежным. Например, триггер может давать ложные отрицательные или ложные положительные результаты, потому что он не будет «видеть» одновременные обновления, производимые другими транзакциями (например, чтобы изменить ресторан, с которым связана поставка или пункт меню). Чтобы быть надежным, триггерное решение должно использовать блокировку для предотвращения таких обновлений. –

+0

Истинно, и OP должен рассмотреть, возможно ли это, прежде чем использовать этот вариант; но мне кажется маловероятным (исходя из предположений, которые я делаю о наборе данных), что вы получите эти изменения, поскольку я не знаю ресторанов, которые делают доставку, когда доставка внезапно переместится в другой ресторан. @ Ответ Винсента - лучший вариант, но если дизайн таблиц неизменен, то это невозможно, это вариант. – MT0

Смежные вопросы