查找列的值连续增加的行

Man*_*jua 5 sql sql-server-2008-r2

我有一个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
Run Code Online (Sandbox Code Playgroud)

quoteid列是主键.

在表中,股票ID 1的收盘价每天都在增加.股票ID 3波动很大,股票ID 2的价格在最后一天下跌.

我正在寻找这样的结果:

stockid     Consecutive Count (CC)
----------------------------------
    1                5
    2                4
Run Code Online (Sandbox Code Playgroud)

如果你能得到连续连胜的日期输出,那就更好了:

stockid     Consecutive Count (CC)      StartDate      EndDate
---------------------------------------------------------------
    1                5                 01/01/2012    01/05/2012
    2                4                 01/01/2012    01/04/2012
Run Code Online (Sandbox Code Playgroud)

StartDate是价格开始上涨的时候,也EndDate就是牛市实际结束的时候.

我认为这不是一个容易的问题.我在这里查看了其他帖子,这些帖子也处理了这个连续的场景,但它们不符合我的需求.如果你知道任何类似我的帖子,请告诉我.

Clo*_*use 7

在任何情况下,它都有助于增加每行的行数(实际quoteid值在这里并不真正有用).捕获的天数(在此表中)是最简单的 - 如果您想要其他东西(例如只有工作日,忽略周末/假期或其他什么),它会更多地参与其中; 你可能需要一个日历文件.如果您还没有索引,那么您将希望索引超过[ stockid,createdate].

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
Run Code Online (Sandbox Code Playgroud)

从提供的数据中得出以下结果:

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
Run Code Online (Sandbox Code Playgroud)

SQL Fiddle Example
(小提琴也有一个多次运行的例子)

此分析将忽略所有间隙,正确匹配所有运行(下次正运行开始时).


那么这里发生了什么?

StockRow AS (SELECT stockId, closePrice, createdDate,
                    ROW_NUMBER() OVER(PARTITION BY stockId 
                                      ORDER BY createdDate) rn
             FROM Quote)
Run Code Online (Sandbox Code Playgroud)

这个CTE用于一个目的:我们需要一种方法来查找下一行/上一行,所以首先我们按顺序(日期)对每一行进行编号...

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)
Run Code Online (Sandbox Code Playgroud)

...然后根据索引加入它们.如果你最终得到的东西LAG()/ LEAD(),使用它们几乎肯定会是一个更好的选择.这里有一个关键的东西 - 匹配仅在行不按顺序(小于前一行)时才会出现.否则,该值最终为null(LAG()之后使用CASE`将其拉出).你得到一个看起来像这样的临时集:

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
Run Code Online (Sandbox Code Playgroud)

...所以CASE只有当前一个大于"当前"行时才有值.使用的Restart窗口功能被用来迄今所看到的最大价值......这是因为MAX()最低,导致直至出现另一个不匹配(这给出了一个新值)要保留所有其他行的行索引.此时,我们基本上具有查询的中间结果,为最终聚合做好准备.

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
Run Code Online (Sandbox Code Playgroud)

查询的最后一部分是获取运行的开始和结束日期,并计算这些日期之间的条目数.如果日期计算有更复杂的事情,那么可能需要在此时进行.在null被示出的几个合法实例中的一个包括在列GROUP BY子句.该SELECT子句用于消除"太短"的运行.