SELECTステートメントの「前の行」の値にアクセスする方法はありますか?


93

テーブルの2行間の列の差を計算する必要があります。SQLでこれを直接行う方法はありますか?Microsoft SQL Server 2008を使用しています。

私はこのようなものを探しています:

SELECT value - (previous.value) FROM table

「前の」変数が最後に選択された行を参照することを想像します。もちろん、そのような選択では、n行のテーブルでn-1行が選択されることになりますが、それはおそらく、実際には私が必要とするものではありません。

それは何らかの方法で可能ですか?


6
新しい視聴者に役立つコメントを追加するだけです。SQL 2012は現在、LAGおよびLEADを持っている:)このリンク参照してくださいblog.sqlauthority.com/2013/09/22/...
KD

回答:


61

SQLには順序の概念が組み込まれていないため、これを意味のあるものにするために、いくつかの列で順序を付ける必要があります。このようなもの:

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

物事を並べ替える方法は知っているが、現在の値を指定して以前の値を取得する方法がわからない場合(たとえば、アルファベット順に並べ替えたい場合)、標準SQLでそれを行う方法はわかりませんが、ほとんどのSQL実装にはそれを行うための拡張機能。

以下は、それぞれが異なるように行を順序付けできる場合に機能するSQLサーバーの方法です。

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

結合を解除する必要がある場合は、ORDER BYに必要な数の列を追加できます。


順調です。注文は問題ではありません。単純にするために例から削除しただけなので、試してみることにします。
エドウィンジャービス

7
これは、主キーが順番に生成され、行が削除されることはなく、selectには他の注文句がなく、そして...と
仮定

マーティンは正しいです。これが機能する場合もありますが、ビジネスの意味で「前の」という意味を正確に定義する必要があります。できれば、生成されたIDに依存する必要はありません。
トムH

そうです、SQL Server拡張機能を使用して改善を加えました。
RossFabricant 2009

2
「それで結構です、順序は問題ではありません」への応答...それでは、順序を考慮しない場合は、そうしているので、クエリの任意の値を差し引いてみませんか?
JohnFx 2009

79

lag関数を使用します。

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

Idに使用されるシーケンスは値をスキップできるため、Id-1は常に機能するとは限りません。


1
これはPostgreSQLのソリューションです。問題はMSSQLについてです。MSSQLは2012+バージョン(このような機能を有するmsdn.microsoft.com/en-us/en-en/library/hh231256(v=sql.120).aspxを
Kromster

10
@KromStern PostgreSQLソリューションだけではありません。SQLウィンドウ関数は、SQL:2003標準で導入されました。
Hans Ginzel、2015

LAG関数は次の3つのパラメーターを使用できますLAG(ExpressionToSelect, NumberOfRowsToLag, DefaultValue)。ラグするデフォルトの行数は1ですが、セットの先頭にいるためにラグできない場合に選択するデフォルトの値とその値を指定できます。
vaindil 2018年

29

Oracle、PostgreSQL、SQL Server、およびその他の多くのRDBMSエンジンには、分析関数が呼び出されてLAGおりLEAD、これがまさにそれを行っています。

2012より前のSQL Serverでは、次のことを行う必要があります。

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

COL1は注文する列です。

インデックスを付けると、(COL1, PK)このクエリが大幅に改善されます。


14
SQL Server 2012には、LAGとLEADも含まれるようになりました。
ErikE 2013年

Hana SQLスクリプトは、LAGおよびLEADもサポートしています。
mik

ここに来て、Hiveでそれを行うことを探している視聴者に別のコメントを追加するだけです。また、LAGおよびLEAD機能も備えています。ここでのドキュメント:cwiki.apache.org/confluence/display/Hive/...
ハイメカファレル

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

クエリにグループ化がない場合は正しく機能しますが、グループ内でのみ以前の値から値を減算したい場合、同じEmployeeIDと言って、どうすればよいでしょうか。これを実行するCozは、各グループの上位2行に対してのみ機能し、そのグループの残りの行に対しては機能しません。このため、whileループでこのコードを実行しましたが、非常に遅いようです。このシナリオで私たちができる他のアプローチはありますか?そしてそれはSQL Server 2008でのみですか?
Hemant Sisodia、2015年

10

テーブルをそれ自体にLEFT JOINし、結合条件が機能するようにして、テーブルの結合バージョンで一致した行が、「前の」の特定の定義に対して1行前になるようにします。

更新:最初は、すべての行を保持する必要があると考えていましたが、前の行がない場合はNULLを使用します。もう一度読むと、行が間引かれるだけなので、左結合ではなく内部結合を使用する必要があります。


更新:

Sql Serverの新しいバージョンには、これにも使用できるLAGおよびLEADウィンドウ関数も含まれています。


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

選択した回答は、シーケンスにギャップがない場合にのみ機能します。ただし、自動生成されたIDを使用している場合、ロールバックされた挿入が原因でシーケンスにギャップが生じる可能性があります。

この方法は、ギャップがある場合に機能します

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.