0

someIDで識別される別のエンティティの時系列を表す単純なテーブルがあるとします。各行はsomeIDとタイムスタンプで識別されます。タイムスタンプは定期的な増分の対象ではありません。つまり、間隔は次のように変化する可能性があります。

CREATE TABLE someSeries
(
   someID int not null,
   rowTS datetime not null,
   val int not null
);
ALTER TABLE someSeries 
ADD CONSTRAINT PK_someSeries(someID, rowTS);

すべての行を返し、その行のrowTSとそのsomeIDの最新の前のrowTSを表示するためのエレガントで効率的な方法(デカルト積を使用しない)はありますか?

たとえば、データが

someID        rowTS            val
------------------------------------
1             9/1/2012         2
1             9/2/2012         3
1             9/5/2012         5
2             9/2/2012         1
2             9/4/2012         6
3             9/5/2012         7
3             9/7/2012         9
3             9/10/2012        2

そのクエリは

someID        rowTS            prevRowTS          val          prevVal
------------------------------------------------------------------------
1             9/1/2012         null               2            null
1             9/2/2012         9/1/2012           3            2 
1             9/5/2012         9/2/2012           5            3
2             9/2/2012         null               1            null
2             9/4/2012         9/2/2012           6            1
3             9/5/2012         null               7            null
3             9/7/2012         9/5/2012           9            7
3             9/10/2012        9/7/2012           2            9

現在、アプリにこのようなものが必要です。これを行う方法はアプリケーション層です。基本的に、最後のrowTSをPKであるsomeIDマスターテーブルに格納し、時系列を挿入すると、次のようになります。マスターテーブルから値を取得し、最新の前のレコードを検索し、いくつかの計算(valとprevValの比較など)を実行して、時系列テーブルに挿入します。

しかし、SQLだけでそれを行うための高速な方法があるかどうか疑問に思いました。頭に浮かぶのはデカルト積だけですが、言うまでもなく、それはあまり効率的ではありません。

4

3 に答える 3

2

SQL Server、Oracle、およびPostgreSQLの場合-ウィンドウ関数を使用

;with cte as (
select *, rn=row_number() over (partition by someid order by rowTS)
from someSeries
)
select a.someID, a.rowTS, b.rowTS prevRowTS, a.val, b.val prevVal
from cte a
left join cte b on a.someid = b.someID and b.rn = a.rn-1
order by a.someID, a.rowts

SQL Server 2012およびOracleの場合、上記を簡単に上回るLAG関数を使用します。

select
    someid,
    rowts,
    lag(rowts) over (partition by someid order by rowts) prevrowts,
    val,
    lag(val) over (partition by someid order by rowts) prevval
from someSeries
order by someid, rowts

MySQLの場合のみ、ハックしますが、非常にうまく機能します。

select
  @ts:=rowts rowts,
  if(@s=someID,@ts,null) prevrowts,
  @v:=val val,
  if(@s=someID,@v,null) prevval,
  @s:=someID someID
from (select @s:=null) a, someSeries
order by someID, rowts

注:誘惑されるかもしれませんが、someID列を他の列の前に移動しないでください。

于 2012-10-02T00:47:35.567 に答える
1

使用しているRDBMSは重要ではないとおっしゃっていたので、SQLServerでこれを行う方法は次のとおりです。

;WITH cte
AS
(
    SELECT *, ROW_NUMBER() OVER(Partition BY someID ORDER BY someID, rowTS) row_num
    FROM @Temp
)
SELECT c1.someID, c1.rowTS, 
  (SELECT MAX(c2.rowTS) 
   FROM cte c2 
   WHERE c2.someID = c1.someID AND c2.row_num < c1.row_num) AS prevRowTS,
  c1.val,
  (SELECT MAX(c2.val) 
   FROM cte c2 
   WHERE c2.someID = c1.someID AND c2.row_num < c1.row_num) AS prevVal
FROM cte c1

これがライブデモです

于 2012-10-02T00:58:41.777 に答える
0

これは、この質問と非常によく似ています。SQLは、日付と別の列に基づいて2行を減算します

そこには多くの解決策があります。

于 2012-10-02T01:01:58.747 に答える