Как я могу преодолеть ошибки округления при делении с T-SQL? - PullRequest
1 голос
/ 26 октября 2009

У меня есть общее значение, которое мне нужно распределить по нескольким строкам в таблице SQL:

DECLARE @total numeric(38,5);
DECLARE @count int;

SET @total=123.10000

SET @count = SELECT COUNT(*) FROM mytable WHERE condition=@val;
-- let's say @count is now 3

UPDATE mytable SET my_part=@total/@count WHERE condition=@val;

--each record now has 41.03333

SELECT SUM(my_part) FROM mytable where condition = @val;

-- the sum is 123.09999, not my original 123.10000

Очевидно, что исходная сумма не делится поровну на 3, поэтому сумма не будет соответствовать исходному значению. И независимо от того, что я использую для масштабирования, будут такие разделы, которые не могут выстраиваться в линию.

Я хотел бы, чтобы одна из строк UPDATEd содержала 41.03334, а две другие - 41.03333. Мне все равно, какие округлить вверх, а какие округлить. Но я забочусь о том, что значения можно пересчитать, чтобы получить исходную сумму. Это возможно? Существуют ли известные алгоритмы для подобных вещей?

Ответы [ 4 ]

3 голосов
/ 26 октября 2009

Положите остаток на секретный счет, который медленно накапливает дробные копейки ... затем подождите несколько лет ...

На самом деле, если у вас SQL Server 2005+, вы можете использовать предложение TOP 1 в ОБНОВЛЕНИИ, чтобы ограничить количество обновляемых строк. Так что может быть:

DECLARE @EPSILON numeric(38,5);
DECLARE @T1 numeric(38,5);
DECLARE @T2 numeric(38,5);
SET @T1 = 1;
SET @T2 = 3;
SET @T1 = @T1/@T2;
SET @T2 = 3 * @T1;
SET @EPSILON = 1 - @T2;


DECLARE @total numeric(38,5);
DECLARE @count int;

DECLARE @REMAINDER numeric(38,5);
DECLARE @PARTIAL numeric(38,5);
DECLARE @RESUM numeric(38,5);
DECLARE @LIMITN Integer;

SET @total=123.10000;

SELECT @count = COUNT(*) FROM mytable WHERE condition=@val;

SET @PARTIAL = @TOTAL / @COUNT;
SET @RESUM = @PARTIAL * @COUNT;
SET @REMAINDER = @TOTAL - @RESUM;
IF @REMAINDER < 0 SET @EPSILON = -@EPSILON;
SET @LIMITN = @REMAINDER / @EPSILON;    

UPDATE mytable SET my_part=@PARTIAL WHERE condition=@val;

UPDATE TOP @LIMITN mytable SET my_part = my_part + @EPSILON WHERE condition=@val;

SELECT SUM(my_part) FROM mytable where condition = @val;
1 голос
/ 26 октября 2009

Вы можете использовать дроби, чтобы избежать проблем с округлением. По крайней мере, умножение и деление нескольких строк будет легко. SUM () будет не так просто, если вам нужно точное значение.

0 голосов
/ 26 октября 2009

попробуйте

DECLARE @total numeric(38,5);
DECLARE @count int;
SET @total=123.10000
SELECT @count = COUNT(*) 
FROM mytable 
WHERE condition=@val;
-- let's say @count is now 3
UPDATE mytable SET 
    my_part = @total/@count
WHERE condition=@val;
Update mytable SET 
    my_part = my_part +  
        @total - (Select Sum(My_Part)
                  From mytable 
                  Where condition=@val)
Where PK = (Select Max(PK) From mytable 
            Where condition=@val)
-- each record except one with highest PK now has 41.03333
-- the one with highest PK has 41.03334 (or whatever)
SELECT SUM(my_part) 
FROM mytable 
where condition = @val;
-- the sum should be the original 123.10000
0 голосов
/ 26 октября 2009

Ниже взломан, но он работает для этой ситуации:

DECLARE @total numeric(38,5);
DECLARE @count int;

declare @mytable table
(
   my_part numeric(38,6)  --note the scale is +1
)

insert into @mytable values (0)
insert into @mytable values (0)
insert into @mytable values (0)

SET @total=123.10000

SELECT @count = COUNT(*) FROM @mytable 
-- let's say @count is now 3

UPDATE @mytable SET my_part=@total/@count;

--each record now has 41.03333

-- note the cast
SELECT cast(SUM(my_part) as numeric(38,5)) FROM @mytable;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...