Som allerede foreslået, prøv først at få designet rigtigt i forhold til dine krav. Du kan implementere mange begrænsninger blot ved at designe dit databaseskema korrekt.
Hold dig væk fra triggere og PL/SQL så længe som muligt. Det vil tvinge dig til bedre design i sidste ende og vil betale sig.
Før du bruger triggere til forretningslogik, så prøv at bruge visninger til ting, der kan vælges. Det er det, databasen er til.
Når du er "færdig", test for ydeevnen, og hvis den er suboptimal, forbedre dit skema. Hvis intet hjælper, så begynd at bruge triggere til forretningslogik.
Jeg har sammensat en prøve med synspunkter jeg taler om. Jeg håber, det kan få dig i gang.
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;
Nogle af ideerne derinde:
- Priser gemmes i en separat tabel, refererer til produktet og har gyldighed, så produktprisen kan ændre sig over tid. Prisvisningen har
ValidTo
kolonne tilføjet, så det er nemmere at arbejde med - Der er et unikt indeks på priser, så vi ikke kan have 2 priser på det samme produkt på samme tid
- Du kan have mange varer i rækkefølge, så derfor er der
Orders
ogOrderLines
tabeller i 1-til-mange forhold - I
Order_V
det samlede betalte beløb vises (ved hjælp af en underforespørgsel påPayments
). ), og de samlede ordreværdier vises (ved hjælp af en underforespørgsel påOrderLines
ogPrices
, bestillingsdatoen bruges til at få priser fra den korrekte periode)
Baseret på skemaet vil du se, hvilke ting du kan repræsentere, og hvilke du ikke kan. Det er din opgave at få det til at matche dine krav :)
Og nu er jeg kommet til det punkt, du siger, at triggere og procedurer er obligatoriske i dit projekt. Derfor har jeg et forslag:
- Opret en procedure, der giver brugerne mulighed for at oprette en ny pris for et produkt. Det bør absolut kontrollere, at gyldigheden ikke starter i fortiden. Implementer derefter en anden, der giver mulighed for at ændre den gyldige til dato (kan heller ikke slutte i fortiden). Du kan derefter tilbagekalde alle indsættelses-/opdateringsrettigheder på produkttabellen og tvinge brugerne til at bruge dine procedurer, der vil indeholde denne forretningslogik.
- Opret en tabel
PricesLog
og trigger påPrices
der vil indsætte PriceId, old.Price, new.Price, sysdate ogUser
til loggen på eventuelle indstik/opdateringer til pristabellen.