forum.bitel.ru
http://forum.bitel.ru/

contract_balance. прогрессирующее расхождение сумм
http://forum.bitel.ru/viewtopic.php?f=22&t=2396
Страница 1 из 1

Автор:  kompot [ 05 июн 2009, 13:42 ]
Заголовок сообщения:  contract_balance. прогрессирующее расхождение сумм

Имеются две таблицы:
contract_balance и её копия недельной давности contract_balance_bckup.

на текущий момент:
Код:
select sum(cb.summa1+cb.summa2-cb.summa3-cb.summa4)
from contract_balance cb
left join contract c on c.id=cb.cid
where c.gr&(1<<0)>0
and cb.mm=04 and cb.yy=2009


даёт:

2300471.22

и

Код:
select sum(cb.summa1+cb.summa2-cb.summa3-cb.summa4)
from contract_balance_bckup cb
left join contract c on c.id=cb.cid
where c.gr&(1<<0)>0
and cb.mm=04 and cb.yy=2009


даёт:

2300371.22

Как видно, за неделю появилась разница в 100 рублей.

Сравниваю кол-во записей в таблицах при (mm=04 and yy=2009) -- одинаковое.

Сравниваю построчно суммы:

Код:
select (cb.summa1+cb.summa2-cb.summa3-cb.summa4) as cb, (cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4) as cbb,
(cb.summa1+cb.summa2-cb.summa3-cb.summa4)-(cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4) as diff
from contract_balance cb
left join contract_balance_bckup cb1 on cb1.cid=cb.cid
left join contract c on c.id=cb.cid
where cb.yy=2009 and cb.mm=04 and cb1.yy=2009 and cb1.mm=04
and c.gr&(1<<0)>0


Результат:

Код:
 cb         cbb        diff   
 ---------  ---------  -------
 0          0          0       
 -1000      -1000      0       
 -2.33      -2.33      -0     
 -295.32    -295.32    -0     
 -1471.81   -1471.81   0       
 34.15      34.15      -0     
 -112.79    -112.79    0       
 320.18     320.18     0     


Дальше интереснее. Если в крайний запрос добавить условие:

Код:
and c.gr&(1<<0)>0
and (cb.summa1+cb.summa2-cb.summa3-cb.summa4)-(cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4)>0.001


и менять число в условии от 0.00001 до 0.1, то кол-во результатов, соотв. этому условию уменьшается от ~300 до 0.


вопрос 1. В связи с каким событием изменилась сумма? напомню, что это происходило не постепенно от 10 рублей до 100, например, а в один день примерно через неделю после сохранения копии таблицы.

вопрос 2. Почему вообще возникает такая ситуация, если столбцы сумм в тблицах имеют тип decimal(10,2)?

(Бухгалтерия не верит в мистику, и потихоньку сооружает для меня эшафот)

Спасибо.

Автор:  skn [ 05 июн 2009, 14:08 ]
Заголовок сообщения: 

попробуйте прочечать базу

такое случается когда повреждены индексы, в запросах в которых участвует индекс выдается одни результаты, а в которых не участвует - другие...

Автор:  kompot [ 05 июн 2009, 14:10 ]
Заголовок сообщения: 

не понял, что надо сделать?

Автор:  kompot [ 05 июн 2009, 14:42 ]
Заголовок сообщения: 

mysqlchek? сделал. ничего не изменилось


И причём здесь индексы, если я построчно сравниваю таблицы?
берем сумму из одной таблицы и сумму из другой таблицы, вычитаем одну из другой и видим, что разница не равна нулю и отличается от нуля на тысячные или десятитысячные доли единицы, в то время как значения сумм должны быть десятичными числами с двумя знаками после запятой.

Автор:  skn [ 05 июн 2009, 18:12 ]
Заголовок сообщения: 

ау вас группа у договора не могла поменять? кол-во записей одинаково?

Автор:  kompot [ 06 июн 2009, 09:53 ]
Заголовок сообщения: 

Кол-во записей одинаково, об этом я писал выше. Проверил первым делом, конечно. Но, я всё-таки ещё раз повторю, расхождение по все таблице - это следствие вот этого:

