Som redan föreslagits, försök först att få designen rätt med hänsyn till dina krav. Du kan implementera många begränsningar bara genom att utforma ditt databasschema på rätt sätt.
Håll dig borta från triggers och PL/SQL så länge som möjligt. Det kommer att tvinga dig till bättre design i slutändan och kommer att löna sig.
Innan du använder triggers för affärslogik, försök att använda vyer för saker som kan väljas. Det är vad databasen är till för.
När du är "klar", testa prestanda och om det är suboptimalt, förbättra ditt schema. Om inget hjälper, börja använda triggers för affärslogik.
Jag har satt ihop ett exempel med åsikter jag talar om. Jag hoppas att det kan få dig igång.
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;
Några av idéerna där:
- Priserna lagras i en separat tabell, refererar till produkten och har giltighet så att produktpriset kan ändras över tiden. Prisvyn har
ValidTo
kolumn har lagts till så att det är lättare att arbeta med - Det finns ett unikt index på priser så att vi inte kan ha två priser för samma produkt samtidigt
- Du kan ha många varor i ordning, så det är därför det finns
Orders
ochOrderLines
tabeller i 1-till-många-relation - I
Order_V
det totala betalda antalet visas (med hjälp av en underfråga påPayments
) och de totala ordervärdena visas (med hjälp av en underfråga påOrderLines
ochPrices
, beställningsdatum används för att få priser från rätt period)
Baserat på schemat kommer du att se vilka saker du kan representera och vilka du inte kan. Det är ditt jobb att få det att matcha dina krav :)
Och nu har jag kommit till den punkt du säger att triggers och procedurer är obligatoriska i ditt projekt. Därför har jag ett förslag:
- Skapa en procedur som gör det möjligt för användare att skapa ett nytt pris för en produkt. Det bör definitivt kontrollera att giltigheten inte startar tidigare. Implementera sedan en annan som gör det möjligt att ändra den giltiga till datum (kan inte heller sluta i det förflutna). Du kan sedan återkalla alla insättnings-/uppdateringsprivilegier på produkttabellen och tvinga användare att använda dina procedurer som kommer att innehålla denna affärslogik.
- Skapa en tabell
PricesLog
och trigger påPrices
som kommer att infoga PriceId, old.Price, new.Price, sysdate ochUser
till loggen på eventuella inlägg/uppdateringar till pristabellen.