2015-05-17 17 views
5

ho un semplice scenario live-caso sulla SQL Server 2014. Ho una tabella con ordini e le loro quantità?:T-SQL distribuire un valore compreso tra le righe senza un cursore

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) 

ho un valore, per esempio 50, che devo distribuire tra gli ordini, in base alla priorità Iiterating attraverso gli ordini

  • se la quantità di sinistra è maggiore quanity dell'ordine corrente, l'ordine sarà realizzata, e la @qty_to_distribute ridotto dalla quantità dell'ordine,
  • in caso contrario - l'ordine è annullato.

Osservando i dati di esempio, gli ordini 1, 2 e 4 saranno realizzati e # 3 annullati.

Esiste una soluzione semplice, efficace e senza cursore, preferibilmente basata su funzioni della finestra?

Questo sembra essere molto promettente, ma non riesco a capire dove mettere le condizioni:

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

Grazie per l'aiuto, per me funziona perfecty, e mi dispiace, stato AFK per un lungo istante. – malpka

+0

grazie, ho cancellato il mio commento precedente :-) –

risposta

6

Prova questo:

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) 

uscita:

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

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

4

AGGIORNAMENTO grazie a 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

Abbiamo pensato allo stesso modo. Puoi usare un cte in un altro. + da parte mia –

+0

e + da me alla stessa ora esatta :-) –

+0

Ho aggiornato la risposta, ma è chiaro che l'idea è tua :-) –