Question

J'ai un produit qui coûte 4 € et je dois diviser cet argent pour 3 départements. Sur la deuxième colonne, j'ai besoin pour obtenir le nombre de lignes pour ce produit et diviser le nombre de départements.

Ma requête:

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

Mais quand je résume les valeurs, je reçois 3,999999. Bien sûr, avec des centaines de lignes que je reçois de grandes différences ... Est-il possible de définir 2 nombres décimaux et dernière valeur ronde? (Mes résultats seraient 1.33 1.33 1.34) Je veux dire, d'une certaine façon de régler la dernière ligne?

Était-ce utile?

La solution

Avec six décimales de précision, vous auriez besoin d'environ 5 000 transactions à remarquer une différence d'un cent, si vous arrondissez le nombre final à deux décimales. L'augmentation du nombre de décimales à un niveau acceptable éliminerait la plupart des problèmes, à savoir l'utilisation 9 décimales dont vous auriez besoin d'environ 5.000.000 transactions pour constater une différence d'un cent.

Autres conseils

Pour gérer cela, pour chaque ligne que vous auriez à faire ce qui suit:

  • Effectuer la division
  • Arrondir le résultat au nombre approprié de cents
  • Somme la différence entre le montant arrondi et le résultat de l'opération de division
  • Lorsque la somme des différences dépasse le plus bas décimale (dans ce cas, 0,01), ajouter ce montant aux résultats de la prochaine opération de division (après arrondi).

distribuera des quantités fractionnaires uniformément sur les lignes. Malheureusement, il n'y a pas moyen facile de le faire dans SQL avec des requêtes simples; il est probablement préférable d'effectuer cette procédure dans le code.

En ce qui concerne l'importance, en ce qui concerne les applications et les institutions financières, les choses de ce genre sont très importantes, même si elle est seulement un sou, et même si elle ne peut se produire chaque X nombre de dossiers; En règle générale, les utilisateurs veulent voir les valeurs cravate au centime (ou quel que soit votre unité monétaire est) exactement.

Plus important encore, vous ne voulez pas permettre un exploit comme "Superman III" ou "office Space" pour se produire.

Peut-être que vous pouvez faire une quatrième ligne qui sera totale - somme (A, B, C). Mais cela dépend de ce que vous voulez faire, si vous avez besoin valeur exacte, vous pouvez garder les fractions, sinon, troncature et ne se soucient pas de la perte virtuelle

peut également se faire en ajoutant simplement la différence d'arrondi d'une valeur particulière au numéro suivant à arrondir (avant arrondi). De cette façon, la pile reste toujours la même taille.

Voici une implémentation TSQL (Microsoft SQL Server) de l'algorithme fourni par Martin :

-- 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

;

Sortie:

Vous pouvez brancher ce que les numéros que vous voulez en haut. Je ne sais pas s'il y a une preuve mathématique de cet algorithme.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top