2012-04-27 12 views
4

私は株式の日々の価格を格納するSQLテーブルを持っています。市場が閉鎖された後、毎日新しい記録が挿入されます。私は価格が連続的に上昇している株を探したい。列の値が連続して増加する行を見つける

テーブルは列の多くを持っていますが、これは、関連するサブセットです:

quoteid  stockid  closeprice  createdate 
-------------------------------------------------- 
    1   1    1  01/01/2012 
    2   2    10  01/01/2012 
    3   3    15  01/01/2012 

    4   1    2  01/02/2012 
    5   2    11  01/02/2012 
    6   3    13  01/02/2012 

    7   1    5  01/03/2012 
    8   2    13  01/03/2012 
    9   3    17  01/03/2012 

    10   1    7  01/04/2012 
    11   2    14  01/04/2012 
    12   3    18  01/04/2012 

    13   1    9  01/05/2012 
    14   2    11  01/05/2012 
    15   3    10  01/05/2012 

quoteid列が主キーです。

この表では、株式id1の終値が毎日増加しています。株式id3は大きく変動し、株式id2の価格は最終日に低下した。

私はこのような結果を探しています

stockid  Consecutive Count (CC) 
---------------------------------- 
    1    5 
    2    4 

あなたは連続したストリークの日付で出力を得ることができる場合、それはさらに良いでしょう:価格が始まったとき

stockid  Consecutive Count (CC)  StartDate  EndDate 
--------------------------------------------------------------- 
    1    5     01/01/2012 01/05/2012 
    2    4     01/01/2012 01/04/2012 

StartDateがあります増加し、EndDateは、雄牛の走りが実際に終了したときです。

これは簡単な問題ではないと私は考えています。私はこの連続したシナリオにも対処する他の投稿を見てきましたが、私のニーズに合っていません。私と似ている投稿を知っているなら、私に知らせてください。

+1

あなたの最小長の連続的な増加は、一日よりも大きかったらどうしますか?あるいは、何とかその減少によって相殺されましたか?そして、私はそれについてのデータがあれば、あなたは複数の実行を見たいと思っています。 –

+1

週末のようにデータに隙間がありますか?そこで何をする必要がありますか? –

+0

私は連続的な増加のための規則を持っていません、それは前日よりも大きくなければなりません。はい、私は複数の実行を探しています。私はこの質問を過去3ヶ月、6ヶ月のデータにわたって実行しているか、それ以上であるかもしれません。データにギャップがあります、私たちは前の日のレコードを取得するために主キーの列を使用することができます –

答えて

6

いずれにしても、それは在庫あたりの行数の増加に役立ちます(実際のquoteidの値は実際には役に立ちません)。 (この表の)のキャプチャは最も簡単です。(営業日のみ、週末/休日を無視するなど)何かが必要な場合は、より関与します。おそらくカレンダーファイルが必要です。 [stockidcreatedate]以上のインデックスがまだ存在しない場合は、そのインデックスを使用します。

提供されたデータから、以下の結果得
WITH StockRow AS (SELECT stockId, closePrice, createdDate, 
         ROW_NUMBER() OVER(PARTITION BY stockId 
              ORDER BY createdDate) rn 
        FROM Quote), 

    RunGroup AS (SELECT Base.stockId, Base.createdDate, 
         MAX(Restart.rn) OVER(PARTITION BY Base.stockId 
               ORDER BY Base.createdDate) groupingId 
        FROM StockRow Base 
        LEFT JOIN StockRow Restart 
         ON Restart.stockId = Base.stockId 
          AND Restart.rn = Base.rn - 1 
          AND Restart.closePrice > Base.closePrice) 

SELECT stockId, 
     COUNT(*) AS consecutiveCount, 
     MIN(createdDate) AS startDate, MAX(createdDate) AS endDate 
FROM RunGroup 
GROUP BY stockId, groupingId 
HAVING COUNT(*) >= 3 
ORDER BY stockId, startDate 

Increasing_Run 
stockId consecutiveCount startDate endDate 
=================================================== 
1   5     2012-01-01 2012-01-05 
2   4     2012-01-01 2012-01-04 
3   3     2012-01-02 2012-01-04 

SQL Fiddle Example
を(フィドルも複数の実行のための例があります)

この分析は、すべてのギャップを無視し、すべての実行に正しく一致します(次回の正の実行が開始されたとき)。


ここでは何が起こっているのですか?

StockRow AS (SELECT stockId, closePrice, createdDate, 
        ROW_NUMBER() OVER(PARTITION BY stockId 
             ORDER BY createdDate) rn 
      FROM Quote) 

このCTEは1つの目的のために使用されている:我々は、次/前の行を見つけるための方法を必要とするので、最初に我々は(日付の)ために、各列に番号を付けます...

RunGroup AS (SELECT Base.stockId, Base.createdDate, 
        MAX(Restart.rn) OVER(PARTITION BY Base.stockId 
             ORDER BY Base.createdDate) groupingId 
      FROM StockRow Base 
      LEFT JOIN StockRow Restart 
        ON Restart.stockId = Base.stockId 
         AND Restart.rn = Base.rn - 1 
          AND Restart.closePrice > Base.closePrice) 

