我遇到一种情况,即编写查询以查找表A中所有行的组合并将其插入表B中,条件是:
a x b =第1行的总数
c x d =第2行的总计...等其中count(total)<= X
商品的“ a”价格
“ b”个项目
想法是有所有组合等例子
有关$ 100美元,我可以买:
2 tshirt, 1 jacket, 1 pants
或
1 tshirt, 2 jacket, 1 pants
...等
创建游标将帮助我为每一行运行查询,但是如何同时在col.quantity中拆分数字呢?
答案 0 :(得分: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或商品价格乘以qty和sub total,如果我们选择过滤女巫组合,我们可以很容易地显示该表正在选择最接近金额的第一个,我们可以更改查询的最后一部分以显示所需的表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'仅将结果合并为(qty + item +逗号),因此以下解决方案可能会有所帮助。
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
如果任何假设是错误的,或者您需要一些描述,我将很乐意为您提供帮助。
答案 1 :(得分: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;
对 db <>小提琴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