Poprawnie – typ DECIMAL w bazach danych jest przeznaczony do przechowywania liczb rzeczywistych stałoprzecinkowych, czyli takich, gdzie liczba miejsc po przecinku jest z góry określona i zawsze dokładnie reprezentowana. W definicji kolumny podajemy zwykle dwa parametry, np. DECIMAL(10,2), gdzie 10 to całkowita liczba cyfr, a 2 to liczba cyfr po przecinku. Dzięki temu baza wie, że np. wartość 1234,50 będzie przechowywana bez zaokrągleń binarnych, które występują w typach zmiennoprzecinkowych (FLOAT, DOUBLE). To jest kluczowe w zastosowaniach finansowych: kwoty pieniędzy, stawki VAT, kursy walut, limity kredytowe, rozliczenia magazynowe. W takich miejscach nawet jeden grosz różnicy przy dużej liczbie operacji potrafi narobić bałaganu. Moim zdaniem w projektach produkcyjnych dobra praktyka jest taka, że wszystko, co ma sens biznesowy jako „kwota”, „saldo”, „cena jednostkowa”, „rabat procentowy z dokładnością do dwóch lub czterech miejsc” trzymamy właśnie w DECIMAL/NUMERIC, a nie w FLOAT. Standard SQL definiuje typy DECIMAL i NUMERIC jako typy dokładne (exact numeric), co oznacza, że operacje arytmetyczne na nich są przewidywalne i nie generują dziwnych ogonków typu 1.199999999 zamiast 1.2. W wielu silnikach (MySQL, PostgreSQL, SQL Server) DECIMAL jest implementowany wewnętrznie jako zapis dziesiętny, często podobny do „dużej liczby całkowitej” z wirtualnym przecinkiem w określonym miejscu. Dobrą praktyką jest też dobór precyzji z zapasem, np. DECIMAL(18,4) dla kwot w systemach księgowych, żeby uniknąć przepełnień przy większych sumach. W praktyce webowej, gdy aplikacja PHP czy JavaScript komunikuje się z bazą, to właśnie typ DECIMAL pozwala zachować spójność między tym, co widzi użytkownik na formularzu, a tym, co finalnie zapisuje się w tabeli – bez ukrytych błędów zaokrągleń.
W tym pytaniu łatwo się złapać na skojarzeniach z innymi typami danych, dlatego warto sobie to porządnie poukładać. Typ DECIMAL nie ma nic wspólnego z „liczbami zapisanymi w systemie binarnym” w takim sensie, w jakim często myśli się o typach zmiennoprzecinkowych. Oczywiście fizycznie wszystko i tak jest binarne, ale chodzi o model reprezentacji logicznej. Klasyczne typy zmiennoprzecinkowe (FLOAT, DOUBLE) trzymają wartość w formacie binarnym IEEE 754, co powoduje znane problemy z dokładnością, np. 0.1 + 0.2 ≠ 0.3 idealnie. DECIMAL jest zaprojektowany właśnie po to, żeby unikać tych błędów i zapewnić precyzyjną reprezentację dziesiętną z określoną liczbą miejsc po przecinku. Dlatego utożsamianie DECIMAL z „typem binarnym” albo ogólnie z „liczbami w systemie binarnym” to takie lekkie pomieszanie poziomów abstrakcji. Kolejna pułapka to traktowanie DECIMAL jako typu do przechowywania danych napisowych. Dane tekstowe o określonej długości trzymamy w typach CHAR(n) lub VARCHAR(n). One przechowują ciągi znaków, a nie wartości numeryczne, więc nie wykonamy na nich sensownie operacji arytmetycznych, agregacji typu SUM, AVG itp. Oczywiście czasem ktoś z lenistwa zapisuje np. kwoty jako VARCHAR, ale to jest sprzeczne z dobrymi praktykami projektowania schematu bazy i potem mści się przy raportach i walidacji. Częsty błąd myślowy polega też na wrzuceniu wszystkich „liczb z przecinkiem” do jednego worka i uznaniu, że to zawsze są liczby zmiennoprzecinkowe. W rzeczywistości w SQL rozróżniamy typy przybliżone (FLOAT, DOUBLE, REAL) i typy dokładne (DECIMAL, NUMERIC). Te pierwsze są dobre do obliczeń naukowych, pomiarów, gdzie dopuszczalne jest małe odchylenie, za to są szybsze i mniej pamięciożerne. Te drugie, stałoprzecinkowe, są stosowane tam, gdzie ważna jest bezwzględna poprawność wartości dziesiętnej, np. finanse, księgowość, rozliczenia magazynowe. Traktowanie DECIMAL jako „liczby rzeczywistej zmiennoprzecinkowej” zaciera tę bardzo ważną granicę. Z mojego doświadczenia w projektach komercyjnych wybór między DECIMAL a FLOAT/DOUBLE to jedno z kluczowych miejsc, gdzie początkujący projektanci baz popełniają błędy. Jeśli typ dobierzemy źle, to później pojawiają się trudne do wytłumaczenia różnice groszowe, błędne sumy w raportach, problemy z porównaniami w WHERE. Dlatego warto zapamiętać: DECIMAL to liczby rzeczywiste stałoprzecinkowe, z dokładnie określoną precyzją i skalą, a nie typ tekstowy ani przybliżony typ binarny.