2015-05-17 23 views
5

Mam proste żywo scenariusz na SQL Server 2014. Mam tabeli z zamówienia i ich ilości grubszych:T-SQL dystrybuować wartość między rzędami bez kursora

declare @qty_to_distribute int = 50 

create table orders (id int, priority int, qty int) 

insert into orders (id, priority, qty) values 
(1, 1, 10), 
(2, 2, 30), 
(3, 3, 20), 
(4, 4, 5) 

mam wartość, na przykład 50, który muszę rozprowadzać między zamówień, na podstawie ich priorytetu Iiterating przez zleceń

  • jeżeli kwota lewo jest większa wtedy quanity bieżącego zlecenia, zlecenie zostanie zrealizowane, a @qty_to_distribute zmniejszona według ilości zamówienia,
  • jeśli nie - zamówienie zostanie anulowane.

Przyjrzyj się przykładowym danym - zamówienia 1, 2 i 4 zostaną zrealizowane, a # 3 anulowane.

Czy istnieje proste, efektywne rozwiązanie bez kursora, najlepiej oparte na funkcjach okna?

To wygląda obiecująco, ale nie można dowiedzieć się, gdzie umieścić warunki:

select id, 
sum(qty) OVER (ORDER BY priority asc ROWS UNBOUNDED PRECEDING) as qtysum 
from orders 

id qtysum 
1 10 
2 40 
3 60  * 
4 65  * 

http://sqlfiddle.com/#!6/05fa2/2/0

+0

Dzięki za pomoc, działa na mnie perfecty, i przepraszam, byłem AFK na dłuższy czas. – malpka

+0

dzięki, usunąłem mój poprzedni komentarz :-) –

Odpowiedz

6

Spróbuj tego:

create table o (id int, priority int, qty int) 

insert into o (id, priority, qty) values 
(1, 1, 10), 
(2, 2, 30), 
(3, 3, 20), 
(4, 4, 5), 
(5, 5, 1), 
(6, 6, 10) 

with cte1 as(select *, row_number() over(order by priority) as rn from o), 
cte2 as(
    select top 1 id, priority, qty, rn, 
    case when qty > 50 then 1 else 0 end as ToBeCanceled 
    from cte1 order by rn 
    Union all 
    select cte1.id, cte1.priority, case when cte2.qty + cte1.qty > 50 then cte2.qty 
    else cte2.qty + cte1.qty end as qty, 
    cte1.rn, 
    case when cte2.qty + cte1.qty > 50 then 1 else 0 end as ToBeCanceled 
    from cte1 
    join cte2 on cte1.rn = cte2.rn + 1) 
select id, priority, ToBeCanceled from cte2 
option(maxrecursion 0) 

wyjściowa:

id ToBeCanceled 
1 0 
2 0 
3 1 
4 0 
5 0 
6 1 

Fiddle http://sqlfiddle.com/#!6/87ac9/19

4

Updated dzięki Giorgi Nakeuri

WITH Intermediate AS (
    SELECT 
     Id, 
     ROW_NUMBER() OVER (ORDER BY Priority) as Priority, 
     qty     
    FROM Orders), 
Result AS 
(
    SELECT 
     Id, 
     Priority, 
     CASE when qty < 50 THEN 0 ELSE qty END as Residual, 
     CASE when qty < 50 THEN 50 - qty ELSE 50 END as Remaining 
    FROM Intermediate 
    WHERE Priority = 1 
    UNION ALL 
    SELECT 
     ORD.Id, 
     ORD.Priority, 
     CASE when ORD.qty < Result.Remaining THEN 0 ELSE qty END as Residual, 
     CASE when ORD.qty < Result.Remaining THEN Result.Remaining - ORD.qty ELSE Result.Remaining END as Remaining 
    FROM Intermediate ORD INNER JOIN Result ON ORD.Priority = Result.Priority + 1 
) 
    select Id, Priority, Residual, Remaining from result 

http://sqlfiddle.com/#!6/2ac98f/2

+0

Myśleliśmy w ten sam sposób. Możesz użyć jednego cte w innym. + ode mnie. –

+0

i + ode mnie w tym samym dokładnym czasie :-) –

+0

Uaktualniłem odpowiedź, ale jest jasne, że pomysł jest Twój :-) –