在SQL中查找本地最大值和局部最小值

时间:2010-10-28 19:41:38

标签: sql-server-2005

为了找到股票价格与时间图表的最大亏损,您首先必须找到给定价格和天数的所有局部最大值(峰值)和局部最小值(谷值)。您将如何在SQL Server 2005中执行此操作?

编辑: 使用游标有一种蛮力的方式: 比较第一天的高点和第二天的高点。 如果第一天的高点高于第二天的高点,则第一天的高点是当地的Max。

有效地,我需要找到价格图表趋势改变方向的每个点。

edit2:我应该注意到要使用的数据库表包含以下列:

  

stockid int
  日期
  嗨int - 这是便士   低价 - 也是便士   因此,对于给定的日期范围,您将在该日期范围内每天看到相同的stockid。

2 个答案:

答案 0 :(得分:1)

好的,这里一步一步是我的想法:

1 - 查找所有“峰值”,这些峰值是第二天LOWER最大值的最大值:

DECLARE @HiTable (hi int, day date)

INSERT INTO @HiTable
SELECT hi, day
FROM table t1
WHERE EXISTS (
 SELECT t2.hi
 FROM Table t2
 WHERE t1.hi > t2.hi AND t1.day < t2.day and StockID = X)

2 - 找到你所有的“山谷”,它们是第二天具有更高最小值的最小值:

DECLARE @LowTable (low int, day date)

INSERT INTO @LowTable
SELECT low, day
FROM table t1
WHERE EXISTS (
 SELECT t2.low
 FROM Table t2
 WHERE t1.low < t2.low AND t1.day < t2.day and StockID = X)

3 - 将这些组合成一个按日期排序的表格,其中包含一个标识值以使我们按顺序排列

DECLARE @TableVar (low int, hi int, day date, autoid int IDENTITY)
INSERT INTO @TableVar
(SELECT low, hi, day
FROM (
 SELECT Low, NULL as 'hi', date FROM @LowTable
 UNION ALL
 SELECT NULL as 'Low', hi, date FROM @HiTable
 )
ORDER BY DATE)

4 - 删除异常值

DELETE FROM @TableVar WHERE AutoID > (SELECT MAX(AutoID) FROM @Table WHERE low IS NULL)
DELETE FROM @TableVar WHERE AutoID < (SELECT MIN(AutoID) FROM @Table WHERE hi IS NULL)

答案 1 :(得分:0)

有时未经过彻底测试 - 但如何使用CTE和ROWNUMBER()分两步完成此操作

1)确定每行的所有nextsubseqent hi 2)紧邻下一行的任何行具有小于当前行的后续高 - 然后当前行必须是本地最大值。

或类似的东西:

begin 
    DECLARE  @highTable as table (high bigint, day date)

    declare @securityid int,
    @start datetime,
    @end datetime

    set @start = '1-1-2010'
    set @end = '2-1-2010'   
    select @securityid = id from security where riccode = 'MSFT.OQ' ;

    with highsandlows_cte as (
        SELECT 
            ROW_NUMBER() over (order by day) i
            , high
            , day
            , (select top 1 day from quotes nextHi where nextHi.high > today.high and nextHi.day >= today.day and nextHi.securityId = today.securityId order by day asc) nextHighestDay

        FROM 
            quotes today
        WHERE 
            today.securityid = @securityid )

    select 
        * 
        , (Coalesce((select 1 from highsandlows_cte t2 where t1.i + 1  = t2.i and t1.nextHighestDay > t2.nextHighestDay),0))  as isHigh
    from 
        highsandlows_cte t1

    order by 
        day
end

好的以上是错误的 - 这似乎更正常:

begin 
      DECLARE  @highTable as table (high bigint, day date)

      declare @securityid int,
    @start datetime,
    @end datetime

      set @start = '1-1-2010'
      set @end = '2-1-2010'   
      select @securityid = id from security where riccode = 'MSFT.OQ' ;



      with highsandlows_cte as (
            SELECT 
                  ROW_NUMBER() over (order by day) i
                  , high
                  , day
                  , low
            FROM 
                  quote today
            WHERE 
                  today.securityid = @securityid and today.day > convert(varchar(10), @start, 111) and convert(varchar(10), @end, 111) >today.day)



 select
             cur.day
            , cur.high
            , cur.low
        , case when ((cur.high > prv.high or prv.high IS null)and(cur.high > nxt.high or nxt.high is null)) then 1 else 0 end as isLocalMax
        , case when ((cur.low < prv.low or prv.low IS null)and(cur.low < nxt.low or nxt.low is null)) then 1 else 0 end as isLocalMin
  from 
        highsandlows_cte cur left outer join highsandlows_cte nxt
                on  cur.i + 1  = nxt.i
            left outer join highsandlows_cte prv
                on  cur.i - 1  = prv.i
  order by 
        cur.day
end

虽然可以解决重复(高/低)问题...