Come già proposto, cerca prima di ottenere il design giusto rispetto alle tue esigenze. Puoi implementare molti vincoli semplicemente progettando correttamente lo schema del tuo database.
Stai lontano da trigger e PL/SQL il più a lungo possibile. Alla fine ti costringerà a progettare meglio e ti ripagherà.
Prima di utilizzare i trigger per la logica aziendale, provare a utilizzare le viste per le cose che possono essere selezionate. Ecco a cosa serve il database.
Quando hai "finito", verifica le prestazioni e, se non è ottimale, migliora il tuo schema. Se nulla aiuta, inizia a utilizzare i trigger per la logica aziendale.
Ho messo insieme un campione con viste di cui parlo. Spero che possa farti iniziare.
create table Products (
ProdId number generated always as identity primary key
, ProdName varchar2(20) not null
);
create table Stores (
StoreId number generated always as identity primary key
, StoreName varchar2(20) not null
);
create table Customers (
CustomerId number generated always as identity primary key
, CustomerName varchar2(20) not null
);
create table Prices (
PriceId number generated always as identity primary key
, ProdId number not null
, Price number
, ValidFrom date default on null sysdate
, constraint fk_Prices_Product foreign key (ProdId) references Products (ProdId)
);
create unique index uniq_prices_product_price on Prices (ProdId, ValidFrom);
create table Orders (
OrderId number generated always as identity primary key
, CustomerId number not null
, StoreId number not null
, OrderedAt date default on null sysdate
, constraint fk_Orders_Customer foreign key (CustomerId) references Customers (CustomerId)
, constraint fk_Orders_Store foreign key (StoreId) references Stores (StoreId)
);
create table OrderLines (
OrderLineId number generated always as identity primary key
, OrderId number not null
, ProdId number not null
, ProdQuantity number not null
, constraint fk_OrderLines_Order foreign key (OrderId) references Orders (OrderId)
, constraint fk_OrderLines_Prod foreign key (ProdId) references Products (ProdId)
);
create table Payments (
PaymentId number generated always as identity primary key
, OrderId number not null
, PaidAt date default on null sysdate
, PaidAmount number not null
, constraint fk_Payments_Order foreign key (OrderId) references Orders (OrderId)
);
create view Prices_V as
select
p.*
, coalesce(
lead(p.ValidFrom) over (partition by p.ProdId order by p.ValidFrom)
, to_date('9999', 'YYYY')
) ValidTo
from Prices p;
create view Orders_V as
select
o.*
, (
select sum(ol.ProdQuantity * p.Price)
from OrderLines ol
join Prices_V p on (p.ProdId = ol.ProdId and o.OrderedAt between p.ValidFrom and p.ValidTo)
where o.OrderId = ol.OrderId
) Total
, (
select sum(PaidAmount)
from Payments p
where p.OrderId = o.OrderId
) TotalPaid
from Orders o;
insert into Products(ProdName)
select 'Prod A' from dual union all
select 'Prod B' from dual;
insert into Stores(StoreName) values ('Store A');
insert into Customers(CustomerName)
select 'Customer A' from dual union all
select 'Customer B' from dual;
insert into Prices(ProdId, Price, ValidFrom)
select 1, 10, sysdate - 10 from dual union all
select 1, 12, sysdate - 2 from dual union all
select 1, 14, sysdate + 3 from dual union all
select 2, 100, sysdate - 10 from dual union all
select 2, 90, sysdate - 2 from dual union all
select 2, null, sysdate + 5 from dual;
insert into Orders(CustomerId, StoreId, OrderedAt)
select 1 cid, 1 stoid, sysdate - 5 from dual union all
select 2, 1, sysdate - 5 from dual union all
select 2, 1, sysdate - 1 from dual;
insert into OrderLines(OrderId, ProdId, ProdQuantity)
select 1 ordid, 1 prodid, 3 prodquant from dual union all
select 1, 2, 2 from dual union all
select 2, 2, 10 from dual union all
select 3, 2, 10 from dual;
insert into Payments(OrderId, PaidAmount) values (2, 500);
select * from Prices_V order by ProdId, ValidFrom;
select * from OrderLines order by OrderId, ProdId;
select * from Orders_v order by OrderId;
Alcune delle idee in là:
- I prezzi sono memorizzati in una tabella separata, fanno riferimento al prodotto e hanno validità in modo che il prezzo del prodotto possa variare nel tempo. La visualizzazione dei prezzi ha
ValidTo
colonna aggiunta in modo che sia più facile lavorare con - Esiste un indice univoco sui prezzi in modo che non possiamo avere 2 prezzi per lo stesso prodotto contemporaneamente
- Puoi avere molti articoli in ordine, ecco perché ci sono
Orders
eOrderLines
tabelle in relazione 1-a-molti - In
Order_V
viene mostrato il totale pagato (utilizzando una sottoquery suPayments
) e vengono visualizzati i valori totali dell'ordine (utilizzando una sottoquery suOrderLines
ePrices
, la data dell'ordine viene utilizzata per ottenere i prezzi dal periodo corretto)
In base allo schema vedrai quali cose puoi rappresentare e quali no. Il tuo compito è renderlo conforme alle tue esigenze :)
E ora sono arrivato al punto in cui dici che trigger e procedure sono obbligatori nel tuo progetto. Quindi ho una proposta:
- Crea una procedura che consenta agli utenti di creare un nuovo prezzo per un prodotto. Dovrebbe assolutamente verificare che la validità non inizi nel passato. Quindi implementarne un altro che consenta di modificare il valido fino alla data (anche non può terminare nel passato). Puoi quindi revocare qualsiasi privilegio di inserimento/aggiornamento sulla tabella Prodotti e forzare gli utenti a utilizzare le tue procedure che conterranno questa logica aziendale.
- Crea una tabella
PricesLog
e attiva suPrices
che inserirà PriceId, old.Price, new.Price, sysdate eUser
al log su eventuali inserimenti/aggiornamenti alla tabella prezzi.