Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8929907
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 15, 20262026-06-15T08:46:20+00:00 2026-06-15T08:46:20+00:00

Came across an interesting little problem at work yesterday. This is a question about

  • 0

Came across an interesting little problem at work yesterday. This is a question about arithmetic as much as SQL. Let’s say you have a bunch of orders, and there is a limit to the volume that the orders can have (all 20 in this case):

if object_id('tempdb..#OMAX') is not null drop table #OMAX
create table #OMAX
    (
    OrderId int primary key,
    MaxVol decimal(15,3)
    )
insert into #OMAX(OrderId, MaxVol) values (1, 20), (2, 20), (3, 20)

And here are your order line items with their current, proposed, volumes:

if object_id('tempdb..#OLI') is not null drop table #OLI
create table #OLI
    (
    OrderId int,
    ProposedVolume decimal(15,3)
    )

insert into #OLI(OrderId, ProposedVolume)
values
    (1, 11.6),
    (1, 5.4),
    (2, 9.744),
    (2, 16.254),
    (2, 9.556),
    (3, 7.1),
    (3, 7.23),
    (3, 7.45)

You also want to round the results to a particular degree of accuracy, let’s say that is 1.0 (whole numbers) for the moment:

declare @nOrderRoundAmt decimal(15,3) = 1.0;

Question: For orders whose current total is greater than the OMAX.MaxVol, can you write a SQL statement that will scale the ProposedVolumes down so that the new total of the order lines is equal to MaxVol? It has to be equal, not less than (reason: the business case here is that order 2 has a total proposed volume of 35.554, but we are saying that the max allowed is 20, so when we reduce the order we need to reduce it to 20, not any less as that would be unreasonable).

Complications: An order can have 1..N line items. Do not consider this an exhaustive set of test data, I suspect there are other tricky cases.

In this case order 1 should be untouched except for rounding, orders 2 and 3 should be reduced and rounded to 20.

Here is my best effort so far:

; with OrderTotals as
    (
    select OrderId, sum(ProposedVolume) as TotalVolume
    from #OLI
    group by OrderId
    )
select
    OLI.*, 
    Ratio.Ratio,
    Scaled.Vol as SVol,
    ScaledAndRounded.Vol as SRVol
from
    #OLI OLI
    join OrderTotals OT on OLI.OrderId = OT.OrderId
    join #OMAX OMAX on OLI.OrderId = OMAX.OrderId
    cross apply
        (
        -- Don't reduce orders that are already below the max.
        select
            case when OMAX.MaxVol / OT.TotalVolume > 1 then 1
            else OMAX.MaxVol / OT.TotalVolume
            end as Ratio
        ) Ratio
    cross apply (select OLI.ProposedVolume * Ratio.Ratio as Vol) Scaled
    -- Rounds to nearest.
    cross apply (select round(Scaled.Vol / @nOrderRoundAmt, 0) * @nOrderRoundAmt as Vol) ScaledAndRounded
    -- Rounds down.
    -- cast(Scaled.Vol / @nOrderRoundAmt as bigint) * @nOrderRoundAmt as ScaledAndRoundedDown,

This demonstrates two problems: order 2 comes out with a total of 19, and order 3 with a total of 21. You can stop order 3 from being more than 20 by always rounding down, but you can then get cases where the order total comes out at 18.

So is it possible in a single statement? My best solution so far is to apply the above logic (using round down) then apply a second step of processing in a cursor to add on differences until we get back to the total of 20.

Can you prove your solution works for all cases?

The following code for generating random orders for testing may be useful:

declare @OrderId int = 0, @NumLineItems int;

while @OrderId < 1000 begin
    set @NumLineItems = cast(rand() * 5 as int) + 1

    insert into #OLI(OrderId, ProposedVolume)
    select top (@NumLineItems) @OrderId, rand(cast(newId() as varbinary)) * 15
    from sys.objects

    set @OrderId = @OrderId + 1
end

SOLUTION

In case anyone is interested in the final solution I made based on Gordon’s answer, here it is. It is a little verbose, returning far more columns than is actually required, but that aids debugging/understanding. Try setting the degree of rounding to 0.1 or 0.01. The solution is vulnerable to division-by-zero errors if any of the line items have a proposed volume of 0, but they are easily filtered out beforehand. It can also generate some line items that are rounded to zero, which need excluding after the fact.

declare @nOrderRoundAmt decimal(15,3) = 0.1;  -- Degree of rounding required.
if object_id('tempdb..#Results') is not null drop table #Results

select
    T.*,
    row_number() over (partition by OrderId order by Remainder desc) as seqnum,
    case
        when NeedsAdjustment = 0 then ProposedVolumeRounded
        else
            (case when row_number() over (partition by OrderId order by Remainder desc) <= LeftOver
            then AppliedVolInt + 1
            else AppliedVolInt
            end)
    end * @nOrderRoundAmt as NewVolume
