Comme déjà proposé, essayez d'abord d'obtenir le bon design par rapport à vos besoins. Vous pouvez implémenter de nombreuses contraintes simplement en concevant correctement votre schéma de base de données.
Éloignez-vous des déclencheurs et du PL/SQL aussi longtemps que possible. Cela vous obligera à mieux concevoir à la fin et sera payant.
Avant d'utiliser des déclencheurs pour la logique métier, essayez d'utiliser des vues pour les éléments pouvant être sélectionnés. C'est à cela que sert la base de données.
Lorsque vous avez "terminé", testez les performances et si elles ne sont pas optimales, améliorez votre schéma. Si rien ne vous aide, commencez à utiliser des déclencheurs pour la logique métier.
J'ai rassemblé un exemple avec les vues dont je parle. J'espère que cela pourra vous aider à démarrer.
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;
Quelques-unes des idées qu'il contient :
- Les prix sont stockés dans un tableau séparé, référencent le produit et sont valides afin que le prix du produit puisse changer au fil du temps. La vue des prix a
ValidTo
colonne ajoutée pour faciliter l'utilisation - Il existe un index unique sur les prix afin que nous ne puissions pas avoir 2 prix pour le même produit en même temps
- Vous pouvez commander plusieurs articles, c'est pourquoi il existe des
Orders
etOrderLines
tables en relation 1-à-plusieurs - Dans
Order_V
le total payé est affiché (en utilisant une sous-requête surPayments
) et les valeurs totales de la commande sont affichées (à l'aide d'une sous-requête surOrderLines
etPrices
, la date de la commande est utilisée pour obtenir les prix de la bonne période)
Sur la base du schéma, vous verrez ce que vous pouvez représenter et ce que vous ne pouvez pas. C'est à vous de le faire correspondre à vos besoins :)
Et maintenant j'en suis venu au point où vous dites que les déclencheurs et les procédures sont obligatoires dans votre projet. J'ai donc une proposition :
- Créez une procédure qui permettra aux utilisateurs de créer un nouveau prix pour un produit. Il faut absolument vérifier que la validité ne commence pas dans le passé. Ensuite, implémentez-en un autre qui permet de modifier la date de validité (ne peut pas non plus se terminer dans le passé). Vous pouvez ensuite révoquer tous les privilèges d'insertion/mise à jour sur la table Produits et forcer les utilisateurs à utiliser vos procédures qui contiendront cette logique métier.
- Créer un tableau
PricesLog
et déclencher surPrices
qui insérera le PriceId, old.Price, new.Price, sysdate etUser
au journal sur toutes les insertions/mises à jour du tableau des prix.