Код:
select if((cb.summa1+cb.summa2-cb.summa3-cb.summa4)-(cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4)>0.00001,"YES","NO")
from contract_balance cb
left join contract_balance_bckup cb1 on cb1.cid=cb.cid
where cb.yy=2009 and cb.mm=04 and cb1.yy=2009 and cb1.mm=04
and cb1.cid=902


результат: YES

А откуда оно берётся, если в обеих таблицах тип всех слагаемых decimal(10,2) и они соответственно равны?

Автор:  Jimson [ 07 июн 2009, 04:41 ]
Заголовок сообщения: 

извиняюсь за возможную глупость, но кто мешает таки сравнить таблицы целиком за апрель и найти те строки где данные не идентичны ?
если разница в 1-2 копейки на куче договоров, то это одно, а если 100 рублей разницы на одном договоре то как бы...

Автор:  kompot [ 07 июн 2009, 13:28 ]
Заголовок сообщения: 

Jimson писал(а):
извиняюсь за возможную глупость, но кто мешает таки сравнить таблицы целиком за апрель и найти те строки где данные не идентичны ?
если разница в 1-2 копейки на куче договоров, то это одно, а если 100 рублей разницы на одном договоре то как бы...



Вы не внимательно читали мой первый пост.

Автор:  Jimson [ 07 июн 2009, 18:14 ]
Заголовок сообщения: 

читал, я спрашиваю почему нельзя сравнить "select *", чудес то не бывает, ну или сделать select с условиями на неравенство и увидить воочию как у тебя не будут равны два числа decimal(10,2)

Автор:  kompot [ 07 июн 2009, 22:45 ]
Заголовок сообщения: 

1) Третий sql-запрос в первом посте -- построчное сравнение.
2) Последний sql-запрос, который я приводил -- это то, о чём вы говорите для одного конкретного договора (cid указан явно).
Или я что-то не понимаю?

Автор:  Jimson [ 07 июн 2009, 23:06 ]
Заголовок сообщения: 

наверно я чего то не понимаю...
я вижу несхождение по двум агрегирующим выборкам, бог с ними
я вижу выборку где вычисляется разность и результат который дает нули в этой разности
а строки где разность будет не равна нулю есть ?
не будет ли проще сделать выборку с условием на <> что бы увидить сколько будет таких строк и будут ли они вообще

Автор:  kompot [ 08 июн 2009, 00:27 ]
Заголовок сообщения: 

таких строк будет около тысячи. но сравниваемые значения визуально (в формате decimal(10,2)) будут равны, понимаете?
Т.е., например:
select cbb.summa, cb.summa
from cbb, cb
where ...
and cb.summa!=cbb.summa

даст около тысячи вот таких строк:

122.10 122.10

Если попросить показать строки, в которых разница мжду суммами будет больше 1 копейки - таких строк будет 0.
Если попросить показать строки, в которых разница между суммами будет больше 0.001 копейки - то таких строк уже будет пара сотен. Понимаете?

Автор:  Jimson [ 08 июн 2009, 10:18 ]
Заголовок сообщения: 

Понимаю. Виновата mysql. Искать схожие проблемы с decimal через гугл пробовали ?

Автор:  skn [ 08 июн 2009, 12:03 ]
Заголовок сообщения: 

храняться значения в decimal(10,2) но для арифметических операция приводяться к float отсюда и разница

попробуйте исходящий остаток умножить на 100 и отбросить дробную часть а потом уже просумировать

Автор:  Jimson [ 08 июн 2009, 12:24 ]
Заголовок сообщения: 

он то "отбросит", а вы ? :)

Автор:  skn [ 08 июн 2009, 13:45 ]
Заголовок сообщения: 

мы стараемся считать средствами java в частности класс BigDecimal, а не средствами mysql

у вас не схождение в биллинге вылезло или на базе?

Автор:  Jimson [ 08 июн 2009, 15:52 ]
Заголовок сообщения: 

на самом деле достаточно странная проблема, если ошибка только из за округлений float то значит на одном вычислении ошибка может составить лишь копейку, следовательно для разницы в 100 рублей договоров должно быть 10к

