Czy istnieje sposób uzyskania dostępu do wartości "poprzedni wiersz" w instrukcji SELECT?

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 zmienna" poprzednia " odwołuje się do ostatniego wybranego wiersza. Oczywiście z select jak, że skończę z N-1 wierszy wybranych w tabeli z n wierszy, to nie jest prawdopodobnie, faktycznie jest dokładnie to, czego potrzebuję.

Czy jest to możliwe w niektórych sposób?

Author: Gabriel L., 2009-04-02

7 answers

SQL nie ma wbudowanego pojęcia porządku, więc musisz zamówić według jakiejś kolumny, aby to miało znaczenie. Coś takiego:

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

Jeśli wiesz, jak zamawiać rzeczy, ale nie jak uzyskać poprzednią wartość biorąc pod uwagę bieżącą (np. chcesz zamówić Alfabetycznie) to nie wiem jak to zrobić w standardowym SQL, ale większość implementacji SQL będzie mieć rozszerzenia do tego.

Oto sposób na SQL server, który działa, jeśli można zamówić wiersze takie, że każdy z nich jest odrębny:

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ć krawaty, możesz dodać dowolną liczbę kolumn do zamówienia przez.

 46
Author: RossFabricant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-02 15:49:30

Użyj funkcji lag :

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

Sekwencje używane dla Ids mogą pomijać wartości, więc Id - 1 nie zawsze działa.

 32
Author: Hans Ginzel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 07:15:06

Oracle, PostgreSQL, SQL Server i wiele innych silników RDBMS mają funkcje analityczne o nazwie LAG i LEAD, które robią to właśnie.

W SQL Server przed 2012 rokiem musisz 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 kolumną, którą zamawiasz.

Posiadanie indeksu na {[4] } znacznie poprawi to zapytanie.

 25
Author: Quassnoi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-03 12:08:13
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
 24
Author: Jeremy Stein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-29 20:37:33

LEFT JOIN the table to itself, with the join condition worked so the row matched in the joined version of the table is one row previous, for your specific definition of "previous".

Update: na początku myślałem, że chcesz zachować wszystkie wiersze, z NULL dla warunku, w którym nie było poprzedniego wiersza. Czytając to jeszcze raz, chcesz tylko, aby wiersze były culled, więc powinieneś połączyć wewnętrzne, a nie lewe.


Update:

Nowsze wersje Sql Server ma również funkcje LAG i LEAD Windowing, które mogą być również używane do tego celu.

 8
Author: Joel Coehoorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-01 21:46:51

Wybrana odpowiedź będzie działać tylko wtedy, gdy nie ma luk w sekwencji. Jeśli jednak używasz autogenerowanego identyfikatora, prawdopodobnie będą luki w sekwencji spowodowane wstawkami, 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
 2
Author: HLGEM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-02 18:51:50
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
 2
Author: user1920851,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-06 07:39:39