--into #Results
from
    (
    select
        T.*,
        floor(T.AppliedVol) as AppliedVolInt,
        (T.AppliedVol - 1.000 * floor(T.AppliedVol)) as Remainder,
        T.MaxVol * 1.0 - sum(floor(T.AppliedVol)) over (partition by T.OrderId) as LeftOver
    from
        (
        select
            OLI.OrderId,
            OMAX.MaxVol as OrigMaxVol,
            MaxVol.Vol as MaxVol,
            OLI.ProposedVolume as OrigProposedVolume,
            ProposedVolume.Vol as ProposedVolume,
            ProposedVolumeRounded.Vol as ProposedVolumeRounded,
            sum(ProposedVolume.Vol) over (partition by OLI.OrderId) as SumProposedVolume,
            sum(ProposedVolumeRounded.Vol) over (partition by OLI.OrderId) as SumProposedVolumeRounded, -- Round, THEN sum.
            case
                -- when SumProposedVolumeRounded > MaxVol, i.e. the sum of the rounded line items would be
                -- greater than the order limit, then scale, else take the original.
                when sum(ProposedVolumeRounded.Vol) over (partition by OLI.OrderId) > MaxVol.Vol then 1
                else 0
            end as NeedsAdjustment,
            case
                -- when SumProposedVolumeRounded > MaxVol, i.e. the sum of the rounded line items would be
                -- greater than the order limit, then scale, else take the original.
                when sum(ProposedVolumeRounded.Vol) over (partition by OLI.OrderId) > MaxVol.Vol then MaxVol.Vol * (ProposedVolume.Vol / sum(ProposedVolume.Vol) over (partition by OLI.OrderId))
                else ProposedVolume.Vol
            end as AppliedVol
        from
            ##OLI OLI
            join ##OMax OMAX on OLI.OrderId = OMAX.OrderId
            cross apply (select OLI.ProposedVolume / @nOrderRoundAmt as Vol) ProposedVolume
            cross apply (select OMAX.MaxVol / @nOrderRoundAmt as Vol) MaxVol
            cross apply (select round(ProposedVolume.Vol, 0) as Vol) ProposedVolumeRounded
        ) T
    ) T
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-15T08:46:21+00:00Added an answer on June 15, 2026 at 8:46 am

    This is a partitioning problem, where you are trying to have the results be integers (or equivalently, some fixed multiple of integers). The strategy is to calculate everything as integers, find the remainder, and then apportion the remainder among the items.

    Here is an overview of the calculation:

    1. Calculate the new volume as a floating point number for each entry in the order
    2. Separate the integer portion from the fraction in this volume.
    3. Calculate the max volume minus the sum of the integer proportions. The diffence is the amount you have to make up.
    4. Enumerate the fractions, from the largest to the smallest.
    5. Calculate the final amount as the integer amount plus either 1 or 0. Use 1 when the enumeration is less than or equal to the amount to make up. 0 for the others.

    The following SQL does this:

    select t.*, row_number() over (partition by orderid order by remainder desc) as seqnum,
           (case when row_number() over (partition by orderid order by remainder desc) <= LeftOver
                 then AppliedVolInt + 1
                 else AppliedVolInt
            end) as NewVolume
    from (select t.*, floor(AppliedVol) as AppliedVolInt,
                 (AppliedVol - 1.000*floor(AppliedVol)) as Remainder,
                 maxvol*1.0 - sum(floor(AppliedVol)) over (partition by orderid) as LeftOver
          from (select oli.orderid, oli.ProposedVolume, omax.MaxVol,
                       sum(proposedVolume) over (partition by oli.orderid) as sumProposed,
                       omax.maxvol * (oli.ProposedVolume / sum(proposedVolume) over (partition by oli.orderid)) as AppliedVol
                from #OLI oli join
                     #OMax omax
                     on oli.orderid = omax.orderid
               ) t
         ) t
    

    If you don’t have integers the arithmetic is slightly more complicated (because of the use of the enumeration from (4) to (5). My recommendation is to just multiply all numbers by a constant and turn it into the integer problem or multiply the enumeration in (4) by the factor.

    And, yes, I have tested this on your test data. It not only works logically but in practice.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

While answering this question I came across an interesting difference in the overload resolution
I came across this interesting situation while answering this question . Try this piece
I came across an interesting problem, i have main ViewController let's call him MainVC
So I came across this interesting problem while reviewing code: class Foo: def __init__(self,
I came across an interesting algorithm question in an interview. I gave my answer
I just came across the interesting problem of trying to trim the leading zeroes
I came across this Linq to Sql code in an application I am maintaining:
so i came across this interesting article on WPF exception handling: http://srtsolutions.com/public/item/251263 it works
Whilst debugging my code, I came across this interesting scenario with my switch statement
I came across an interesting problem today whilst implementing a feature into a dynamic

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.