Splitting an Amount Across Rows Without Losing a Cent

Individually rounded shares can fail to add back to their source amount. Splitting an amount therefore needs a reconciliation rule as well as a division. Integer cents and ranked remainders make that rule explicit.

Three bowls with equal heaps of toffees, one last toffee resting in an open palm above them

Splitting an Amount With Independent Rounding

Dividing one hundred dollars into three equal decimal shares gives a repeating fraction. Rounding each share to two decimals gives thirty-three dollars and thirty-three cents. Three such shares total ninety-nine dollars and ninety-nine cents. That is arithmetic, not a measured database performance result.

I check the sum of allocated shares before accepting any allocation query. Every row can look reasonable while the complete allocation loses a cent. The missing cent does not become less important because its row is small. Financial reconciliation needs a precise equality check against the original amount.

DECLARE @Amount decimal(19,2)=100.00;
SELECT ROUND(@Amount/3,2) AS RoundedShare,
       ROUND(@Amount/3,2)*3 AS RoundedTotal,
       @Amount-ROUND(@Amount/3,2)*3 AS LeftoverAmount;
GO

Use decimal for the monetary input. Float represents approximate binary values and is a poor basis for exact cent reconciliation. The output scale and rounding policy also belong in the business contract. Two decimal places are appropriate for this example's currency, not every currency or unit.

Define Recipients and Weights

Weights determine each recipient's proportion. They do not need to total one hundred. Equal weights produce equal ideal shares; unequal weights produce proportional ideal shares. Reject negative weights under this example's policy and require a positive total weight.

CREATE TABLE #AllocationWeights
(RecipientID int PRIMARY KEY,Weight bigint NOT NULL CHECK(Weight>=0));
INSERT #AllocationWeights VALUES(1,1),(2,1),(3,1);
DECLARE @Amount decimal(19,2)=100.00;
SELECT RecipientID,Weight,
 ROUND(@Amount*Weight/NULLIF(SUM(Weight) OVER(),0),2) AS RoundedWeightedShare
FROM #AllocationWeights ORDER BY RecipientID;
GO

That query demonstrates weighted rounding, but the independently rounded outputs still need reconciliation. Depending on the weights, rounding can leave a shortfall or an excess. Do not assume the correction is always one extra cent for the first row.

RecipientID supplies a stable tie-breaker in the example. The business can choose another priority, such as a documented sequence. Keep it unique and stable across reruns. An allocation should not change simply because SQL Server returned tied rows in another physical order.

Splitting an Amount Into Whole Cents First

For a nonnegative amount already expressed to cents, convert the total to integer cents. Compute each ideal weighted numerator, its whole-cent quotient, and its remainder. Give every recipient the quotient first. The unassigned cents then equal total cents minus the sum of those base allocations.

The sample uses bigint arithmetic with small bounded inputs. In a general procedure, validate that total cents times any weight and the sum of weights fit their numeric types. Exact integer division is useful only when arithmetic does not overflow. Reject oversized inputs or implement a reviewed wider numeric approach.

DECLARE @Amount decimal(19,2)=100.00;
DECLARE @TotalCents bigint=CONVERT(bigint,@Amount*100),@TotalWeight bigint;
SELECT @TotalWeight=SUM(Weight) FROM #AllocationWeights;
IF @Amount<0 OR @TotalWeight IS NULL OR @TotalWeight<=0
 THROW 50000,'A nonnegative amount and positive total weight are required.',1;
WITH Base AS
(
 SELECT RecipientID,Weight,
        (@TotalCents*Weight)/@TotalWeight AS BaseCents,
        (@TotalCents*Weight)%@TotalWeight AS RemainderNumerator
 FROM #AllocationWeights
), Ranked AS
(
 SELECT *,ROW_NUMBER() OVER
 (ORDER BY RemainderNumerator DESC,RecipientID) AS RemainderRank,
 @TotalCents-SUM(BaseCents) OVER() AS LeftoverCents
 FROM Base
)
SELECT RecipientID,Weight,BaseCents,RemainderNumerator,
 BaseCents+CASE WHEN RemainderRank<=LeftoverCents THEN 1 ELSE 0 END
   AS AllocatedCents