а о то что ошибка вылазит в базе это в любом случае не хорошо, отчеты то мы строим по базе, в моем понимании это все же проблема mysql раз она decimal приводит неявно к float на операциях сложения и вычитания

Автор:  Amir [ 08 июн 2009, 17:09 ]
Заголовок сообщения: 

http://dev.mysql.com/doc/refman/5.0/en/ ... float.html ?

Автор:  Jimson [ 08 июн 2009, 19:39 ]
Заголовок сообщения: 

жесть, я вообще не врубаюсь в это:

Код:
 The problem cannot be solved by using ROUND() or similar functions, because the result is still a floating-point number:

mysql> SELECT i, ROUND(SUM(d1), 2) AS a, ROUND(SUM(d2), 2) AS b
    -> FROM t1 GROUP BY i HAVING a <> b;
+------+--------+-------+
| i    | a      | b     |
+------+--------+-------+
|    1 |  21.40 | 21.40 |
|    2 |  76.80 | 76.80 |
|    3 |   7.40 |  7.40 |
|    4 |  15.40 | 15.40 |
|    5 |   7.20 |  7.20 |
|    6 | -51.40 |  0.00 |
+------+--------+-------+


потипу round(x,2) <> round(x,2) и это нормально или как ?

Автор:  skn [ 08 июн 2009, 23:16 ]
Заголовок сообщения: 

насколько я понял результат round все также число в формате float, а то, что видно в распечатке можно считать округленным форматированным выводом

как вариант можете попробовать проапгрейдить БД, или хотя бы для эксперимента

Автор:  kompot [ 09 июн 2009, 14:14 ]
Заголовок сообщения: 

skn писал(а):
храняться значения в decimal(10,2) но для арифметических операция приводяться к float отсюда и разница

попробуйте исходящий остаток умножить на 100 и отбросить дробную часть а потом уже просумировать



Зачем мне это?
(Дальше следует небольшая речь о том, что у каждого из нас своя работа и так далее. По причине очевидности, я её опущу.)

Автор:  skn [ 09 июн 2009, 14:34 ]
Заголовок сообщения: 

kompot писал(а):
Зачем мне это?
(Дальше следует небольшая речь о том, что у каждого из нас своя работа и так далее. По причине очевидности, я её опущу.)


Затем, что если вы используете mysql напрямик для построения отчетов, мы ничем кроме советов помочь не сможем....

Автор:  kompot [ 09 июн 2009, 14:59 ]
Заголовок сообщения: 

Хорошо. А как вы делаете на java?
Вы обращаетесь к базе не с суммимрующим запросом, а построчно вытаскиваете значения из базы и средствами java их суммируете, так?
Т.е. если я буду использовать вместо java какой-нибудь, например, php, то такой способ не должен дать такого эффекта?

Автор:  Amir [ 09 июн 2009, 16:01 ]
Заголовок сообщения: 

При работе с числом с плавающей точкой погрешность будет возникать всегда. Во всех языках идет просто округление, поэтому число выглядит нормально.
Когда же вы пытаетесь сравнить
(cb.summa1+cb.summa2-cb.summa3-cb.summa4)-(cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4)>0.001
то точность результата разности mysql приходится делать также 0.001. А в этом месте уже вылезает погрешность плавающей точки.
Т.е.
(cb.summa1+cb.summa2-cb.summa3-cb.summa4)-(cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4)=0.00
(cb.summa1+cb.summa2-cb.summa3-cb.summa4)-(cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4)=0
будет TRUE
(cb.summa1+cb.summa2-cb.summa3-cb.summa4)-(cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4)>=0.01
false

Разность же ровно в 100 рублей вряд ли погрешность.

В mysql 5.0.7 с полем DECIMAL нормально, а также при использовании FLOAT с округлением (FLOAT(,2)), он автоматически округляет результат суммы.

Если измените конец запроса с
and (cb.summa1+cb.summa2-cb.summa3-cb.summa4)-(cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4)>0.001
на
and (cb.summa1+cb.summa2-cb.summa3-cb.summa4)!=(cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4)
должен быть нормальный результат.

