2011-08-29 19 views
1
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(記錄3應部分包含,Qty = 700),我必須獲得1,2,3條記錄。返回數量小於或等於某個值的總和的記錄的查詢

謝謝。

+1

這基本上是運行總和。您的查詢通常會返回多少行?這將決定三角聯合方法是否可行。 –

+0

@Martin Smith每個查詢不會再有500-800條記錄。 – garik

+0

啊對了。用三角形連接應該不會太差(自己加入'Sales1.PKId

回答

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) 
+0

它的工作原理!但性能。 – garik

+0

@garik - 這是第一個CTE是cuplrit,或者說是列「rn」上缺少索引。添加了一個版本,其中結果訂購的銷售結果存儲在一個表格變量中,然後遞歸使用。如果這對你是可行的,將會有更好的表現。 –

+0

賓果!它完美的作品! – garik

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

謝謝你的回答 – garik

1

記錄3應該部分地與數量列入= 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 
+0

輸出100,500,200 – garik

+0

@garik - 修復它。在數量上是匹配的,而不是在最內部的連接上使用pkid,並且爲了好玩而添加了一個訂單。 –

+0

不錯!謝謝你讓我想起它,你忘了一個修正(當然有趣)關於'Sum(ISNULL(s2.Qty,s1.Qty))作爲PriorTotal' :)第一個記錄被移位 – garik

相關問題