C’è un modo per accedere al valore “riga precedente” in un’istruzione SELECT?

Devo calcolare la differenza di una colonna tra due linee di un tavolo. C’è un modo per farlo direttamente in SQL? Sto usando Microsoft SQL Server 2008.

Sto cercando qualcosa di simile a questo:

SELECT value - (previous.value) FROM table 

Immaginando che la variabile “precedente” faccia riferimento all’ultima riga selezionata. Ovviamente con una selezione del genere finirò con le righe n-1 selezionate in una tabella con n righe, che non è un probabilmente, in realtà è esattamente ciò di cui ho bisogno.

È ansible in qualche modo?

SQL non ha una nozione di ordine incorporata, quindi è necessario ordinare per alcune colonne perché ciò sia significativo. Qualcosa come questo:

 select t1.value - t2.value from table t1, table t2 where t1.primaryKey = t2.primaryKey - 1 

Se sai come ordinare le cose ma non come ottenere il valore precedente dato quello attuale (EG, vuoi ordinare alfabeticamente) allora non so come farlo in SQL standard, ma la maggior parte delle implementazioni SQL avrà estensioni per farlo.

Ecco un modo per SQL server che funziona se è ansible ordinare file in modo tale che ognuna sia distinta:

 select rank() OVER (ORDER BY id) as 'Rank', value into temp1 from t select t1.value - t2.value from temp1 t1, temp1 t2 where t1.Rank = t2.Rank - 1 drop table temp1 

Se hai bisogno di rompere i legami, puoi aggiungere tutte le colonne necessarie per ORDER BY.

Usa la funzione di ritardo :

 SELECT value - lag(value) OVER (ORDER BY Id) FROM table 

Le sequenze utilizzate per gli ID possono saltare i valori, quindi Id-1 non sempre funziona.

Oracle, PostgreSQL, SQL Server e molti altri motori RDBMS dispongono di funzioni di analisi chiamate LAG e LEAD che fanno proprio questo.

In SQL Server prima del 2012 è necessario eseguire quanto segue:

 SELECT value - ( SELECT TOP 1 value FROM mytable m2 WHERE m2.col1 < m1.col1 OR (m2.col1 = m1.col1 AND m2.pk < m1.pk) ORDER BY col1, pk ) FROM mytable m1 ORDER BY col1, pk 

, dove COL1 è la colonna che stai ordinando.

Avere un indice su (COL1, PK) migliorerà notevolmente questa query.

 WITH CTE AS ( SELECT rownum = ROW_NUMBER() OVER (ORDER BY columns_to_order_by), value FROM table ) SELECT curr.value - prev.value FROM CTE cur INNER JOIN CTE prev on prev.rownum = cur.rownum - 1 

LEFT UNISCONO la tabella a se stessa, con la condizione di join elaborata in modo che la riga corrispondente nella versione unita del tavolo sia una riga precedente, per la tua particolare definizione di “precedente”.

Aggiornamento: all’inizio pensavo che avresti voluto mantenere tutte le righe, con NULL per la condizione in cui non c’era nessuna riga precedente. Leggendolo di nuovo vuoi solo che le righe vengano abbattute, quindi dovresti un join interno piuttosto che un join sinistro.


Aggiornare:

Le versioni più recenti di Sql Server dispongono anche delle funzioni LAG e LEAD Windowing che possono essere utilizzate anche per questo.

La risposta selezionata funzionerà solo se non ci sono interruzioni nella sequenza. Tuttavia, se si utilizza un ID generato automaticamente, è probabile che vi siano degli spazi vuoti nella sequenza dovuti agli inserti che sono stati ripristinati.

Questo metodo dovrebbe funzionare se hai lacune

 declare @temp (value int, primaryKey int, tempid int identity) insert value, primarykey from mytable order by primarykey select t1.value - t2.value from @temp t1 join @temp t2 on t1.tempid = t2.tempid - 1 
 select t2.col from ( select col,MAX(ID) id from ( select ROW_NUMBER() over(PARTITION by col order by col) id ,col from testtab t1) as t1 group by col) as t2