Автор:  kompot [ 09 июн 2009, 16:17 ]
Заголовок сообщения: 

Нет, с этого я и начинал, когда искал разницу.

Смотрите:

Код:
select count(*)
from contract_balance cb
left join contract_balance_bckup cb1 on cb1.cid=cb.cid
left join contract c on c.id=cb.cid
where cb.yy=2009 and cb.mm=04 and cb1.yy=2009 and cb1.mm=04
and c.gr&(1<<0)>0


результат:

Код:
 count(*)   
 -----------
 7834       


Это было кол-во всех строк. Теперь выберем только неравные суммы, как вы посоветоваали:

Код:
select (cb.summa1+cb.summa2-cb.summa3-cb.summa4) as cb, (cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4) as cbb,
(cb.summa1+cb.summa2-cb.summa3-cb.summa4)-(cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4) as diff
from contract_balance cb
left join contract_balance_bckup cb1 on cb1.cid=cb.cid
left join contract c on c.id=cb.cid
where cb.yy=2009 and cb.mm=04 and cb1.yy=2009 and cb1.mm=04
and c.gr&(1<<0)>0
and [b](cb.summa1+cb.summa2-cb.summa3-cb.summa4)!=(cb1.summa1+cb1.summa2-cb1.summa3-cb1.summa4)[/b]


результат:

Код:
 cb         cbb        diff   
 ---------  ---------  -------
 -2.33      -2.33      -0     
 -295.32    -295.32    -0     
 -1471.81   -1471.81   0       
 34.15      34.15      -0     
 -112.79    -112.79    0       
 320.18     320.18     0       
 -1643.82   -1643.82   -0     
 -330.79    -330.79    0       
 -5881.88   -5881.88   -0     
 . . . .

 1964 record(s) selected [Fetch MetaData: 0/ms] [Fetch Data: 68/ms]

 [Executed: 6/9/09 4:06:35 PM YEKST ] [Execution: 301/ms]


Автор:  Amir [ 09 июн 2009, 16:40 ]
Заголовок сообщения: 

А версия mysql какая? Таблицу contract_balance_bckup как создавали? Выполните
SHOW COLUMNS FROM contract_balance
SHOW COLUMNS FROM contract_balance_bckup

Автор:  kompot [ 09 июн 2009, 16:46 ]
Заголовок сообщения: 

Ver 5.0.18


contract_balance:

Код:
 Field     Type                  Null     Key     Default     Extra   
 --------  --------------------  -------  ------  ----------  --------
 yy        smallint(5) unsigned  NO       PRI     0                   
 mm        tinyint(3) unsigned   NO       PRI     0                   
 cid       int(10) unsigned      NO       PRI     0                   
 summa1    decimal(10,2)         NO                                   
 summa2    decimal(10,2)         NO                                   
 summa3    decimal(10,2)         NO                                   
 summa4    decimal(10,2)         NO   


contract_balance_bckup:


Код:
 Field     Type                  Null     Key     Default     Extra   
 --------  --------------------  -------  ------  ----------  --------
 yy        smallint(5) unsigned  NO       PRI     0                   
 mm        tinyint(3) unsigned   NO       PRI     0                   
 cid       int(10) unsigned      NO       PRI     0                   
 summa1    float(10,2)           NO               0.00                 
 summa2    float(10,2)           NO               0.00                 
 summa3    float(10,2)           NO               0.00                 
 summa4    float(10,2)           NO               0.00   

Автор:  Amir [ 09 июн 2009, 17:00 ]
Заголовок сообщения: 

На 5.0.77 при использовании в обоих таблицах decimal (float(,2)) нормально.
Правда тут написано http://dev.mysql.com/doc/refman/5.0/en/ ... float.html что нужно перезагрузить таблицу, не уверен с какой версии у нас таблица contract_balance не передампивалась...

Теоретически такой дамп (из decimal в float) не корректен, т.е. какое-либо значение могло перенестись неверно.
Скажем в 4 байтном float -383130.27 будет -383130.28
Однако в 8 байтный double влезет нормально.

Страница 1 из 1 Часовой пояс: UTC + 5 часов [ Летнее время ]
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/