29.07.10

И опять округления в mssql


Очередной раз разбираясь с проблемами ошибок округлений и точности данных типа numeric в mssql пришлось перелопатить BOL. Собственно задачка была достаточно простая - надо было разделить два числа типа numeric(16,7). Все бы хорошо, но... пример:

declare @n1 numeric(16,7), @n2 numeric(16,7)
select @n1=1, @n2=1

select @n1*@n2 as mul, @n2/@n1 as div

                                       
mul                                     div
--------------------------------------- ---------------------------------------
1.00000000000000                        1.0000000000000000000000

Количество нолей для mul – 14, для div - 22
А почему для DIV-а появляется столько нулей? Правила умножения и деления для numeric(p,s) в MSSQL:
Operation
Result precision
Result scale *
e1 * e2
p1 + p2 + 1
s1 + s2
e1 / e2
p1 - s1 + s2 + max(6, s1 + p2 + 1)
max(6, s1 + p2 + 1)

Т.е. для данного примера получается следующее:
Операция
Точность
Масштаб
Итоговый тип
e1 * e2
16 + 16 + 1 = 33
7 + 7 = 14
Numeric(33,14)
e1 / e2
16 – 7 + 7 + max(6, 7 + 16 + 1) = 40
max(6, 16 + 7 + 1) = 24
Numeric(40, 24)

Все было бы хорошо, но согласно правил мы должны получить 24 нуля в дробной части при делении, а мы получили только 22.
Тут не стоит забывать о том, что в MSSQL максимальная точность для numeric – 38 разрядов. Из-за этого в результате деления двух numeric-ов будут потеряны 2 разряда.  Согласно BOL для сохранения точности целой части результата будет сокращена дробная часть итогового числа, т.е. в результате получим тип  numeric(38,22).

Поосторожней надо быть с такой разрядностью :)

16.07.10

А вы используете recompile?


А кто-нибудь пользуется для параметризированных запросов хинтом recompile? Хинт заставляет оптимизатор запросов вычислить и упростить условие where, поставляя значения параметров, актуальные на момент выполнения  запроса как константы.

Вот как отличаются планы выполнения одного и того же запроса с хинтом и без него:

      declare @date datetime = '20100710'

      declare @df datetime, @dt datetime
      select @df = @date, @dt = DATEADD(day, 1, @date)

      -- запрос без recompile
      select filID, lagerID, kolvo, date, operationID, parentid
      from movement..fact_movement with (nolock)
      where
            operationID in (3,23)
            and date>=@df and date<@dt

      -- запрос с recompile
      select filID, lagerID, kolvo, date, operationID, parentid
      from movement..fact_movement with (nolock)
      where
            operationID in (3,23)
            and date>=@df and date<@dt
      option (recompile)

первый запрос (идет скан кластерного индекса):

 















а вот что происходит при добавлении option (recompile):