我有一个关于编写查询发现,并插入到表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在同一时间分割多少?
我会先写我的理解,
因此,如何确定数量的每个项目,以利用大部分我们有这笔钱。
我认为这是可以用不同的方式来描述更加清晰,例如像: - 一个人去的店,想购买每个可用的项目,但如果他有一些余钱,他想了解其他项目,他可以用它购买。如果项目不是很多,钱不是很多,这可能是容易的,但如果项目很多,钱也很多,我可以看到,这可能是一个问题。所以让我们找到一个解决方案。
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
如果任何的假设是错误的,或者如果你需要一些说明我会很乐意提供帮助。
也许,以获得可能的组合最简单的方法是通过自连接,并加入到数字。
如果你想要的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