2009-10-26 43 views
1

我有一個總價值,我需要幾行中分配的SQL表:如何在T-SQL劃分過程中克服舍入錯誤?

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

SET @total=123.10000 

SET @count = SELECT COUNT(*) FROM mytable WHERE [email protected]; 
-- let's say @count is now 3 

UPDATE mytable SET [email protected]/@count WHERE [email protected]; 

--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整除,因此SUM將不匹配原始值。不管我用什麼尺度,都會有可能出現這樣的分歧,無法恢復。

我想要的是UPDATE行之一將包含41.03334,其他兩個將有41.03333。我不在乎哪些是圍繞着哪一個輪迴。但我關心的是,價值可以重新總結,以獲得最初的總和。這可能嗎?有沒有已知的算法來做這種事情?

+0

我能想到的唯一解決方案是不是很優雅 - 喜歡重新選擇的總和,從減原始金額,然後將餘數隨機添加到其中一行。 – 2009-10-26 18:59:52

回答

1

你可以用分數四捨五入迴避的問題。至少幾行的乘法和除法將很容易。如果你需要確切的值,SUM()不會那麼容易。

+0

我假設你正在談論存儲分子和分母。我其實是精確理性的狂熱粉絲! – 2009-10-26 19:28:46

+0

現在,客戶已經決定這些款項不需要匹配,所以沒關係,但這似乎是一種明智的方法,如果我將來需要保留確切的價值。 – Clyde 2009-10-26 19:36:09

+0

除了你將不得不編寫所有你自己的算術函數去做幾乎所有的分子和分母列。 – 2009-11-05 22:40:21

3

把剩餘成慢慢累積分數便士一個祕密帳戶...然後等待幾年...

其實,如果你有SQL服務器2005 +,你可以使用TOP 1子句中的UPDATE限制更新的行。因此,也許:

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 [email protected]; 

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

UPDATE mytable SET [email protected] WHERE [email protected]; 

UPDATE TOP @LIMITN mytable SET my_part = my_part + @EPSILON WHERE [email protected]; 

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

Like in Superman III – 2009-10-26 18:58:06

+0

這僅適用於SQL Server 2005+,因爲那時「TOP ...」似乎被包含在UPDATE語法中。否則,如果您有任何其他唯一的行ID可用,您可以對其進行排序,只更新最小或最大行以重新添加餘數。 – 2009-10-26 19:27:53

+0

我不喜歡這個,因爲如果四捨五入錯誤是.00002而不是0.00001,我寧願看到它分配給兩行,而不是一行上的所有行。這一定很重要,一旦我們分佈在10,000行以上。 – Clyde 2009-10-26 19:38:01

0

下面是一個黑客,但它適用於這種情況:

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 [email protected]/@count; 

--each record now has 41.03333 

-- note the cast 
SELECT cast(SUM(my_part) as numeric(38,5)) FROM @mytable; 
+0

這可能儘可能接近,但請注意,它僅適用於@count小於10的情況。如果count> = 10,則細節表中需要2個額外的比例,而總數則爲2。如果count> = 100,則需要3個額外的刻度,如果在寫入代碼時計數未知,則需要未知的額外刻度。 – Clyde 2009-10-26 19:31:45

+0

我認爲只要@total上的數據類型和結尾處的數據類型相同,就可以將其推送到最大值並仍然可以。 – 2009-10-26 19:35:20

0

試試這個

DECLARE @total numeric(38,5); 
DECLARE @count int; 
SET @total=123.10000 
SELECT @count = COUNT(*) 
FROM mytable 
WHERE [email protected]; 
-- let's say @count is now 3 
UPDATE mytable SET 
    my_part = @total/@count 
WHERE [email protected]; 
Update mytable SET 
    my_part = my_part + 
     @total - (Select Sum(My_Part) 
        From mytable 
        Where [email protected]) 
Where PK = (Select Max(PK) From mytable 
      Where [email protected]) 
-- 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