Страница 1 из 1

Большие погрешности...

Добавлено: 20 сен 2007, 12:34
AnryGTR
Здравствуйте уважаемые гуру этого форума!
Проблемка с вычислениями. FireBird 1.5.3. Есть следующие таблицы:

Код: Выделить всё

юзеры     - ID, FIO,...
проценты - id_юзера, процент, дата_начал_действия, дата_конца_действия
заказы    - ID, id_Юзера, оплата, скидка, ID_услуги, дата,...
прайс      - ID_услуги, цена, дата_начал_действия, дата_конца_действия
Так вот мне нужно посчитать скока бабла в заданный период(между d1 и d2) будут иметь юзеры, у которых соответственно имеются проценты в таблице "Проценты", с общих поступлений, не учитывая те заказы которые выполнял юзер под номером 19 и 20, пишу

следующее:

Код: Выделить всё

  select ю.ID,
   min(ю.FIO),
   cast(sum((п.цена - п.цена/100*з.скидка)*з.оплата/100*проц.процент) as numeric(10,2))
   from заказы з 
   left join проценты проц
   on(з.дата >= проц.дата_начала_действия and з.дата <= проц.дата_конца_действия and 
   з.оплата = 1 and проц.процент > 0 and з.id_юзера <> 19 and з.id_юзера <> 20 and 
   з.дата >= d1 and з.дата <= d2)
  left join юзеры ю
  on(проц.id_юзера = ю.id)
  left join прайс п 
  on(з.id_услуги = п.id_услуги and з.дата>=п.дата_начала_действия and 
   з.дата<=п.дата_конца_действия)
  where ю.категория = 1 and ю.id <> 19 and ю.id <> 20 
  group by ю.id
В итоге, если сверить полученные суммы, то они почему-то "немного" неверны, т.е. если в другом отчёте посмотреть общую сумму которую принесли заказы, и отнять от неё общую сумму заказов выполненных юзерами 19 и 20, а затем вычислить соответствующий процент, то полученные цифры почему-то "расходятся" с цифрами из вышеописанного запроса. Процент пока что у всех один и тот же, чтобы легче было проверить. Например, при подсчёте в ручную юзер N имеет сумму 545380,85
а запрос выдаёт мне сумму 545398,35
или например в ручную - 389557,75
а запрос выдаёт - 389570,25...
Типы полей:

Код: Выделить всё

 прайс.цена       - numeric(10,2)
 заказы.скидка    - SMALLINT
 проценты.процент - numeric(2,2)
Вроде бы все формулы правильно написаны, да и текст запроса вроде тож, а почему такие погрешности?
пробовал убрать cast не помогло...

Добавлено: 20 сен 2007, 13:00
Merlin
В приведении типа промежуточного результата каждого элементарного оператора в формуле и полследовательнсти выполнения этих элементарных операторов. Правила вспоминать лень, когда-то здесь уже копали.

Добавлено: 20 сен 2007, 13:13
AnryGTR
не ну понимаю погрешности в 1-2 рублика, но не такие же или и такие могут быть из-за приведения типов и т.п.?

Добавлено: 20 сен 2007, 13:29
WildSery
AnryGTR писал(а):не ну понимаю погрешности в 1-2 рублика, но не такие же или и такие могут быть из-за приведения типов и т.п.?
Это как повезёт. Может даже и сойтись, если цены "прилижешь" под проценты свои.
Беда в том, что вычисление в каждой строке с округлением практически никогда (кроме указанного случая) не сойдётся с суммированием, а уже потом округлением.

Правь консерваторию. Храни к примеру сразу вычисленную построчно для суммирования без вычислений.
Это как с НДС - та же байда.

Добавлено: 20 сен 2007, 13:56
Dimitry Sibiryakov
(п.цена - п.цена/100*з.скидка)*з.оплата/100*проц.процент
Надеюсь, ты понимаешь, что тут при делении идет округление? Наставь скобок так, чтобы сначала все умножалось, а только потом делилось.

Добавлено: 20 сен 2007, 13:59
WildSery
Dimitry Sibiryakov писал(а):Наставь скобок так, чтобы сначала все умножалось, а только потом делилось.
Не поможет. Всё равно округление построчное будет.
И с суммой, домноженной на тот же процент, не сойдётся.

