为A * B选择行的所有组合=总<= X

问题描述 投票:-3回答:2

我有一个关于编写查询发现,并插入到表B,从表A,其中的条件是中行的所有组合情况:

一个X B =从ROW1总 ÇX d =从ROW2总...等,其中的计数(总)<= X 项目的“一”价 项目的“b”的量

想法是有像例如所有组合

对于$ 100块钱我能买:

2 tshirt, 1 jacket, 1 pants  

要么

1 tshirt, 2 jacket, 1 pants

...等等

创建光标会帮我跑每一行的查询,但如何在col.quantity在同一时间分割多少?

sql-server tsql
2个回答
1
投票

我会先写我的理解,

  • 我们将有一个项目表,每个项目将有一个价格,
  • 我们的钱数,我们想购买尽可能多的项目
  • 我们想要的物品有作为两个例子所提供的相同的权重“2 T恤,外套1,1条裤子或1周的T恤,夹克2,1个裤”未指定的解决方案有一个项目,但试图利用的所有项目。

因此,如何确定数量的每个项目,以利用大部分我们有这笔钱。

我认为这是可以用不同的方式来描述更加清晰,例如像: - 一个人去的店,想购买每个可用的项目,但如果他有一些余钱,他想了解其他项目,他可以用它购买。如果项目不是很多,钱不是很多,这可能是容易的,但如果项目很多,钱也很多,我可以看到,这可能是一个问题。所以让我们找到一个解决方案。

Declare @Items Table (
    Item varchar(250),Price decimal
)

insert into @Items values 
 ('tshirt',30)
,('jacket',30)
,('pants' ,10)
--,('shoe' ,15)   ---extra items for testing
--,('socks',5)    ---extra items for testing

Declare @total int=100 -- your X
Declare @ItemsCount int
Declare @flag int
Declare @ItemsSum decimal
Declare @AllItmsQty int
select @ItemsCount=count(*),@ItemsSum=sum(price),@flag=POWER(2,count(*)) From @Items

select @AllItmsQty=@total/cast(@ItemsSum as int)

;with Numbers(n) as (
    --generat numbers from 1,2,3,... @flag
    select 1 union all 
    select (n+1) n from Numbers where n<@flag
),ItemsWithQty as (
    select *,Price*n [LineTotal] from @Items,Numbers
),Combination as (
    select items.*,Numbers.n-1 [CombinationId] from @Items items,Numbers
),CombinationWithSeq as (
    select *
        ,ROW_NUMBER() over (Partition by [CombinationId] order by [CombinationId]) [seq] 
        from Combination
),CombinationWithSeqQty as (
    select *,case when (CombinationId & power(2,seq-1))>0 then 1 else 0 end +@AllItmsQty  [qty] 
    from CombinationWithSeq
),CombinationWithSeqQtySubTotal as (
    select *,Price*qty [SubTotal] from CombinationWithSeqQty
)
select 
    --CombinationId,
    sum(subtotal) [Total],
    replace(
        replace(
            STRING_AGG( 
            case when (Qty=0) then 'NA' else (cast(Qty as varchar(5))+' '+Item)
                end
            ,'+')
            ,'+NA','')
            ,'NA+','')  [Items] 
    from CombinationWithSeqQtySubTotal   
    group by CombinationId
    having sum(subtotal)<=@total

其结果将是如下: -

Total   Items
=====   ===========================
100     2 tshirt+1 jacket+1 pants
100     1 tshirt+2 jacket+1 pants
80      1 tshirt+1 jacket+2 pants
70      1 tshirt+1 jacket+1 pants

如果我添加其他两个项目,我们会得到

Total   Items
=====   ===========================
100     1 tshirt+1 jacket+2 pants+1 shoe+1 socks
95      1 tshirt+1 jacket+1 pants+1 shoe+2 socks
90      1 tshirt+1 jacket+1 pants+1 shoe+1 socks

确定,所以该查询是给予最终结果不表B,您描述为具有AXB或项目的价格乘以数量和子总,以及我们可以通过过滤女巫组合很容易地显示一个我们选择,如果我们选择第一个这将是最接近我们可以改变查询的最后一部分,以显示你需要的表B中的量。

),CombinationWithSeqQtySubTotal as (
    select *,Price*qty [SubTotal] from CombinationWithSeqQty
),Results as (
select 
    CombinationId,
    sum(subtotal) [Total],
    replace(
        replace(
            STRING_AGG( 
            case when (Qty=0) then 'NA' else (cast(Qty as varchar(5))+' '+Item)
                end
            ,'+')
            ,'+NA','')
            ,'NA+','')  [Items] 
    from CombinationWithSeqQtySubTotal   
    group by CombinationId
    having sum(subtotal)<=@total
    --order by [Total] desc
)
select item, price, qty, SubTotal from CombinationWithSeqQtySubTotal t where t.CombinationId in
(select top(1) CombinationId from Results order by [Total] desc)

其结果将是如下: -

item    price   qty SubTotal
=====   =====   === =======
tshirt  30      1   30
jacket  30      1   30
pants   10      2   20
shoe    15      1   15
socks   5       1   5

或者,如果我们只与项目运行它,你所提供的结果将是如下: -

item    price   qty SubTotal
======  ===     === =======
tshirt  30      2   60
jacket  30      1   30
pants   10      1   10

如果我们不想使用“STRING_AGG”或者我们没有它,我们就可以通过添加一些CTE的,将做同样的工作,管理其相同的功能,作为“STRING_AGG”只是结果在一个(数量+项目+逗号组合),所以下面的解决方案可以帮助。

Declare @Items Table (Item varchar(250),Price decimal)

