Zapisywanie ceny/kwot w MySQL

MySQL - logo

Dzisiejszy wpis jest tym z rodzaju błahych, ale nie do końca. Często zapominamy, że przeniesienie słowa z życia codziennego (w tym wypadku: float) do świata maszyn nie zawsze jest właściwe.

Często w projektach, w których uczestniczę projektowanie bazy danych oddaje się w ręce niedoświadczonych osób. Bo dlaczego nie może jej zaprojektować osoba, która będzie tworzyła logikę w aplikacji do niej?

Zaprojektować owszem – może, ale często osoba odpowiedzialna za część programistyczną aplikacji ma nikłe pojęcie o projektowaniu bazy, a szczególnie dostępnych typach, procedurach itp.

INT(10) != VARCHAR(10)

Szczególnie studenci i osoby z firm, gdzie wyznaje się zasadę „człowiek-orkiestra” popełniają ten błąd. Korzystając z typu VARCHAR i podając jego wielkość (10) sądzą, że tak samo można ograniczyć wielkość (długość) pól liczbowych, np. INT.

Tym bardziej powielają ten błąd, gdy silnik bazodanowy podczas tworzenia tabeli nie zwraca błędu. Zapis INT(10) jest poprawny, ale nie powoduje on, że ograniczymy INT do 10 miejsc (pomijam fakt, że niektórzy sądzą, że podając INT(18) przeskoczą definicję długości samego INT-a). Z założenia zapis INT(X) powinien iść w parze z opcją ZEROFILL. Konstrukcja:

To spowoduje, że gdy dodamy kod o wartości 1, to wynik zapytania zwróci nam 00001 (czyli cyfrę 1 uzupełnioną do 5 miejsc zerami – ZEROFILL). I tylko dlatego istnieje zapis INT(X), a nie z powodu ograniczenia długości pola danego typu numerycznego. Od tego mamy masę aliasów (jak SMALLINT czy TINYINT).

FLOAT czy DECIMAL/INT?

Jedną sprawę mamy wyjaśnioną. Teraz poważniejsza sprawa, która wpływa na wyniki zapytań bazy danych. Raz na jakiś czas zdarza mi się zetknąć z kodem, gdzie kwoty z faktur, rachunków są umieszczane w polach typu FLOAT. Wydaje mi się, że autorów takich konstrukcji popycha posługiwanie się tłumaczeniem FLOAT na język polski jako liczba zmiennoprzecinkowa. Człowiek widzi hasło „liczba … przecinkowa” i sądzi, że służy do zapisu ogólnie pojętych cen. Dlatego lepiej odwołać się do angielskich definicji, które moim zdaniem są jaśniejsze: float jest to liczba aproksymowana, czyli przybliżona. A najlepiej posłużyć się przykładem. Utwórzmy tabelę:

Następnie umieśćmy w niej dane z wartościami po przecinku, np. kwotę „29.99„. Cała operacja się powiedzie, a zapytanie SELECT zwróci nam odpowiednie wyniki. Oto nam chodziło. Na pewno?

W przypadku, gdy będziemy chcieli uzyskać faktury, które mają pozycje z ceną „29.99” lub są większe równe to nie zobaczymy żadnych wyników. Dlaczego? Ponieważ jest to liczba zmiennoprzecinkowa, a prościej nieprecyzyjna. Nasze „29.99” wygląda tak naprawdę tak:

Dlatego jeśli chcecie umieścić cenę, kwotę w bazie danych skorzystajcie z typu DECIMAL(9,2) albo INT. Tak INT, ale wtedy w bazie zapiszcie kwotę w groszach, tworząc odpowiednie settery/gettery np. w modelu, gdzie posługujemy się odpowiednią kwotą.

Jak wspomniałem na początku – wiem, że to oklepany temat, ale wykładowcy często zapominają, aby dobitnie podkreślić tą ważną różnicę. Nie wspomnę o uczeniu studentów, żeby zawsze normalizowali bazę danych… Nie pokładajcie całej nadziei w studiach, tylko uczcie się, zdobywajcie wiedzę praktyczną na własną rękę.