简体   繁体   English

SQL 获取最接近数字的值

[英]SQL Get closest value to a number

I need to find the closet value of each number in column Divide from the column Quantity and put the value found in the Value column for both Quantities.我需要从 Quantity 列中找到 Divide 列中每个数字的最接近的值,并将在这两个 Quantities 的 Value 列中找到的值。

Example: In the column Divide the value of 5166 would be closest to Quantity column value 5000. To keep from using those two values more than once I need to place the value of 5000 in the value column for both numbers, like the example below.示例:在 Divide 列中,5166 的值将最接近 Quantity 列的值 5000。为了避免多次使用这两个值,我需要将 5000 的值放在两个数字的值列中,如下例所示。 Also, is it possible to do this without a loop?另外,是否可以在没有循环的情况下执行此操作?

Quantity    Divide  Rank         Value
15500       5166    5            5000
1250        416     5            0
5000        1666    5            5000
12500       4166    4            0
164250      54750   3            0
5250        1750    3            0
6250        2083    3            0
12250       4083    3            0
1750        583     2            0
17000       5666    2            0
2500        833     2            0
11500       3833    2            0
1250        416     1            0

There are a couple of answers here but they both use ctes/complex subqueries.这里有几个答案,但它们都使用 ctes/complex 子查询。 There is a much simpler/faster way by just doing a couple of self joins and a group-by有一种更简单/更快的方法,只需进行几次自我加入和分组

https://www.db-fiddle.com/f/rM268EYMWuK7yQT3gwSbGE/0 https://www.db-fiddle.com/f/rM268EYMWuK7yQT3gwSbGE/0

select 
      min(min.quantity) as minQuantityOverDivide
    , t1.divide
    , max(max.quantity) as maxQuantityUnderDivide
    , case 
        when 
            (abs(t1.divide - coalesce(min(min.quantity),0)) 
            <
            abs(t1.divide - coalesce(max(max.quantity),0)))
        then max(max.quantity)
        else min(min.quantity) end as cloestQuantity

from t1
left join (select quantity from t1) min on min.quantity >= t1.divide
left join (select quantity from t1) max on max.quantity < t1.divide

group by    
    t1.divide

If I understood the requirements, 5166 is not closest to 5000 - it's closes to 5250 (delta of 166 vs 84 )如果我理解要求, 5166并不最接近5000 - 它接近525016684的增量)

The corresponding query, without loops, shall be (fiddle here: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=be434e67ba73addba119894a98657f17 ).相应的查询,没有循环,应该是(这里的小提琴: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=be434e67ba73addba119894a98657f17 )。

(I added a Value_Rank as it's not sure if you want Rank to be kept or recomputed) (我添加了一个Value_Rank ,因为不确定您是否希望保留或重新计算Rank

select
    Quantity, Divide, Rank, Value,
    dense_rank() over(order by Value) as Value_Rank
from
    (
        select
            Quantity, Divide, Rank,
            --
            case
                when abs(Quantity_let_delta) < abs(Quantity_get_delta) then Divide + Quantity_let_delta
                                                                       else Divide + Quantity_get_delta
            end as Value
        from
        (
            select
                so.Quantity, so.Divide, so.Rank,
                -- There is no LessEqualThan, assume GreaterEqualThan
                max(isnull(so_let.Quantity, so_get.Quantity)) - so.Divide as Quantity_let_delta,
                -- There is no GreaterEqualThan, assume LessEqualThan
                min(isnull(so_get.Quantity, so_let.Quantity)) - so.Divide as Quantity_get_delta
            from
                SO so
                    left outer join SO so_let
                    on so_let.Quantity <= so.Divide
                    --
                    left outer join SO so_get
                    on so_get.Quantity >= so.Divide
            group by so.Quantity, so.Divide, so.Rank
        ) so
    ) result

Or, if by closest you mean the previous closest (fiddle here: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b41fb1a3fc11039c7f82926f8816e270 ).或者,如果最接近你的意思是以前最接近的(这里的小提琴: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b41fb1a3fc11039c7f82926f8816e270 )。

select
    Quantity, Divide, Rank, Value,
    dense_rank() over(order by Value) as Value_Rank
from
    (
        select
            so.Quantity, so.Divide, so.Rank,
            -- There is no LessEqualThan, assume 0
            max(isnull(so_let.Quantity, 0)) as Value
        from
            SO so
                left outer join SO so_let
                on so_let.Quantity <= so.Divide
        group by so.Quantity, so.Divide, so.Rank
    ) result

You don't need a loop, basically you need to find which is lowest difference between the divide and all the quantities (first cte).您不需要循环,基本上您需要找到除数和所有数量(第一个 cte)之间的最小差异。 Then use this distance to find the corresponding record (second cte) and then join with your initial table to get the converted values (final select)然后用这个距离找到对应的记录(第二个cte)然后和你的初始表join得到转换后的值(最终选择)

;with cte as (
select t.Divide, min(abs(t2.Quantity-t.Divide)) as ClosestQuantity
from #t1 as t
    cross apply #t1 as t2
group by t.Divide
)
,cte2 as (
select distinct
    t.Divide, t2.Quantity
from #t1 as t
cross apply #t1 as t2
where abs(t2.Quantity-t.Divide) = (select ClosestQuantity from cte as c where c.Divide = t.Divide)
)
select t.Quantity, cte2.Quantity as Divide, t.Rank, t.Value
from #t1 as t
left outer join cte2 on t.Divide = cte2.Divide

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM