2015-12-22 34 views
11

Ho una tabella che assomiglia a questo:SQL Server: aggregato condizionale;

Year  Value 
    ----------------- 
    2013  -0.0016 
    2014  -0.0001 
    2015  0.0025 
    2016  -0.0003 
    2017  0.0023 
    2018  0.0002 

e ho bisogno di eseguire un aggregato condizionale che si traduce in una nuova colonna. Le condizioni sono le seguenti:

Se il valore è negativo, l'aggregazione inizia e non si arresta finché il valore non è positivo. Poi più nulla fino a quando il valore è di nuovo negativo ... Il risultato sarà simile a questa:

Year  Value  AggCol 
    2013  -0.0016  -0.0016 
    2014  -0.0001  -0.0017 
    2015  0.0025  0.0008 
    2016  -0.0003  -0.0003 
    2017  0.0023  0.002 
    2018  0.0002  0.0002 

Questa UDF è così vicino come ho ottenuto:

create function dbo.fn(@cYear numeric, @rate float) 
returns float 
as 
begin 
    declare @pYear numeric 
    declare @return float 

    set @pYear = @cYear - 1 

    set @return = (select 
         case 
          when Value < 0 and @rate > 0 then null 
          when Value < 0 then Value + @rate 
          else @rate 
         end 
        from Table1 
        where [year] = @pYear) 

    return @return 
end 

Sono ok con una risposta in C# se fosse più facile, preferisci SQL. Il problema con la funzione che ho realizzato è che devo essere in grado di prendere i risultati dalla riga precedente per aggiungere valore quando il valore è positivo.

Sono stato sveglio tutta la notte su qui alla ricerca di indizi e nessuna gioia ...

EDIT: Quindi, pensare di questi come valori CPI per l'anno da applicare per la bolletta del cellulare dal gestore ... Essi aumenterà la tua fattura solo dal CPI e non la diminuirà mai (se l'IPC è negativo) ... ma compenserà l'IPC negativo degli anni precedenti con l'IPC degli anni attuali se l'IPC dell'anno corrente è positivo (o la somma risulta in positivo) ...

Questo può o non può aiutare ma questa è la situazione lol.

+0

Quale versione di SQL Server si sta utilizzando? –

+0

È possibile eseguire questa operazione utilizzando una query in SQL Server, ma richiede CTE ricorsive. –

+0

2012 e sto bene con CTE ... ho accesso dbo, quindi posso fare tutto il necessario. – user3486773

risposta

6
DECLARE @t TABLE ([Year] INT, Value MONEY) 

INSERT INTO @t 
VALUES (2013, -0.0016), 
     (2014, -0.0001), 
     (2015, 0.0025), 
     (2016, -0.0003), 
     (2017, 0.0023), 
     (2018, 0.0002) 

SELECT t1.Year , 
     t1.Value , 
     oa.AggCol 
FROM @t t1 
     OUTER APPLY (SELECT SUM(Value) AS AggCol 
         FROM  @t t2 
         WHERE  Year <= t1.Year 
           AND Year > (SELECT ISNULL(MAX(Year), 0) 
              FROM @t 
              WHERE Year < t1.Year AND Value > 0) 
        ) oa 

uscita:

Year Value AggCol 
2013 -0.0016 -0.0016 
2014 -0.0001 -0.0017 
2015 0.0025 0.0008 
2016 -0.0003 -0.0003 
2017 0.0023 0.002 
2018 0.0002 0.0002 

Ciò significa: per ogni riga datemi una somma di valori inferiori o uguali a riga corrente e superiori fila massima con valore positivo che appare prima riga corrente, o avviamento da 0 se non è stato trovato.

+0

Lei signore è uno scherzo della natura! Voglio dire che nel modo più gratuito possibile come sono spazzato via dalla velocità che hai risposto a questo! Grazie mille!!!! – user3486773

+0

@ user3486773, grazie anche per la domanda interessante. –

+0

Questo è un grande tentativo e funziona con i dati di test forniti ma si interrompe quando prenderà più valori positivi per rendere la somma progressiva un numero positivo. per esempio. il cambiamento 2014 a .0001 e l'anno 2015 avranno il valore errato presente. – Matt

0

Si può anche farlo utilizzando funzioni finestra:

;WITH PrevValues AS (
    SELECT Year, Value, 
      LAG(Value) OVER (ORDER BY Year) AS prevValue 
    FROM Table1 
), Flags AS (
    SELECT Year, Value, 
     CASE 
      WHEN Value < 0 AND prevValue > 0 THEN 2 -- next slice 
      WHEN Value < 0 OR prevValue < 0 THEN 1 -- same slice 
      WHEN Value > 0 AND prevValue > 0 THEN -1 -- not in a slice 
     END AS flag 
    FROM PrevValues 
), Islands AS (
    SELECT Year, Value,  
     CASE 
      WHEN flag = -1 THEN -1 
      ELSE SUM(flag) OVER (ORDER BY Year)  
       - 
       ROW_NUMBER() OVER (ORDER BY Year) 
     END AS grp 
    FROM Flags 
) 
SELECT Year, Value, 
     CASE 
      WHEN grp = -1 THEN Value 
      ELSE SUM(Value) OVER (PARTITION BY grp ORDER BY Year) 
     END AS AggCol 
FROM Islands 
ORDER BY Year 

L'idea è quella di identificare le isole di righe su cui viene applicata la somma parziale.

Demo here

+0

Questo è un grande tentativo e funziona con i dati di test forniti, ma si interrompe quando prenderà più valori positivi per rendere la somma progressiva un numero positivo. per esempio. il cambiamento 2014 a .0001 e l'anno 2015 avranno il valore errato presente. – Matt

0
DECLARE @t TABLE ([Year] INT, Value MONEY) 
INSERT INTO @t 
VALUES (2013,-0.0016),(2014,0.0001),(2015,0.0025),(2016,-0.0003),(2017,0.0023),(2018,0.0002) 

;WITH cteRowNum AS (
    SELECT *, ROW_NUMBER() OVER (ORDER BY Year) as RowNum 
    FROM 
     @t 
) 
, cteRecursive AS (
    SELECT 
     Year 
     ,Value 
     ,Value as AggCol 
     ,RowNum 
    FROM 
     cteRowNum 
    WHERe 
     RowNum = 1 

    UNION ALL 

    SELECT 
     c.Year 
     ,c.Value 
     ,CASE 
      WHEN AggCol >= 0 THEN c.Value 
      ELSE AggCol + c.Value 
     END 
     ,c.RowNum 
    FROM 
     cteRecursive r 
     INNER JOIN cteRowNum c 
     ON r.RowNum + 1 = c.RowNum 
) 

SELECT Year, Value, AggCol 
FROM 
    cteRecursive 

notare che questo è un insieme di dati di diverso da ciò che hai fornito! ecco i risultati

Year Value  AggCol 
2013 -0.0016  -0.0016 
2014  0.0001  -0.0015 
2015  0.0025  0.001 
2016 -0.0003  -0.0003 
2017  0.0023  0.002 
2018  0.0002  0.0002 

Il problema con i tuoi dati di test originale è che non tiene conto della situazione in cui ci vorrebbe più record positivi sequenziali per fare la somma parziale positivo. Successivamente entrambe le risposte al momento in cui sto postando la mia risposta sono errate. Così ho modificato solo il record del 2014 in positivo .0001 e puoi vedere come funziona questa soluzione e gli altri no.

Probabilmente ci sono modi di fare questo con le funzioni di finestra, ma il CTE ricorsiva è piuttosto semplice così sono andato questa strada:

  • prima costruire un row_number sul set di dati da utilizzare in join per tenere conto di situazioni se manca un ANNO nel set di dati o qualcosa del genere.
  • Quindi creare il cte ricorsivo e la riga del passaggio 1 alla volta utilizzando il numero di riga e determinare se il valore aggregato deve essere ripristinato o aggiunto in base al fatto che il valore di righe precedenti sia positivo o negativo.

Ecco i risultati di Giorgos & risposte di Giorgi se si effettua la modifica dei dati di test:

Year Value  AggCol 
2013 -0.0016  -0.0016 
2014  0.0001  -0.0015 
2015  0.0025  0.0025 
2016 -0.0003  -0.0003 
2017  0.0023  0.002 
2018  0.0002  0.0002 

Si può vedere il problema con l'AggCol per l'anno 2015 è sbagliato

prega nota, penso che le risposte siano ottimi tentativi e mostrino delle vere abilità/codice quando si tratta di lacune/isole. Non sto cercando di attaccare semplicemente migliorare la qualità del post.