...インデックスに基づいて結合します。もしあなたがLAG()/LEAD()を持っているものに終わるなら、それらを代わりに使うことは、ほぼ確実に良い選択です。ここでは重要なことが1つあります。一致するのは、行がの順序外の場合です((前の行よりも小さい)。それ以外の場合、値はnullLAG(), you'd need to use something likeの場合はそれ以降にこれを取り除くことになります)になります。あなたはこのようになります一時的なセットを取得:

B.rn B.closePrice B.createdDate R.rn R.closePrice R.createdDate groupingId 
1  15    2012-01-01  -  -    -    - 
2  13    2012-01-02  1  15    2012-01-01  1 
3  17    2012-01-03  -  -    -    1 
4  18    2012-01-04  -  -    -    1 
5  10    2012-01-05  4  18    2012-01-04  4 

を...だから、以前は「現在」の行よりも大きかっただけRestartの値があります。ウィンドウ関数内のMAX()の使用は、これまでに見た最大の値に使用されています... nullが最も小さいため、別の不一致が発生するまで(他の値を与える) 。この時点で、最終的な集計に備えて、というクエリの中間結果が得られます。

SELECT stockId, 
     COUNT(*) AS consecutiveCount, 
     MIN(createdDate) AS startDate, MAX(createdDate) AS endDate 
FROM RunGroup 
GROUP BY stockId, groupingId 
HAVING COUNT(*) >= 3 
ORDER BY stockId, startDate 

クエリの最後の部分は、実行の開始日と終了日を取得し、それらの日付の間のエントリ数をカウントしています。日付計算にもっと複雑なものがあった場合は、おそらくこの時点で発生する必要があります。 GROUP BYは、のいくつかの正当なインスタンスのうちの1つを示しています。これは、SELECT句の列を含むではありません。 HAVING句は、「短すぎる」実行を排除するために使用されます。

+0

非常に便利なありがとう! –

1

私はおおよそのように、CTEをしようと:これは何のギャップを負いません

with increase (stockid, startdate, enddate, cc) as 
(
    select d2.stockid, d1.createdate as startdate, d2.createdate as enddate, 1 
    from quote d1, quote d2 
    where d1.stockid = d2.stockid 
    and d2.closedprice > d1.closedprice 
    and dateadd(day, 1, d1.createdate) = d2.createdate 

    union all 

    select d2.stockid, d1.createdate as startdate, cend.enddate as enddate, cend.cc + 1 
    from quote d1, quote d2, increase cend 
    where d1.stockid = d2.stockid and d2.stockid = cend.stockid 
    and d2.closedprice > d1.closedprice 
    and d2.createdate = cend.startdate 
    and dateadd(day, 1, d1.createdate) = d2.createdate 
) 
select o.stockid, o.cc, o.startdate, o.enddate 
from increase o where cc = (select max(cc) from increase i where i.stockid = o.stockid and i.enddate = o.enddate) 

dateadd(day, 1, d1.createdate) = d2.createdateの基準は、d2がd1の後の「次の日」であるかどうかを示す他のものに置き換えなければならない。

0

これは私の必要に応じた最終的なSQLです。テストでは正常に動作していることがわかります。私は@OranからのCCのメソッドを使用しています

WITH StockRow (stockId, [close], createdDate, rowNum) 
as 
(
    SELECT stockId,   [close],     createdDate, 
      ROW_NUMBER() OVER(PARTITION BY stockId ORDER BY createdDate) 
    FROM dbo.Quote 
    where createddate >= '01/01/2012' --Beginning of this year 
    ), 

    RunStart (stockId, [close], createdDate, runId) as (
    SELECT  a.stockId,  a.[close], a.createdDate, 
      ROW_NUMBER() OVER(PARTITION BY a.stockId ORDER BY a.createdDate) 
    FROM StockRow as a 
    LEFT JOIN StockRow as b 
    ON b.stockId = a.stockId 
    AND b.rowNum = a.rowNum - 1 
    AND b.[close] < a.[close] 
    WHERE b.stockId IS NULL) 
    , 
RunEnd (stockId, [close], createdDate, runId) as (
    SELECT a.stockId, a.[close], a.createdDate, 
      ROW_NUMBER() OVER(PARTITION BY a.stockId ORDER BY a.createdDate) 
    FROM StockRow as a 
    LEFT JOIN StockRow as b 
    ON b.stockId = a.stockId 
    AND b.rowNum = a.rowNum + 1 
    AND b.[close] > a.[close] 
    WHERE b.stockId IS NULL) 

SELECT a.stockId,  s.companyname,   s.Symbol, 
a.createdDate as startdate,  b.createdDate as enddate, 
(select count(r.createdDate)  from  dbo.quote r  where r.stockid = b.stockid and  r.createdDate   between a.createdDate  and  b.createdDate) as BullRunDuration 
FROM RunStart as a JOIN RunEnd as b 
ON b.stockId = a.stockId 
join dbo.stock as s 
on a.stockid = s.stockid 
AND b.runId = a.runId 
AND b.[close] > a.[close] 
and (select count(r.createdDate) from dbo.quote r where r.stockid = b.stockid and 
r.createdDate between a.createdDate and b.createdDate) > 2 -- trying to avoid cluter 
order by 6 desc, a.stockid 
関連する問題