Bir SELECT deyiminde "önceki satır" değerine erişmenin bir yolu var mı?


96

Bir tablonun iki satırı arasındaki bir sütunun farkını hesaplamam gerekiyor. Bunu doğrudan SQL'de yapabilmemin bir yolu var mı? Microsoft SQL Server 2008 kullanıyorum.

Bunun gibi bir şey arıyorum:

SELECT value - (previous.value) FROM table

"Önceki" değişkenin en son seçilen satıra başvurduğunu hayal etmek. Tabii ki böyle bir seçimle n satırlı bir tabloda n-1 satır seçilecek, bu muhtemelen değil, aslında tam olarak ihtiyacım olan şey.

Bu bir şekilde mümkün mü?


6
Sadece yeni izleyiciler için faydalı bir yorum ekledik. SQL 2012'de artık LAG ve LEAD var :) Bu bağlantıya bakın blog.sqlauthority.com/2013/09/22/…
KD

Yanıtlar:


65

SQL'de yerleşik bir düzen kavramı yoktur, bu nedenle bunun anlamlı olması için bir sütuna göre sıralamanız gerekir. Bunun gibi bir şey:

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

Bir şeyleri nasıl sipariş edeceğinizi biliyorsanız, ancak mevcut değer verilen önceki değeri nasıl alacağınızı bilmiyorsanız (örneğin, alfabetik olarak sıralamak istiyorsunuz) bunu standart SQL'de yapmanın bir yolunu bilmiyorum, ancak çoğu SQL uygulamasında bunu yapmak için uzantılar.

Aşağıda, satırları her biri farklı olacak şekilde sıralayabiliyorsanız çalışan SQL sunucusu için bir yol gösterilmektedir:

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

Eğer bağları koparmanız gerekirse, ORDER BY'a gerektiği kadar sütun ekleyebilirsiniz.


Sorun değil, düzen sorun değil, basitleştirmek için onu örnekten çıkardım, deneyeceğim.
Edwin Jarvis

7
... hangi birincil anahtarlar sırasıyla oluşturulur ve satırları silinir hiçbir zaman, kabul basıp başka düzen maddesini ve ve ve yok
MartinStettner

Martin haklı. Bu bazı durumlarda işe yarayabilirse de, ticari anlamda "önceki" ile tam olarak ne kastettiğinizi, tercihen oluşturulan bir kimliğe güvenmeden tam olarak tanımlamanız gerekir.
Tom H

Haklısın, bir SQL Server uzantısı kullanarak bir iyileştirme ekledim.
RossFabricant

2
"Sorun değil, düzen sorun değil" cevabına göre ... O halde neden sorgunuzdan bir arbitrasyon değeri çıkarmıyorsunuz, çünkü sırayı düşünmüyorsanız yaptığınız şey budur?
JohnFx

85

Gecikme işlevini kullanın :

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

Kimlikler için kullanılan diziler değerleri atlayabilir, bu nedenle Id-1 her zaman çalışmaz.


1
Bu PostgreSQL çözümüdür. Soru MSSQL ile ilgili. MSSQL, 2012+ ( msdn.microsoft.com/en-us/en-en/library/hh231256(v=sql.120).aspx ) sürümlerinde böyle bir işleve sahiptir
Kromster

10
@KromStern Sadece PostgreSQL çözümü değil. SQL Window fonksiyonları SQL: 2003 standardında tanıtıldı .
Hans Ginzel

UDK fonksiyonu üç parametre alabilir: LAG(ExpressionToSelect, NumberOfRowsToLag, DefaultValue). Gecikecek varsayılan satır sayısı 1'dir, ancak bunu ve kümenin başında olduğunuz için gecikmenin mümkün olmadığı durumlarda seçmek için varsayılan değeri belirtebilirsiniz.
vaindil

30

Oracle, PostgreSQL, SQL Server ve daha birçok RDBMS motorunun analitik işlevleri vardır LAGve LEADbu da bunu yapar.

2012'den önceki SQL Server'da aşağıdakileri yapmanız gerekir:

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

, COL1sipariş verdiğiniz sütun nerede .

Bir dizine sahip olmak, (COL1, PK)bu sorguyu büyük ölçüde geliştirecektir.


14
SQL Server 2012'de artık LAG ve LEAD de var.
ErikE

Hana SQL betiği ayrıca LAG ve LEAD'i de destekler.
mik

Hive'da bunu yapmak için buraya gelen izleyicilere başka bir yorum eklemek için. Ayrıca LAG ve LEAD işlevlerine de sahiptir. Belgeler burada: cwiki.apache.org/confluence/display/Hive/…
Jaime Caffarel

27
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

Sorguda gruplama yoksa doğru çalışır, ancak ya önceki değerden değerleri sadece bir grup içinde çıkarmak istersek, aynı EmployeeID diyelim, o zaman bunu nasıl yapabiliriz? Çünkü bunu çalıştırmak, o gruptaki diğer satırlar için değil, her grubun yalnızca ilk 2 satırı için çalışır. Bunun için, bu kodu while döngüsünde çalıştırmayı kullandım, ancak bu çok yavaş görünüyor. Bu senaryoda yapabileceğimiz başka bir yaklaşım var mı? Ve bu da sadece SQL Server 2008'de?
Hemant Sisodia

10

SOL BİRLEŞTİRME koşulu oluşturulmuş, böylece tablonun birleştirilmiş sürümünde eşleşen satır, "önceki" tanımınız için bir satır öncedir.

Güncelleme: İlk başta, önceki satırın olmadığı durumda tüm satırları NULL ile tutmak isteyeceğinizi düşünmüştüm. Tekrar okurken sadece satırların ayıklanmasını istersiniz, bu nedenle bir sol birleşim yerine bir iç birleşim yapmalısınız.


Güncelleme:

Sql Server'ın daha yeni sürümleri, bunun için de kullanılabilen LAG ve LEAD Windowing işlevlerine sahiptir.


3
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

Seçilen cevap yalnızca sıralamada boşluk yoksa işe yarar. Ancak, otomatik olarak oluşturulmuş bir kimlik kullanıyorsanız, geri alınan eklemeler nedeniyle dizide büyük olasılıkla boşluklar olabilir.

Boşluklarınız varsa bu yöntem işe yaramalı

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.