INTO #AllocatedAmounts FROM Ranked;
SELECT RecipientID,
 CONVERT(decimal(19,2),AllocatedCents/100.0) AS AllocatedAmount
FROM #AllocatedAmounts ORDER BY RecipientID;
SELECT SUM(AllocatedCents) AS ReconciledCents,@TotalCents AS OriginalCents
FROM #AllocatedAmounts;
From total cents to a reconciled split: a diagram about the splitting an amount

Rank the Largest Fractional Claims

All recipients share the same denominator, total weight. Ranking their integer remainder numerators therefore ranks the fractional claims exactly. No approximate comparison is needed. Recipients with larger unfinished fractions receive the remaining cents first.

The whole-cent base is rounded downward rather than independently rounded to nearest. That guarantees the remaining amount is nonnegative. For nonnegative weights, the remaining cent count is strictly less than the number of recipients with positive weights. Each selected recipient receives at most one additional cent.

A zero-weight recipient has zero base and zero remainder. It should receive no share under this policy. Verify that case directly alongside the equal-weight demonstration. The deterministic key resolves equal remainders among eligible recipients without changing the total.

Document Who Receives the Extra Cent

Largest remainder is a fair-looking mathematical policy, but fairness is a business decision. Equal claims require a tie rule. Always choosing the lowest identifier can consistently favor the same recipient across repeated allocations. A reviewed rotating priority can be appropriate in another system.

I document the rule beside the allocation result. Which recipient gets an equal-remainder cent, and should the choice remain identical on a retry? A payment workflow needs idempotent behavior so rerunning the calculation does not create conflicting amounts.

Do not introduce random ordering merely to appear neutral. It makes reconciliation and retries harder to explain. If rotation is required, store the assigned priority with the allocation request. The next calculation can then reproduce the same decision from the same approved inputs.

Decide the Policy for Refunds and Precision

The sample rejects negative total amounts. A refund allocation can instead allocate the absolute amount and reapply a sign under an explicit policy. Test negative rounding and tie behavior rather than extending the positive algorithm casually. Extreme bigint values also need separate overflow handling.

If the source amount has more than two decimal places, decide when it becomes a cent total. Rounding the source first and rounding each share are different operations. The allocated output must reconcile to the approved source total, not an undocumented conversion chosen by SQL type precedence.

Weights can also originate as percentages or quantities. Normalize them into the chosen exact representation before allocation. Reject missing or zero total weight clearly. A division protected with NULLIF is not sufficient input validation for a financial workflow because it can silently produce NULL shares.

Validate Splitting an Amount as a Complete Set

Test zero amount, one recipient, equal weights, unequal weights, zero weights, and an amount smaller than the recipient count. Confirm no share is negative under this policy and zero weights remain zero. Assert that integer allocated cents sum exactly to total cents.

Store the original amount, weights, tie policy, and result together when the allocation becomes a business record. That evidence explains every cent later. The reconciliation check should block saving an inconsistent result rather than merely display a warning after payment.

SQL Server 2012 and later support the window ranking used here. The useful improvement is exact set reconciliation with a repeatable rule. The database is distributing money, not voting on where the last cent feels comfortable.

Splitting an amount needs an exact sum check at the final storage boundary. Preserve weights and tie priority when splitting an amount so a retry reproduces every assigned cent.

Related reading on this blog: Banker's Rounding and Datatype Decimal Explained: Datatype Numeric.

Cases to test before saving: a checklist on the splitting an amount

A rounded share is not a reconciled allocation, it is one piece that still needs an exact total and an approved remainder policy.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.


Discover more from SQL Authority with Pinal Dave

Subscribe to get the latest posts sent to your email.

Mathematical Function, SQL Datatype, SQL Scripts, SQL Server
Previous Post
Plan Properties Window: Where the Useful Numbers Hide
Next Post
Spotting Unusually Small Backups Against Each Database’s Average

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.