Domanda

Ho un prodotto che costa 4 € e ho bisogno di dividere questo denaro per 3 dipartimenti. Sulla seconda colonna, ho bisogno di ottenere il numero di righe per questo prodotto e dividere per il numero di dipartimenti.

La mia domanda:

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

Ma quando ho sommare i valori, ottengo 3,999999. Naturalmente con centinaia di righe ottengo grandi differenze ... C'è qualche possibilità di definire 2 numeri decimali e rotondo ultimo valore? (I miei risultati sarebbero 1.33 1.33 1.34) Insomma, un modo per regolare l'ultima riga?

È stato utile?

Soluzione

Con sei decimali di precisione, si avrebbe bisogno di circa 5.000 transazioni a notare una differenza di un centesimo, se si arrotonda il numero finale a due decimali. Aumentando il numero di decimali da un livello accettabile eliminerebbe gran parte dei problemi, vale a dire utilizzando 9 decimali si avrebbe bisogno di circa 5.000.000 di transazioni a notare una differenza di un centesimo.

Altri suggerimenti

Al fine di gestire questa situazione, per ogni riga si dovrebbe effettuare le seguenti operazioni:

  • Eseguire la divisione
  • Il giro del risultato per il numero appropriato di centesimi
  • Somma la differenza tra l'importo arrotondate e il risultato dell'operazione di divisione
  • Quando la somma delle differenze supera la cifra decimale più basso (in questo caso, 0,01), aggiungere tale importo per i risultati della successiva operazione di divisione (dopo l'arrotondamento).

Questa distribuirà quantità frazionarie in modo uniforme le righe. Purtroppo, non esiste un modo semplice per farlo in SQL con le query semplici; probabilmente è meglio eseguire questo codice procedurale.

Per quanto riguarda quanto sia importante, quando si tratta di applicazioni e le istituzioni finanziarie, cose come questa sono molto importanti, anche se è solo per un centesimo, e anche se può accadere solo ogni tot numero di record; in genere, gli utenti vogliono vedere i valori legare al centesimo (o qualunque sia la vostra unità di valuta è) esattamente.

La cosa più importante, non si vuole per consentire un exploit come "Superman III" o "Office Space" a verificarsi.

Forse si può fare una fila indietro che sarà totale - somma (A, B, C). Ma dipende da cosa si vuole fare, se avete bisogno di valore esatto, è possibile mantenere le frazioni, il resto, troncare e non si preoccupano della perdita virtuale

Inoltre può essere fatto semplicemente aggiungendo la differenza arrotondamento di un valore particolare al numero successivo di essere arrotondate (prima dell'arrotondamento). In questo modo la pila rimane sempre la stessa dimensione.

Ecco un'implementazione TSQL (Microsoft SQL Server) dell'algoritmo fornito da 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

;

Output:

entrare descrizione dell'immagine qui

È possibile collegare qualsiasi numero che si desidera in alto. Non sono sicuro se v'è una dimostrazione matematica per questo algoritmo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top