在我的数据库中有数十万行。我正试图计算股票或外汇的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语句在最后一列重复相同的数字,对于每一行应该是不同的,因为它与其他所有行的范围不同。
答案 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