insert into @Items values 
 ('tshirt',30)
,('jacket',30)
,('pants' ,10)
--,('shoes' ,15)   ---extra items for testing
--,('socks',5)    ---extra items for testing

Declare @total int=100 -- your X
Declare @ItemsCount int
Declare @flag int
Declare @ItemsSum decimal
Declare @AllItmsQty int
select @ItemsCount=count(*),@ItemsSum=sum(price),@flag=POWER(2,count(*)) From @Items

select @AllItmsQty=@total/cast(@ItemsSum as int)

;with Numbers(n) as (
    --generat numbers from 1,2,3,... @flag
    select 1 union all 
    select (n+1) n from Numbers where n<@flag
),ItemsWithQty as (
    select *,Price*n [LineTotal] from @Items,Numbers
),Combination as (
    select items.*,Numbers.n-1 [CombinationId] from @Items items,Numbers
),CombinationWithSeq as (
    select *,ROW_NUMBER() over (Partition by [CombinationId] order by [CombinationId]) [seq] from Combination
),CombinationWithSeqQty as (
    select *,case when (CombinationId & power(2,seq-1))>0 then 1 else 0 end +@AllItmsQty  [qty] from CombinationWithSeq
),CombinationWithSeqQtySubTotal as (
    select *,Price*qty [SubTotal] from CombinationWithSeqQty
),CombinationWithTotal as (
--to find only the combinations that are less or equal to the Total
    select 
        CombinationId,
        sum(subtotal) [Total]
        from CombinationWithSeqQtySubTotal   
        group by CombinationId
        having sum(subtotal)<=@total
),DetailAnswer as (
    select s.*,t.Total,cast(s.qty as varchar(20))+' ' +s.Item QtyItem from CombinationWithTotal t
    inner join CombinationWithSeqQtySubTotal s on s.CombinationId=t.CombinationId
),DetailAnswerFirst as (
    select *,cast(QtyItem as varchar(max)) ItemList from DetailAnswer t where t.seq=1
    union all
    select t.*,cast((t.QtyItem+'+'+x.ItemList) as varchar(max)) ItemList from DetailAnswer t
        inner join DetailAnswerFirst x on x.CombinationId=t.CombinationId and x.seq+1=t.seq
)
select CombinationId,Total,ItemList from DetailAnswerFirst where seq=@ItemsCount order by Total desc
--select * from DetailAnswer --remark the above line and unremark this one for the details that you want to go in Table B

如果任何的假设是错误的,或者如果你需要一些说明我会很乐意提供帮助。


0
投票

也许,以获得可能的组合最简单的方法是通过自连接,并加入到数字。

如果你想要的3组合,然后用3自连接。 而3加入了一些表或CTE为每个加入“项目”表。

ON标准的使用方式,是最小化所有加入的影响。

您也可以采取从COMBOS CTE的SQL,并用它来第一次将其插入到一个临时表。

例如:

declare @PriceLimit decimal(10,2) = 100;

WITH COMBOS AS
(
    SELECT 
     i1.id as id1, i2.id as id2, i3.id as id3, 
     n1.n as n1, n2.n as n2, n3.n as n3,
     (n1.n + n2.n + n3.n) AS TotalItems,
     (i1.Price * n1.n + i2.Price * n2.n + i3.Price * n3.n) as TotalCost
    FROM Items i1
    JOIN Items i2 ON i2.id > i1.id AND i2.Price < @PriceLimit
    JOIN Items i3 ON i3.id > i2.id AND i3.Price < @PriceLimit
    JOIN Nums n1 
      ON n1.n between 1 and FLOOR(@PriceLimit/i1.Price)
     AND (i1.Price * n1.n) < @PriceLimit
    JOIN Nums n2 
      ON n2.n between 1 and FLOOR(@PriceLimit/i2.Price)
     AND (i1.Price * n1.n + i2.Price * n2.n) < @PriceLimit
    JOIN Nums n3 
      ON n3.n between 1 and FLOOR(@PriceLimit/i3.Price) 
     AND (i1.Price * n1.n + i2.Price * n2.n + i3.Price * n3.n) <= @PriceLimit
     AND (i1.Price * n1.n + i2.Price * n2.n + i3.Price * (n3.n+1)) > @PriceLimit
    WHERE i1.Price < @PriceLimit
)
SELECT 
 c.TotalItems, c.TotalCost,
 CONCAT (c.n1,' ',item1.Name,', ',c.n2,' ',item2.Name,', ',c.n3,' ',item3.Name) AS ItemList
FROM COMBOS c
LEFT JOIN Items item1 ON item1.id = c.id1
LEFT JOIN Items item2 ON item2.id = c.id2
LEFT JOIN Items item3 ON item3.id = c.id3
ORDER BY c.TotalCost desc, c.TotalItems desc, c.id1, c.id2, c.id3;

上分贝<>拨弄here测试

测试结果:

 TotalItems | TotalCost | ItemList                   
 ---------- | --------- | ---------------------------
          7 | 100.00    | 1 pants, 1 tshirt, 5 socks 
          6 | 100.00    | 1 jacket, 1 tshirt, 4 socks
          6 | 100.00    | 1 pants, 2 tshirt, 3 socks 
          5 | 100.00    | 1 jacket, 1 pants, 3 socks 
          5 | 100.00    | 1 jacket, 2 tshirt, 2 socks
          5 | 100.00    | 1 pants, 3 tshirt, 1 socks 
          5 | 100.00    | 2 pants, 1 tshirt, 2 socks 
          3 | 90.00     | 1 jacket, 1 pants, 1 tshirt
© www.soinside.com 2019 - 2024. All rights reserved.