Добавлено: 20 сен 2007, 21:45
AnryGTR
2 Dimitry Sibiryakov:
переписал формулу ваще без деления - результат тот же... :?

Странно - заменил as numeric(10,2) на as integer и погрешность стала оч маленькой, НО всё равно есть... как же быть?

2 WildSery:
Правь консерваторию. Храни к примеру сразу вычисленную построчно для суммирования без вычислений.
что-то не понял - как это? можно ли небольшой примерчик? :roll:

Проблема конечно не решена(пока) - рою литературу, но всё равно всем спасибо за советы... 8)

Добавлено: 20 сен 2007, 21:58
kdv
заменил as numeric(10,2) на as integer и погрешность стала оч маленькой
у тебя, часом, не первый диалект?

Добавлено: 20 сен 2007, 22:03
AnryGTR
диалект 3-й...

Добавлено: 20 сен 2007, 22:12
kdv
в любом случае, последовательность вычислений и количество переменных влияют на округление.
Типичный пример про НДС 18% уже привели.
То есть, бывают такие числа, когда будет погрешность если НДС накидывать сверху, или наоборот, НДС вычитать из суммы.
Еще хуже когда вроде бы все с туда-обратно НДС нормально, но при умножении стоимости на N в конечной цифре опять лезут копейки.
Это я про 1С.

Добавлено: 20 сен 2007, 22:21
AnryGTR
значит это безвыходная ситуация и с ней придётся смириться?

Добавлено: 21 сен 2007, 04:18
Slavik
AnryGTR писал(а):значит это безвыходная ситуация и с ней придётся смириться?
Угу, если литература уже вся перерыта, а мозги напрягать лень ;)

Во-первых, тебе уже сказали, что при делении идёт округление, т.е. разделил на 100 -- два последних знака потерял (и так на каждом заказе). Сначала умножай, потом дели:
...*з.скидка/100... ...*проц.процент/100...

Во-вторых, кто тебе мешает сначала находить суммы, а потом вычислять по ним проценты? Правда, при схеме с изменяющимися во времени юзерскими процентами одним запросом в FB15 эта задача не решается (во всяком случае я сходу решения не вижу). Либо х.п., либо FB20.

Добавлено: 21 сен 2007, 07:11
AnryGTR
2 Slavik:
так написал же что формулу переписал вообще без знака деления всё с умножением и только...не помогло! :evil:
А насчёт временного изменения процентов юзеров эт ты прав...тока вот не мог бы ты поподробнее о том как это можно реализовать "одним запросом" в FB2?

Добавлено: 21 сен 2007, 10:11
Slavik
AnryGTR писал(а):так написал же что формулу переписал вообще без знака деления всё с умножением и только...не помогло! :evil:
Вообще? даже чуть-чуть? Странно...
AnryGTR писал(а):...поподробнее о том как это можно реализовать "одним запросом" в FB2?
Долгожданное чудо select from (select...):

Код: Выделить всё

select ю.ID, ю.ФИО, юз.Заработал
from (select Юзер, cast(sum(Сумма*0.01*Процент) as numeric(18,2) as Заработал
      from (select з.Юзер, пц.Процент,
                   cast(sum(п.Цена*(1-0.01*з.Скидка)) as numeric(18,2)) as Сумма
            from Заказы з
                 join Проценты пц on
                   пц.Юзер = з.Юзер and з.Дата between пц.ДатаНачала and пц.ДатаКонца
                 join Прайс п on
                   п.Услуга = з.Услуга and з.Дата between п.ДатаНачала and п.ДатаКонца
                 join Юзеры ю on
                   ю.ID = з.Юзер
            where з.Дата between :d1 and :d2
              and з.Оплачен = 1
              and з.Юзер not in (19, 20)
              and пц.Процент > 0
              and ю.Категория = 1
            group by з.Юзер, пц.Процент
          )
      group by Юзер
     ) юз
     join Юзеры ю on ю.ID = юз.Юзер
  order by ю.ФИО
P.S. Не люблю писать запросы за других - пол часа угробил. Своих головоломок хватает...

Добавлено: 21 сен 2007, 12:05
AnryGTR
2 Slavik:
Спасибо за пример! :wink:
А писать запросы за других не надо, мне хватило бы и этого:
Долгожданное чудо select from (select...)
Всем спасибо за ответы!