我们的购物车可能包含产品和付款。
由于这些付款将用于购物车,因此产品与付款之间不存在任何关系,除非它们位于同一购物车中。
有些情况下,即使这些产品位于同一购物车中,也会单独开具发票。
要为产品创建发票,我们需要付款详细信息,因此我们必须将产品映射到付款。
这些是我们的表格:
create table Products
(
ItemId int primary key,
CartId int not null,
ItemAmount smallmoney not null
)
create table Payments
(
PaymentId int primary key,
CartId int not null,
PaymentAmount smallmoney not null
)
create table MappedTable
(
ItemId int not null,
PaymentId int not null,
Amount smallmoney not null
)
INSERT INTO Products (ItemId, CartId, ItemAmount)
VALUES (1, 1, 143.49), (2, 1, 143.49), (3, 1, 143.49), (4, 2, 50.00), (5, 3, 75.00), (6, 3, 75.00)
INSERT INTO Payments (PaymentId, CartId, PaymentAmount)
VALUES (1, 1, 376.47), (2, 1, 54.00), (3, 2, 60.00), (4, 3, 140.00)
--select * from Products
--select * from Payments
--DROP TABLE Products
--DROP TABLE Payments
--DROP TABLE MappedTable
产品
ItemId | CartId | ItemAmount
------ | ------ | ----------
1 | 1 | 143.49
2 | 1 | 143.49
3 | 1 | 143.49
4 | 2 | 50.00
5 | 3 | 75.00
6 | 3 | 75.00
付款
PaymentId | CartId | PaymentAmount
--------- | ------ | -------------
1 | 1 | 376.47
2 | 1 | 54.00
3 | 2 | 60.00
4 | 3 | 140.00
产品和付款的顺序可能不同。
我们需要输出看起来像这样:
MappingTable
ItemId | PaymentId | MappedAmount
------ | --------- | ------------
1 | 1 | 143.49
2 | 1 | 143.49
3 | 1 | 89.49
3 | 2 | 54.00
4 | 3 | 50.00 (Remaining 10.00 from Payment 3 will be ignored)
5 | 4 | 75.00
6 | 4 | 65.00 (Missing 10.00 from Payment 4 will be ignored)
我认为像下面这样的查询可以解决问题,但没有运气。
insert into MappedTable
select
prd.ItemId, pay.PaymentId,
(Case
when prd.ItemAmount - isnull((select sum(m.Amount)
from MappedTable m
where m.ItemId = prd.ItemId), 0) <= pay.PaymentAmount - isnull((select sum(m.Amount) from MappedTable m where m.PaymentId = pay.PaymentId), 0)
then prd.ItemAmount - isnull((select sum(m.Amount) from MappedTable m where m.ItemId = prd.ItemId), 0)
else pay.PaymentAmount - isnull((select sum(m.Amount) from MappedTable m where m.PaymentId = pay.PaymentId), 0)
end)
from
Products prd
inner join
Payments pay on pay.CartId = prd.CartId
where
prd.ItemAmount > isnull((select sum(m.Amount) from MappedTable m where m.ItemId = prd.ItemId), 0)
and pay.PaymentAmount > isnull((select sum(m.Amount) from MappedTable m where m.PaymentId = pay.PaymentId), 0)
我已经读过CTE(通用表格表格)和基于集合的方法,但我无法处理这个问题。
这样可以不使用游标或while循环吗?
答案 0 :(得分:0)
通常,这种任务被称为&#34; knapsack problem&#34;,众所周知,没有任何解决办法比强制执行所有可能的组合更有效。但是,在您的情况下,您有其他条件,即订购的商品和付款组合,因此实际上可以使用&#34;重叠间隔&#34;技术
我们的想法是生成物品和付款范围(每个购物车一对范围),然后按顺序查看哪些付款与哪些商品重叠。
对于任何项目付款组合,有3种可能的情况:
因此,所需要的只是,对于每个项目,找到符合上述标准的所有合适的付款,并按其标识符进行排序。这是一个查询:
with cte as (
-- Project payment ranges, per cart
select pm.*, sum(pm.PaymentAmount) over(partition by pm.CartId order by pm.PaymentId) as [RT]
from @Payments pm
)
select q.ItemId, q.PaymentId,
-- Calculating the amount from payment that goes for this item
case q.Match
when 1 then q.PaymentRT - (q.ItemRT - q.ItemAmount)
when 2 then q.PaymentAmount
when 3 then case
-- Single payment spans over several items
when q.PaymentRT >= q.ItemRT and q.PaymentRT - q.PaymentAmount <= q.ItemRT - q.ItemAmount then q.ItemAmount
-- Payment is smaller than item
else q.ItemRT - (q.PaymentRT - q.PaymentAmount)
end
end as [Amount]
--, q.*
from (
select
sq.ItemId, pm.PaymentId, sq.ItemAmount, sq.RT as [ItemRT],
pm.PaymentAmount, pm.RT as [PaymentRT],
row_number() over(partition by sq.CartId, sq.ItemId, pm.PaymentId order by pm.RT) as [RN],
pm.Match
--, sq.CartId
from (
select pr.*, sum(pr.ItemAmount) over(partition by pr.CartId order by pr.ItemId) as [RT]
from @Products pr
) sq
outer apply (
-- First payment to partially cover this item
select top (1) c.*, 1 as [Match] from cte c where c.CartId = sq.CartId
and c.RT > sq.RT - sq.ItemAmount and c.RT < sq.RT
order by sq.RT
union all
-- Any payments that cover this item only
select c.*, 2 as [Match] from cte c where c.CartId = sq.CartId
and c.RT - c.PaymentAmount > sq.RT - sq.ItemAmount
and c.RT < sq.RT
union all
-- Last payment that covers this item
select top (1) c.*, 3 as [Match] from cte c where c.CartId = sq.CartId
and c.RT >= sq.RT
order by sq.RT
) pm
) q
where q.RN = 1;
outer apply
部分是我获得与每件商品相关的付款的地方。唯一的问题是,如果付款涵盖整个项目,则会多次列出。为了删除这些重复项,我使用row_number()
对匹配进行了排序,并添加了一个额外的包装级别 - 带有q
别名的子查询 - 其中我通过过滤行号值来切断任何重复的行。
P.S。如果您的SQL Server版本早于2012年,则需要使用Internet上提供的众多方法之一来计算运行总计,因为sum() over(order by ...)
仅适用于2012及更高版本。