合計分割値の問題(丸めエラーに対処する)
-
24-10-2019 - |
質問
私は4ユーロの費用がかかる製品があり、このお金を3つの部門に分割する必要があります。 2番目の列では、この製品の行数を取得し、部門数を分割する必要があります。
私の質問:
select
department, totalvalue,
(totalvalue / (select count(*) from departments d2 where d2.department = p.product))
dividedvalue
from products p, departments d
where d.department = p.department
Department Total Value Divided Value
---------- ----------- -------------
A 4 1.3333333
B 4 1.3333333
C 4 1.3333333
しかし、値を合計すると、3,999999を取得します。もちろん、数百行で大きな違いがあります... 2つの10進数とラウンドの最後の値を定義する機会はありますか? (私の結果は1.33 1.33 1.34になります)つまり、最後の行を調整する方法はありますか?
解決
6つの精度の小数がある場合、最終番号を2つの小数に丸めると、1セントの差に気付くために約5,000のトランザクションが必要になります。デシマルの数を許容レベルに増やすと、ほとんどの問題が排除されます。つまり、1セントの違いに気付くために約5,000,000のトランザクションが必要になります。
他のヒント
これを処理するには、各行について次のことを行う必要があります。
- 部門を実行します
- 結果を適切な数セントに丸めます
- 丸い量と部門操作の結果の違いを合計します
- 差の合計が最低小数の場所(この場合は0.01)を超えると、次の分割操作の結果(丸め後)にその量を追加します。
これにより、分数量が行全体に分配されます。残念ながら、SQLで簡単なクエリでこれを行う簡単な方法はありません。おそらく、これを手続きコードで実行する方が良いでしょう。
それがどれほど重要かについては、金融アプリケーションや機関に関しては、このようなことは、たとえそれがペニーだけであっても、たとえそれがすべての数の記録しか起こらないとしても、非常に重要です。通常、ユーザーは、値(または通貨の単位が何であれ)に値が正確に結び付けられているのを見たいと考えています。
最も重要なことは、あなたは次のようなエクスプロイトを許可したくないということです 「スーパーマンIII」 また "オフィススペース" 発生することが。
たぶん、あなたは合計-sum(a、b、c)になるfourt列を作ることができます。しかし、それはあなたがしたいことに依存します、あなたが正確な価値を必要とするならば、あなたは画分を保持することができます、他のもの、切り捨て、仮想損失を気にしないでください
また、特定の値の丸めの違いを次の数値に追加するだけで(丸くする前に)実行することもできます。これにより、パイルは常に同じサイズのままです。
これは、によって提供されたアルゴリズムのTSQL(Microsoft SQL Server)の実装です マーティン:
-- Set parameters.
DECLARE @departments INTEGER = 3;
DECLARE @totalvalue DECIMAL(19, 7) = 4.0;
WITH
CTE1 AS
(
-- Create the data upon which to perform the calculation.
SELECT
1 AS Department
, @totalvalue AS [Total Value]
, CAST(@totalvalue / @departments AS DECIMAL(19, 7)) AS [Divided Value]
, CAST(ROUND(@totalvalue / @departments, 2) AS DECIMAL(19, 7)) AS [Rounded Value]
UNION ALL
SELECT
CTE1.Department + 1
, CTE1.[Total Value]
, CTE1.[Divided Value]
, CTE1.[Rounded Value]
FROM
CTE1
WHERE
Department < @departments
),
CTE2 AS
(
-- Perform the calculation for each row.
SELECT
Department
, [Total Value]
, [Divided Value]
, [Rounded Value]
, CAST([Divided Value] - [Rounded Value] AS DECIMAL(19, 7)) AS [Rounding Difference]
, [Rounded Value] AS [Calculated Value]
FROM
CTE1
WHERE
Department = 1
UNION ALL
SELECT
CTE1.Department
, CTE1.[Total Value]
, CTE1.[Divided Value]
, CTE1.[Rounded Value]
, CAST(CTE1.[Divided Value] + CTE2.[Rounding Difference] - ROUND(CTE1.[Divided Value] + CTE2.[Rounding Difference], 2) AS DECIMAL(19, 7))
, CAST(ROUND(CTE1.[Divided Value] + CTE2.[Rounding Difference], 2) AS DECIMAL(19, 7))
FROM
CTE2
INNER JOIN CTE1
ON CTE1.Department = CTE2.Department + 1
)
-- Display the results with totals.
SELECT
Department
, [Total Value]
, [Divided Value]
, [Rounded Value]
, [Rounding Difference]
, [Calculated Value]
FROM
CTE2
UNION ALL
SELECT
NULL
, NULL
, SUM([Divided Value])
, SUM([Rounded Value])
, NULL
, SUM([Calculated Value])
FROM
CTE2
;
出力:
上部に必要な数字をプラグインできます。このアルゴリズムに数学的な証明があるかどうかはわかりません。