В качестве альтернативы вы можете использовать триггер, чтобы проверить ограничение:
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)
Это решение не является полностью надежным. Например, триггер может давать ложные отрицательные или ложные положительные результаты, потому что он не будет «видеть» одновременные обновления, производимые другими транзакциями (например, чтобы изменить ресторан, с которым связана поставка или пункт меню). Чтобы быть надежным, триггерное решение должно использовать блокировку для предотвращения таких обновлений. –
Истинно, и OP должен рассмотреть, возможно ли это, прежде чем использовать этот вариант; но мне кажется маловероятным (исходя из предположений, которые я делаю о наборе данных), что вы получите эти изменения, поскольку я не знаю ресторанов, которые делают доставку, когда доставка внезапно переместится в другой ресторан. @ Ответ Винсента - лучший вариант, но если дизайн таблиц неизменен, то это невозможно, это вариант. – MT0