【问题标题】:Distribute values to several rows in SQL Server将值分布到 SQL Server 中的多行
【发布时间】:2017-05-12 20:35:59
【问题描述】:

我需要有关如何将行值分配给具有相同 ID 的多行的 SQL Server 帮助。为了说明,

  • Id = ProductInventoryCode
  • 数量 = 数量库存

为了分发

Id |   Qty  | TotalNoOfBranchesWithId
---+--------+-------------------------
1  |   40   |     2
2  |   33   |     3
3  |   21   |     2

将接收分配值的表

Id | BranchCode |    Qty | QtyFromForDistributionTable
-------------------------------------------------------
1       101          13            20
1       102           8            20
2       101          10            11
2       102           2            10
2       103           3            12
3       101           1            11
3       102          12            10

每个 id 和分支的分布应尽可能接近。

我得到了类似下面的东西,但有些困惑和迷失了方向。

with rs as 
(
    select 
        r.*, cume.cumequantity, 
        coalesce(s.shipped, 0) AS shipped
    from 
        tmpForDistribution r
    cross apply
        (SELECT SUM([QuantityInStock]) AS cumequantity
         FROM tmpForDistribution r2
         WHERE r2.ProductInventoryCode = r.ProductInventoryCode) cume 
    left join
        (SELECT ProductInventoryCode, COUNT(ProductInventoryCode) AS shipped
         FROM tmpDistributed s      
         GROUP BY s.ProductInventoryCode) s ON r.ProductInventoryCode = s.ProductInventoryCode      
)
select 
    rs.ProductInventoryCode, rs.cumequantity, rs.QuantityInStock,
    ***"how to distribute"***
from rs

我目前使用的是 SQL Server 2008

这是一个示例屏幕输出

上面的结果是 145 个分支,下面我们用来分配 ForDistributionQty 字段,它是 3130,我最终得到一个分数 (DistVal = 21.586),这对于这个问题是不正确的,它应该是一个整数,例如21,但是,如果它只有 21,那么 21 x 145 就是 3045,比 85 个单位要少。

【问题讨论】:

    标签: sql sql-server sql-server-2008 common-table-expression


    【解决方案1】:

    这里我们分配值,然后对数量最多(任意)的记录进行最后的“调整”。但归根结底,数学是有效的,分布的值是平方的。

    注意:不确定为什么在您的示例中 ID 2 没有得到均匀分布

    Declare @Table table (Id int,BranchCode int,Qty int)
    Insert Into @Table values
    (1,       101,          13),
    (1,       102,           8),
    (2,       101,          10),
    (2,       102,           2),
    (2,       103,           3),
    (3,       101,           1),
    (3,       102,          12)
    
    Declare @Dist table (ID int,Qty int)
    Insert Into @Dist values
    (1,40),
    (2,33),
    (3,49)
    
    ;with cte0 as (
            Select A.*
                  ,ToDist  = cast(D.Qty as int)
                  ,DistVal = cast(D.Qty as int)/C.Cnt 
                  ,RN      = Row_Number() over (Partition By A.ID Order By cast(D.Qty as int)/C.Cnt Desc,A.Qty Desc)
             From  @Table A
             Join  (Select ID,Cnt=count(*) from @Table Group By ID) C on A.ID=C.ID
             Join  @Dist D on A.ID=D.ID  )
    , cte1 as (
            Select ID,AdjVal=Sum(DistVal)-max(ToDist) From cte0 Group By ID
    )
    Select A.ID
          ,A.BranchCode
          ,A.Qty
          ,DistVal = DistVal - case when A.RN<=abs(AdjVal) then 1*sign(AdjVal) else 0 end
     From cte0 A
     Join cte1 B on (A.ID=B.Id)
     Order By 1,2
    

    返回

    ID  BranchCode  Qty DistVal
    1   101         13  20
    1   102         8   20
    2   101         10  11
    2   102         2   11
    2   103         3   11
    3   101         1   24
    3   102         12  25
    

    【讨论】:

    • 我以小数结尾,尽管总和等于分配的数量,但我只需要整数,我不需要均匀分布的数量(与我原来的要求相反) , 有什么办法可以控制和保证分配的数量在去掉小数的同时相等。
    • @paulpolo 您的数量是 int 字段还是其他字段? If else DistVal = cast(D.Qty as int)/C.Cnt
    • 缺少很多数量,因为四舍五入的值被截断,例如。带小数的数量是 21.56,做 DistVal = cast(D.Qty as INT)/C.Cnt 只变成 21,因此当你将它乘以分配的总数时,会有很多缺失。
    • @paulpolo 显然我在您的要求中遗漏了一些东西。也许一些更能代表您的实际数据的样本数据会有所帮助。提供的方法将分配整数而不是分数。
    • 这是一个样本,3130 个用于分发的数量,145 个接收器,因此 3130 / 145 = 21.586,如果我们将其设为 int,则它变为 21,因此我会丢失 .586 * 145,无论如何,我可以直接破解,比如得到一个整数分布的总和,然后得到缺少的内容,然后再次分配缺少的内容?
    【解决方案2】:

    如果您可以容忍十进制值,子查询似乎可以提供更好的查询计划(在 SQL 2014 上测试,使用一些合理的键,这避免了表假脱机和一些额外的索引扫描):

    Declare @Table table (Id int,BranchCode int,Qty int, primary key(id, branchcode))
    Insert Into @Table values
    (1,       101,          13),
    (1,       102,           8),
    (2,       101,          10),
    (2,       102,           2),
    (2,       103,           3),
    (3,       101,           1),
    (3,       102,          12)
    
    Declare @Dist table (ID int primary key,Qty int)
    Insert Into @Dist values
    (1,40),
    (2,33),
    (3,21)
    
    
    SELECT
        t.id
       ,t.BranchCode
       ,t.Qty
       ,(d.Qty / CAST((SELECT COUNT(*) as cnt FROM @table t2 where t.id = t2.id) AS decimal(10,2))) as DistributedQty   
    FROM @Table t
    INNER JOIN @Dist d
    ON d.id = t.Id
    

    输出:

    Id  BranchCode  Qty DistributedQty
    1   101         13  20.00000000000
    1   102         82  20.00000000000
    2   101         10  11.00000000000
    2   102         21  11.00000000000
    2   103         31  11.00000000000
    3   101         11  10.50000000000
    3   102         12  10.50000000000
    

    如果您需要 DistributedQty 成为 int 并保留余数,那么我想不出比 @John Cappelletti 更好的解决方案,注意到不均匀的数量可能不像您那样完全可能希望(例如 32 由三个分配将导致 12/10/10 分配而不是 11/11/10 分配)。

    【讨论】:

    • 就个人而言,我更喜欢你提出的精确度(头韵很有趣)+1
    猜你喜欢
    • 1970-01-01
    • 2014-03-12
    • 2015-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多