Frage

Ich habe ein Produkt, das 4 € kostet, und ich muss dieses Geld für 3 Abteilungen teilen. In der zweiten Spalte muss ich die Anzahl der Zeilen für dieses Produkt abrufen und für die Anzahl der Abteilungen teilen.

Meine Frage:

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

Aber wenn ich die Werte zusammenfasse, bekomme ich 3.999999. Natürlich bekomme ich mit Hunderten von Reihen große Unterschiede ... Gibt es eine Chance, 2 Dezimalzahlen und rundes letzter Wert zu definieren? (Meine Ergebnisse wären 1,33 1,33 1,34) Ich meine, eine Möglichkeit, die letzte Reihe anzupassen?

War es hilfreich?

Lösung

Mit sechs Präzisionsdezimalern benötigen Sie etwa 5.000 Transaktionen, um eine Differenz von einem Cent zu bemerken, wenn Sie die endgültige Zahl auf zwei Dezimalstellen rundeten. Die Erhöhung der Anzahl der Dezimalstellen auf ein akzeptables Niveau würde die meisten Probleme beseitigen, dh mit 9 Dezimalstellen würden Sie etwa 5.000.000 Transaktionen benötigen, um einen Differenz von einem Cent zu bemerken.

Andere Tipps

Um dies zu behandeln, müssten Sie für jede Zeile Folgendes tun:

  • Führen Sie die Division durch
  • Rund um das Ergebnis auf die entsprechende Anzahl von Cent
  • Summe die Differenz zwischen dem abgerundeten Betrag und dem Ergebnis des Abteilungsvorgangs
  • Wenn die Summe der Unterschiede den niedrigsten Dezimalplatz überschreitet (in diesem Fall 0,01), fügen Sie diesen Betrag den Ergebnissen des nächsten Abteilungsvorgangs (nach Runden) hinzu.

Dies verteilt die Bruchmengen gleichmäßig über die Reihen. Leider gibt es in SQL keine einfache Möglichkeit, dies mit einfachen Abfragen zu tun. Es ist wahrscheinlich besser, dies im Verfahrenscode auszuführen.

Wie wichtig es ist, wenn es um finanzielle Anwendungen und Institutionen geht, sind solche Dinge sehr wichtig, auch wenn es nur durch einen Penny und selbst wenn es nur jede x Anzahl von Aufzeichnungen passieren kann. Normalerweise möchten die Benutzer genau sehen, dass die Werte genau an den Penny (oder was auch immer Ihre Währungseinheit) zusammenhängen.

Am wichtigsten ist, dass Sie keinen Exploit -mögen zulassen möchten "Superman III" oder "Büroraum" passieren.

Vielleicht können Sie eine neue Reihe machen, die total sein wird - Summe (a, b, c). Aber es hängt davon ab, was Sie tun möchten, wenn Sie einen genauen Wert benötigen, können Sie Brüche, sonst, abschneiden und sich nicht um den virtuellen Verlust kümmern

Kann auch einfach erfolgen, indem die Rundungsdifferenz eines bestimmten Werts zur abgerundeten Nummer (vor dem Runden) hinzugefügt wird. Auf diese Weise bleibt der Stapel immer die gleiche Größe.

Hier ist eine TSQL -Implementierung des Algorithmus (Microsoft SQL Server) 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

;

Ausgabe:

enter image description here

Sie können alle gewünschten Zahlen oben anschließen. Ich bin mir nicht sicher, ob es einen mathematischen Beweis für diesen Algorithmus gibt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top