符合某些条件的行的MYSQL SELECT AVG()

时间:2013-02-01 20:13:48

标签: mysql excel average

在我的数据库中有数十万行。我正试图计算股票或外汇的RSI。

RSI(14)的公式为:

100 - (100 / 1 + RS )
  

RS是平均增益/平均损失。

该公式回顾过去的14行(包括当前行),并平均有增益(或向上移动)的行的值,并将该平均值除以存在损失的行的平均值。

我在MySQL中遇到的问题是让它选择前14行的平均增益和平均损失。这在Excel中很容易做到。在Excel中,我创建了一个列,分析是否存在增益或损失,然后使用AVERAGEIF(Range,Criteria,Average_Range)。没问题。但Excel无法处理我需要分析的大量行。

我考虑过这样的事情:

SELECT *, 
       id, 
       (SELECT 10000 * Avg(close - open) 
        FROM   `2011` 
        WHERE  id <= 25 
               AND id >= ( 25 - 13 ) 
               AND ( close - open ) >= 0) / (SELECT 10000 * Avg(open - close) 
                                             FROM   `2011` 
                                             WHERE  id <= 25 
                                                    AND id >= ( 25 - 13 ) 
                                                    AND ( open - close ) > 0) 
FROM   `2011` 
LIMIT  50 

限制50只是为了保持可管理性。但这不符合我的要求。它使数学部分恰到好处,但它为每一行重复相同的数字。

因此当它在第25行时,它会查看第12行到第25行。然后在第26行,它会查看第13到26行等,这将是每行的新数字。相反,上面的SELECT语句在最后一列重复相同的数字,对于每一行应该是不同的,因为它与其他所有行的范围不同。

2 个答案:

答案 0 :(得分:1)

以下是表达查询的更简单方法:

select t.*,
        100 - (100 / (1 + avg(case when close - open > 0 then close - open end)/avg(case when open - close > 0 then open - close end))
              ) as RS14
 from `2011` t join
      `2011` t2
      on t2.id between t.id - 14 and t
group by t.id

我不能发誓它会更快,但它可能会。你还应该有一个id的索引。

如果你真的需要这样的功能,你可以切换数据库吗? SQL Server 2012,Oracle和Postgres都提供了可用于此类查询的累积和功能。

我一直在考虑这个问题,你可以有效地进行查询。也许更明显的联接是更痛苦的方式:

select
from `2011` t0 join
     `2011` t1
     on t0.id = t1.id + 1 join
     `2011` t2
     on t0.id = t1.id + 2 join
     . . .
     2011` t13
     on t0.id = t13.id + 13

您需要使用不同表中的14个变量计算出您想要的表达式。但是,这将有效地使用id上的索引,并且应该很快(可能比写入更快)。

另一种方法从观察开始,你可以很容易地得到每个第14个值:

select ((t.id-1) div 14)*14,
       100 - (100 / (1 + avg(case when close - open > 0 then close - open end)/avg(case when open - close > 0 then open - close end))           
from `2011` t
group by ((t.id-1) div 14)

表达可能不完全正确。我们的想法是使用聚合将14行组合在一起。

现在,我们可以通过以下方式获取中间行:

select (((t.id-1 + offset) div 14))*14+offset,
       100 - (100 / (1 + avg(case when close - open > 0 then close - open end)/avg(case when open - close > 0 then open - close end))           
from `2011` t cross join
     (select 0 as offset union all select 1 union all . . .
      select 13
     ) offsets
group by (((t.id-1 + offset) div 14))*14+offset

这会使cross join获取中间的行。

最后两个中的任何一个都应该表现得很好。我会选择group by解决方案,因为它更容易编写,维护和修改。

答案 1 :(得分:0)

问题是MySQL不会自动执行电子表格所做的事情,即根据外部查询中的id调整子查询。

您需要一个相关的子查询,如下所示:

SELECT A.*, A.id,
        (SELECT 10000 * AVG(B.close - B.open)
         FROM `2011` B
         WHERE B.id <= A.id
               AND B.id >= ( A.id - 13 )
               AND (B.close - B.open) >= 0 / (SELECT 10000 * AVG(C.open - C.close)
                                              FROM `2011` C
                                              WHERE C.id <= A.id
                                                    AND C.id >= (A.id - 13)
                                                    AND (C.open - C.close) > 0)
 FROM `2011` A
 LIMIT 50