查询返回数量总和小于或等于某个值的记录

时间:2011-08-29 13:03:41

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

DECLARE @TotalMaxQty int
SET @TotalMaxQty = 1000

SELECT
  PKId
  ,Qty
FROM dbo.Sales

PKId          Qty
____          _____
1             100
2             200 
3             750
4             200
...

由于SUM(Qty) <= @TotalMaxQty,我必须获得1,2,3条记录(记录3应部分包括在内,数量= 700)。

谢谢。

3 个答案:

答案 0 :(得分:1)

这些方面的东西应该可以解决问题。 (注意:拥有TOP和子查询的原因是一旦达到目标就停止三角形连接计算。

SELECT  *
FROM    Sales
WHERE   PKId < = ( SELECT TOP 1
                            S1.PKId
                   FROM     Sales S1
                            LEFT JOIN Sales S2 ON S1.PKId >= S2.PKId
                   GROUP BY S1.PKId
                   HAVING   SUM(S2.Qty) >= @TotalMaxQty
                   ORDER BY PKId
                 )

答案 1 :(得分:1)

您可以使用递归CTE计算运行总和。

declare @Sales table (PKId int, Qty int)

insert into @Sales values
(1,             100),
(2,             200), 
(3,             750),
(4,             200)

declare @TotalMaxQty int = 1000

;with OrderedSales as
(
  select PKId,
         Qty,
         row_number() over(order by PKId) as rn
  from @Sales
  --where "some where clause against Sales"
),
RunningSum as
(
  select OS.PKId,
         case when OS.Qty < @TotalMaxQty then OS.Qty
              else @TotalMaxQty
         end as Qty,
         @TotalMaxQty - OS.Qty as Rest,
         OS.rn
  from OrderedSales OS
  where rn = 1
  union all
  select OS.PKId,
         case when OS.Qty < RS.Rest then OS.Qty
              else RS.Rest
         end as Qty,
         RS.Rest - OS.Qty,
         OS.rn
  from OrderedSales as OS
    inner join RunningSum as RS
      on OS.rn = RS.rn + 1
  where RS.Rest > 0
)
select PKId,
       Qty
from RunningSum
option (maxrecursion 0)

编辑:将订购的销售额存储在以rn为主键的表变量中的版本。我的测试表明性能大大提高。

declare @Sales table (PKId int, Qty int)

insert into @Sales values
(1,             100),
(2,             200), 
(3,             750),
(4,             200)

declare @TotalMaxQty int = 1000

declare @OrderedSales table
( 
  rn int primary key,
  PKId int,
  Qty int
)

insert into @OrderedSales
select row_number() over(order by PKId),
       PKId,
       Qty
from @Sales
--where "some where clause against Sales"

;with RunningSum as
(
  select OS.PKId,
         case when OS.Qty < @TotalMaxQty then OS.Qty
              else @TotalMaxQty
         end as Qty,
         @TotalMaxQty - OS.Qty as Rest,
         OS.rn
  from @OrderedSales OS
  where rn = 1
  union all
  select OS.PKId,
         case when OS.Qty < RS.Rest then OS.Qty
              else RS.Rest
         end as Qty,
         RS.Rest - OS.Qty,
         OS.rn
  from @OrderedSales as OS
    inner join RunningSum as RS
      on OS.rn = RS.rn + 1
  where RS.Rest > 0

)
select PKId,
       Qty
from RunningSum
option (maxrecursion 0)

答案 2 :(得分:1)

  

记录3应部分包含在Qty = 700

这部分在sql中是个坏主意,原因有两个:在客户端代码中执行此操作会更有效率,并且为了做到这一点,您还需要在某处更新或插入记录以了解如何剩余的大部分数量(意味着你需要另一个困难的查询)。

但如果你坚持:

SELECT s.PKId, CASE WHEN PriorTotal + Qty > @TotalMaxQty THEN @TotalMaxQty - PriorTotal ELSE Qty END As Qty
FROM SALES s
INNER JOIN (
   SELECT s1.PKId, Sum(s2.Qty) As PriorTotal 
   FROM SALES s1
   LEFT JOIN SALES s2 ON s2.PKId < s1.PKId
   GROUP BY s1.PKId
) q ON q.PKId = s.PKId
WHERE q.PriorTotal < @TotalMaxQty
ORDER BY s.Qty