Muszę obliczyć różnicę kolumny między dwoma wierszami tabeli. Czy jest jakiś sposób, żebym mógł to zrobić bezpośrednio w SQL? Używam Microsoft SQL Server 2008.
Szukam czegoś takiego:
SELECT value - (previous.value) FROM table
Wyobrażając sobie, że „poprzednia” zmienna odwołuje się do ostatnio wybranego wiersza. Oczywiście z taką selekcją skończę z n-1 wierszami wybranymi w tabeli z n wierszami, to nie jest prawdopodobnie, w rzeczywistości jest dokładnie tym, czego potrzebuję.
Czy to w jakiś sposób możliwe?
sql
sql-server
sql-server-2008
Edwin Jarvis
źródło
źródło
Odpowiedzi:
SQL nie ma wbudowanego pojęcia kolejności, więc aby mieć znaczenie, musisz uporządkować według jakiejś kolumny. Coś takiego:
select t1.value - t2.value from table t1, table t2 where t1.primaryKey = t2.primaryKey - 1
Jeśli wiesz, jak uporządkować, ale nie wiesz, jak uzyskać poprzednią wartość podaną bieżącą (EG, chcesz uporządkować alfabetycznie), to nie wiem, jak to zrobić w standardowym SQL, ale większość implementacji SQL będzie miała rozszerzenia, aby to zrobić.
Oto sposób na serwer SQL, który działa, jeśli możesz uporządkować wiersze w taki sposób, aby każdy z nich był inny:
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
Jeśli chcesz zerwać remisy, możesz dodać tyle kolumn, ile potrzeba, do ORDER BY.
źródło
Użyj funkcji opóźnienia :
SELECT value - lag(value) OVER (ORDER BY Id) FROM table
Sekwencje używane dla identyfikatorów mogą pomijać wartości, więc Id-1 nie zawsze działa.
źródło
LAG(ExpressionToSelect, NumberOfRowsToLag, DefaultValue)
. Domyślna liczba wierszy do opóźnienia to 1, ale możesz to określić i domyślną wartość do wybrania, gdy nie można opóźnić, ponieważ jesteś na początku zestawu.Oracle, PostgreSQL, SQL Server i wiele więcej RDBMS silniki mają funkcji analitycznych nazwie
LAG
iLEAD
że zrobić tę samą rzecz.W programie SQL Server sprzed 2012 r. Należałoby wykonać następujące czynności:
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
, gdzie
COL1
jest kolumna, według której zamawiasz.Posiadanie indeksu
(COL1, PK)
znacznie poprawi to zapytanie.źródło
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
źródło
LEWY DOŁĄCZ tabelę do samej siebie, z warunkiem łączenia opracowanym w taki sposób, aby wiersz dopasowany w połączonej wersji tabeli znajdował się o jeden wiersz wcześniej, dla określonej definicji „poprzedniego”.
Aktualizacja: Na początku myślałem, że chcesz zachować wszystkie wiersze, z wartością NULL dla warunku, w którym nie było poprzedniego wiersza. Czytając go ponownie, chcesz po prostu usunąć te wiersze, więc powinieneś raczej połączyć wewnętrzne niż lewe.
Aktualizacja:
Nowsze wersje Sql Server mają również funkcje LAG i LEAD Windowing, których można również użyć do tego.
źródło
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
źródło
Wybrana odpowiedź będzie działać tylko wtedy, gdy nie ma przerw w sekwencji. Jeśli jednak używasz automatycznie generowanego identyfikatora, prawdopodobnie wystąpią przerwy w sekwencji spowodowane wstawieniami, które zostały wycofane.
Ta metoda powinna działać, jeśli masz luki
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
źródło