xom`s pinion - Tom Hnatovsky Blog

PostgreSQL, funkce round() a 666

17. května 2006 Web design 2 komentáře

PostgreSQL To mě zase jednou sloník PgSQL pěkně převezl.. Program automatizace přiřazování plateb k fakturám pracoval bezproblémově, než došlo na fakturu s částkou 666 bez DPH, což je s 19% daní 792,54. Funkce vyvolaná triggerem na insert platbu nespárovala.. Kde je chyba?

Ceny s DPH zaokrouhluji klasickým způsobem, tedy na celá čísla k nejbližší hodnotě (72,5 je 73 apod.) a právě při zaokrouhlení došlo k problému.

round(SUM(price*(tax+100)/100)) INTO not_paid
První ukázka

Jakou myslíte, že bude mít proměnná not_paid hodnotu, pokud je price=666 a tax=19? 792, ale proč? Pokud rozšíříme zaokrouhlení na dvě desetinná místa, tak dostaneme odpovědi 792,00, takže musí docházet k zaokrouhlení již někde dříve.. Myslel jsem, že při tom dělení stem, ale nakonec se ukázalo, že při tom násobení (samozřejmě, že ne, že to zapříčiňuje dělení), proto stačí doplnit definici numeric(v,s), přičemž v definuje počet číslic v čísle a s přesnost desetinných míst. Doplněno: Nebo, jak psal Andrew v komentářích, nepřetypovávat a jedno z čísel, např. 100 napsat desetinně a výsledek bude s desetinnou čárkou.

round(SUM(price*(tax+100)::numeric(10,2)/100)) INTO not_paid
Druhá ukázka

Pro mé účely jsem použil výše zmíněnou syntaxi a vše již běhá v pořádku a aspoň vím, že si někdy dokáže Postgre leccos domyslet a převést, bohužel v tomto případě na integer..

Aktualizováno:
Ani si nikdo nevšiml, že nadpis článku proklamoval funkci rand, přitom byl text o round, adresu spotu nechávám starou, je již indexována a nahodilí hledači by se nemuseli k tématu dostat.. ;o)

Vaše komentáře

GRavatar

Andrew 18.5.06 09:29

Předně si dovolím upozornit, že v násobení zcela jistě chyba není. Jsouce oba činitelé celočíselní, bude i výsledek celočíselný. IMHO předvídatelně ;) Co mě překvapuje víc, je, že PgSQL u dělení automaticky nezmění typ výsledku. Třeba v opovrhovaném MySQL to funguje podle očekávání.

Nahodil jsem PgSQL (8.1) a jal se bádat. A zjistil jsem, že aby to fungovalo, jak má, je potřeba, aby alespoň jedno číslo ve vzorci bylo desetinné. Tzn. řešení je např toto: round(SUM(price*(tax+100.0)/100)) INTO not_paid, což je IMHO mnohem přehlednější a elegantnější než to přetypování.

GRavatar

xom 18.5.06 09:36

|1| - reakce na Andrew: Nojo, opět mé unáhlené konce.. Prvně jsem zkoušel přetypovat celý vzorec, ale to nepomáhalo, tudíž jsem to prdnul na první pozici, kde to bylo žádoucí.. ;o)

Verze s desetinnou stovkou vypadá dobře a hlavně nemůže dojít k přetečení čísla definovaného přetypováním, díky za analýzu.. Akorát nechápu, proč PgSQL natvrdo přetypovává na Integer, když na každou jinou prkotinu v chybných datových typech upozorňuje a kód neprovede..

Přidejte i vy svůj komentář

Pole komentáře: zmenšit / zvětšit

Jméno:

Email: (Gravatar ID)

Web:

Barva trávy (otázka proti robotům):

© 2004-2012 Tom Hnatovsky - Všechna práva vyhrazena