Я не хочу вставлять элемент-1 и элемент-3 одновременно при покупке
Проблема в том, что Oracle не понимает концепцию в то же время . Он понимает транзакции, он понимает операторы DML, он понимает уникальные ключи. Поэтому нам нужно сформулировать ваш вопрос в терминах, понятных Oracle: например, данная покупка не может иметь более одного товара от одного поставщика .
Ваша первая проблема в том, что ваша модель данных не может поддерживать такое правило. Ваша таблица cpurchase
имеет первичный ключ purchaseid
, что означает, что у вас есть одна запись на каждый купленный предмет. Не существует набора приобретенных предметов, в отношении которых мы можем применить правило. Итак, первое, что нужно изменить модель данных:
CREATE TABLE cPurchase(
purchaseid NUMBER(10) PRIMARY KEY );
CREATE TABLE cPurchaseItem(
purchaseid NUMBER(10),
itemid NUMBER(10),
purchaseqty NUMBER(10)
);
ALTER TABLE CPURCHASEITEM
ADD CONSTRAINT CPURCHASEITEM_PK PRIMARY KEY(PURCHASEID,ITEMID);
ALTER TABLE CPURCHASEITEM
ADD CONSTRAINT CPURCHASEITEM_FK1 FOREIGN KEY(PURCHASEID )REFERENCES CPURCHASE;
ALTER TABLE CPURCHASE
ADD CONSTRAINT CPURCHASE_FK2 FOREIGN KEY(ITEMID )REFERENCES CTITEM(ITEMID );
Теперь у нас есть структура детализации заголовка, которая присваивает несколько предметов одной покупке, что означает, что мы можем попытаться применить правило.
Следующая проблема заключается в том, что supplierid
не является атрибутом cpurchaseitem
. Невозможно создать проверочное ограничение для таблицы или столбца, который выполняет запрос к другой таблице. Что вам нужно, так это SQL-утверждение, которое представляет собой условную конструкцию, которая позволит нам определять такие правила. Увы, Oracle (и любая другая СУБД) в настоящее время поддерживает утверждения.
Так что у нас остается три варианта:
- Перейти к процедуре и написать API транзакции, который обеспечивает соблюдение этого правила.
- Денормализовать
cpurchaeitem
, чтобы включить supplierid
, затем создать уникальное ограничение для (purchaseid, supplierid)
. Вам нужно будет заполнить supplierid
всякий раз, когда вы заполняете cpurchaseitem
.
- Написать триггер после оператора:
(Предупреждение: это закодированный wildstyle и может содержать ошибки и / или ошибки компиляции.)
create or replace trigger cpurchaseitem_trg
after insert or update on cpurchaseitem
declare
rec_count number;
begin
select count(*)
into rec_count
from cpurchaseitem pi
join citem I on pi.itemid = i.itemid
group by pi.purchaseid, i.supplierid having count(*) > 1;
if rec_count > 0 then
raise_application_error(-20000
, 'more than one item for a supplier!');
end if;
end;
Честно говоря, ни одно из этих решений не является особенно привлекательным. API - это твердое решение, но открытое для обхода. Триггер будет страдать от проблем с масштабированием, так как количество покупок со временем увеличивается (хотя это можно уменьшить, написав вместо этого составной триггер, оставленный в качестве упражнения для читателя). Денормализация - это самое безопасное (и, вероятно, наиболее эффективное) решение, даже если оно не моделирует лучшую практику.