Autor Zpráva
polonium
Profil
Ahojte,

řeším menší dilema. Řekněme, že mám v databázi tabulku v níž jsou uloženy objednávky. Každá objednávka má ve sloupečcích info o způsobu dopravy, jeho ceně a sazbě DPH (postageId, postageTax, postagePrice). Protože je jasné, že časem se může DPH změnit nebo cena dopravy, ale zárovneň potřebuju zachovat její hodnotu v uložené objednávce. Jelikož se cena a DPH mění jen jednou za dlouhou dobu v tabulce se tak objevuje spousta duplicitních záznamů.

Řeším tedy dilema jestli má smysl vytvořit novou tabulku kde by byly duplicity sjednoceny nebo to prostě nechat být. Co myslíte?
Taps
Profil
polonium:
a nebylo by vhodné tuto problematiku vyřešit pomocí triggeru ?
polonium
Profil
Možná úplně nechápu jak
juriad
Profil
Ke každému záznamu, ketrý se může měnit přidej sloupce platnost_od a platnost_do.
Mezi produkty budeš používat vždy aktuálně platný záznam, ve faktuře ten, který byl platný v době jejího vystavení.


Budeš tedy mít tabulky
produkty (id, název, popis, dph_id)
ceny_produktu (produkt_id, cena, platnost_od, platnost_do)
faktury (id, dph_id, doprava_id, cas_vystaveni)
produkty_ve_fakture (faktura_id, produkt_id, pocet)
dph (id, hodnota, platnost_od, platnost_do)
doprava (id, popis, hodnota, poatnost_od, platnost_do)

Výpis všech produktů s cenami:
SELECT p.*, c.cena, c.cena*(1+d.hodnota) FROM produkty p JOIN ceny_produktu c ON p.id = c.produkt_id AND NOW() BETWEEN c.platnost_od AND c.platnost_do
JOIN dph d ON p.dph_id = d.id AND NOW() BETWEEN d.platnost_od AND d.platnost_do
Výpis produktů na faktuře:
SELECT p.*, f.pocet, c.cena, c.cena*(1+d.hodnota) FROM faktury f JOIN produkty_ve_fakture pf ON f.id = pf.faktura_id
JOIN produkty p ON pf.produkt_id = p.id
JOIN ceny_produktu c ON p.id = c.produkt_id AND f.cas_vystaveni BETWEEN c.platnost_od AND c.platnost_do
JOIN dph d ON p.dph_id = d.id AND f.cas_vystaveni BETWEEN d.platnost_od AND d.platnost_do
WHERE f.id = ID_FAKTURY
polonium
Profil
A když je cena_produktu právě platná znamená to, že hodnota platnost_do bude mít hodnotu NULL? BETWEEN si s tím dokáže poradit jako kdyby NULL = NOW()?
juriad
Profil
NULL ti s between podle dokumentace fungovat nebude, ale nic ti nebrání nahradit BETWEEN za porovnání:
NOW() >= platnost_od AND (NOW() < platnost_do OR platnost_do IN NULL)
Druhou možností by bylo nahradit NULL za nějakou konstantu dostatečně daleko v budoucnosti, například 2100-01-01, pak konstrukce s BETWEEN bude fungovat.

Nicméně, aktualizaci ceny bude nejlepší stejně provádět TRIGGEREM, jak navrhuje Taps, píšu od boku:
CREATE TRIGGER nastavPlatnost BEFORE INSERT ON ceny_produktu # bude se provadet automaticky pred kazdym insertem do tabulky ceny_produktu
    FOR EACH ROW
    BEGIN
        # NEW se odkazuje na vkládaný záznam, hodnotu, kterou predáváš v INSERTU
        UPDATE ceny_produktu SET platnost_do = NOW() WHERE produkt_id = NEW.produkt_id AND platnost_do = NULL; # ukonci platnost aktualniho
        SET NEW.platnost_od = NOW(); # nastavi zacatek platnosti vkladaneho
    END;
Potom ti bude stačit jen následující a trigger se postará o ukončení platnosti aktuální a nastavení platnosti nové ceny produktu.
INSERT INTO ceny_produktu (produkt_id, cena) VALUES (123, 456) 
polonium
Profil
Jo, to je dobré řešení :)

Možná se to, ale trochu komplikuje ve chvíli, kdy se rozhodnu změnit cenu produktu v objednávce. V takovém případě už tohle asi fungovat nebude moc elegantně, musel bych vložit do tabulky ceny_produktu dva nové řádky a upravit časy platností na 3 řádcích.

Nebo to udělat elegantněji, že tu úpravu ceny v objednávce bych realizoval formou slevy:
slevy_produktu_v_objednavce (objednavka_id, produkt_id, sleva)
polonium
Profil
juriad:
Tak jsem ten trigger zkoušel a výsledkem byla chyba 1442, tuším, že to souvisí s tím UPDATE :(
juriad
Profil
Aha, jedná se o omezení mysql; databáze sqlite, se kterou jsem pracoval toto omezení neměla. Za zmatení se omlouvám.

V tom případě ti nezbývá než provést úlohu TRIGGERU manuálně v php:
$produkt_id = 123;
$cena = 456;
$now = date("Y-m-d H:i:s");
$end_previous = "UPDATE ceny_produktu SET platnost_do = '$now' WHERE produkt_id = $produkt_id AND platnost_do = NULL"
mysql_query($end_previous);
$add_new = "INSERT INTO ceny_produktu (produkt_id, cena, platnost_od) VALUES ($produkt_id, $cena, '$now')";
mysql_query($add_new);
A pokud možno, proveď oba dotazy v transakci. Jde o to, aby nenastala situace, že se povede první a druhý selže, nebo že nějaký jiný dotaz proběhne mezi těmito dotazy.
polonium
Profil
Ok, děkuju ti :) A ještě drobnost, myslíš, že toto omezení se týká i třeba PostgreSQL?
juriad
Profil
Podle dokumentace to vypadá, že takové omezení tam není:
for example, an INSERT trigger might execute a command that inserts an additional row into the same table

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: