За последние 24 часа нас посетили 22456 программистов и 1059 роботов. Сейчас ищут 677 программистов ...

Работа с деньгами в MySQL + PHP [заготовка FAQ]

Тема в разделе "MySQL", создана пользователем artoodetoo, 21 ноя 2014.

  1. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.072
    Симпатии:
    1.236
    Адрес:
    там-сям
    Это заготовка и она будет редактироваться!

    Замечание про хранение денежных величин и вообще чисел с дробной частью.

    В MySQL есть разные типы для хранения вещественных величин:
    NUMERIC (он же DECIMAL) подходит для хранения точных данных, например рублей с копейками
    FLOAT, DOUBLE, REAL или DOUBLE PRECISION хранят значения приблизительно, это реально число "с плавающей запятой", которая может хранить очень большие значения, но точность вычислений с ними бывает непредсказуемой.

    В PHP нет точного аналога NUMERIC. Есть float и есть расширения для работы с числами неограниченной точности, но пока мы не будем погружаться в такие дебри. В этом посте будем считать, что на стороне PHP любые вещественные значения это float, даже для выражения денег.

    Когда вы получаете данные из базы в PHP, записи приходят как массивы строковых значений.
    Чтобы высчислять что-то вам понадобится явно или неявно преобразовывать эти данные в числа. Лучше явно, чтобы текст программы был самодокументированным и чтобы уменьшить количество нежданчиков.

    Код (PHP):
    1. $row = mysqli_fetch_assoc($result);
    2. $row['amount'] = floatval($row['amount']);
    3. // ...
    4. $sum += $row['amount']; 
    При выводе на страницу таких значений вы можете получить где-то точку, а где-то запятую в качестве разделителя. Это зависит от текущей локали. Вроде не страшно, но только не для запросов к базе!

    Проблема:
    Код (PHP):
    1. <?php
    2.  
    3. $amount = 100.54;
    4.  
    5. header('Content-type: text/plain; charset=utf-8');
    6.  
    7. echo setlocale(LC_ALL, 'russian', 'ru_RU', 'ru_RU.UTF-8', 'Russian_Russia'), "\n";
    8. echo "INSERT INTO test(id,amount) VALUES(1,{$amount})\n";
    9.  
    10. echo setlocale(LC_ALL, 'english', 'en_US', 'en_US.UTF-8', 'English_United States'), "\n";
    11. echo "INSERT INTO test(id,amount) VALUES(1,{$amount})\n";
    12.  
    видите: в "русском" варианте у вас три значения вместо двух.

    А что если окружить $amount кавычками?
    Код (PHP):
    1. echo "INSERT INTO test(id,amount) VALUES(1,'{$amount}')\n";
    Теперь в списке два значения, MySQL не заметит ошибку, просто молча преобразует строку в число и запишет данные без дробной части! Потому что в MySQL разделитель дробной части это точка, а не запятая.

    Решение: при работе с базой, когда сами формируете строку запроса, форматируйте вещественные значения ЯВНО и используйте десятичную точку.
    Например так: number_format($amount, 2, '.', '') или так sprintf('%.2F', $amount)

    Или используйте подготовленные запросы с подстановкой параметров, явно указывая тип параметра "double" и само значение параметра должно быть вещественным, а не строкой, чтобы избежать нежданчиков.