2012-04-27 8 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의 마감 가격은 매일 증가합니다. 주식 id 3은 많이 요동하고, 주식 id 2의 가격은 마지막 날에 하락했다.

이 같은 결과를 찾고 있어요 :

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 값은 실제로 도움이되지 않습니다). (이 표에서)을 캡처 한 것이 가장 쉽습니다. - 영업일뿐 아니라 주말/공휴일을 무시하는 등의 다른 작업을 원할 경우 더 많은 작업이 필요합니다. 달력 파일이 필요할 것입니다. [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 

:

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

하나 개의 목적을 위해 사용되는 : 우리는 그래서 먼저 우리는 (날짜) 순서로 각 행 번호를, 다음/이전 행을 찾을 수있는 방법이 필요합니다 ...

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()을 가진 무언가를 얻는다면, 대신 그 대신에 더 나은 선택이 될 것입니다. 여기에는 중요한 한 가지가 있습니다. 행이 시퀀스 외 (이전 행보다 작음) 인 경우에만 일치가 발생합니다. 그렇지 않은 경우 값은 null (LAG(), you'd need to use something like 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 

을 ... 그래서 이전은 "현재"행보다 큰 경우에만 Restart 값이있다. window 함수에서 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이 아닌의 몇 가지 합법적 인 인스턴스 중 하나를 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 
관련 문제