2012-07-08 81 views
2

我一直在使用一个非常简单的数组公式来计算一些数据集,但是他们变得太大,绝对破坏我的计算机性能,每当我更新计算。Excel数组公式到MySQL的等价公式

Excel表单和MySQL数据库被布局像这样:

+-Timestamp-+-value-+ 
| 1340816430| .02 | 
--------------------- 

x600,000行

这里的Excel公式:

{=AVERAGEIFS(B:B,A:A,"<"&A1+1000,A:A,">"&A1-1000)} 

即返回值的平均值,并且是Excel表格中的第三列。有没有什么合理的方法让我创建一个MySQL查询来执行类似的操作,并返回一个列中的值,如果我运行了Excel的公式,那么将会出现在第三列中的值?

+0

对不起,我不记得Excel的公式非常好:但是该公式在单元格基础上还是共同地在单元格A和单元格B之间生成平均值? – 2012-07-08 18:47:29

+0

@ Battle_707该公式是对列B中具有+/- 1000(A列)时间戳的值按行进行平均。每一行都有它自己的值,这些值在其任一侧的范围内。 – 2012-07-08 18:54:27

+0

我很抱歉,但我还是不太明白。如果您用于平均的唯一数字是B行,您如何才能做出每行平均值?如果我猜测你想要做的是从某个时间戳中选择所有具有+/- 1000值的行,并在这些行之间取平均值。在MySQL中,通过使用AVG函数和一些条件选择来完成。例如:'SELECT AGV(B)FROM t1 WHERE a''t-1000'and a''$ t + 1000'',其中$ t是时间戳。如果您正确索引此表(t1),则此查询应该非常快。 – 2012-07-08 19:06:25

回答

4

如果你很高兴使用Excel公式,你可以加快这个计算(我的系统超过3000)。假设A列包含ASCENDING ORDER和B列中的时间戳(如果尚未排序,则使用Excel排序)。
C列put = IFERROR(MATCH(A1-1000,$ A:$ A,1),1)并拷贝下来。这将计算行1000时间戳的行数减去。
在D列put = IFERROR(MATCH(A1 + 1000,$ A:$ A,1),1048576)并复制下来。这更多地计算行1000时间戳的行号。 E列出的=
put = AVERAGE(OFFSET(B1,C1-ROW(),0,D1-C1 + 1,1))并复制下来。这将计算从第一行到最后一行的子集范围的平均值。

在我的系统上,这个完整的计算器在20秒内计算出1000K行。
这种方法的缺点是它的volatile会在您进行更改时重新计算,但我认为您仍然处于手动计算模式。

+0

不错的优化!我认为其他感兴趣的读者也可以从写博客中受益(^: – 2012-07-09 22:03:59

+1

行 - 对于那些对更详细解释感兴趣的人,请参阅http://fastexcel.wordpress.com/2012/07/09/makeing-the-大多数你的xips-part2-when-40-mxips-for-averageifs-is-too-slow/ – 2012-07-09 23:26:59

+0

如果你要共享[shortlink](http://stackoverflow.com/q/11385680/88173)if你想要[Announcer](http://stackoverflow.com/badges/260/announcer)徽章 – JimmyPena 2012-07-10 12:44:31

2

MySQL代码:

select 
    a.timestamp t1, 
    avg(x.value) average_value 
from 
    mydata a inner join (
    select 
     timestamp, 
     value 
    from mydata 
    ) x 
    on x.timestamp between a.timestamp - 1000 and a.timestamp + 1000 
group by 
    a.timestamp 
order by 
    t1 
; 

我想认为,如果没有Excel的开销,这会表现得更好,但我不能保证这将是快如闪电的60万行。你一定会想索引Timestamp。另请参阅我创建的SQL Fiddle

+0

谢谢安迪。当我回到电脑时我会看看。看起来很有希望。 – 2012-07-09 02:20:14

+0

我很欣赏信心的投票,但出于诚实,我不得不说我今天试着用各种各样的行进行尝试,可悲的是,尽管编制索引,我的PC上10k行以北的某处仍然存在MySQL扼流圈。在这一点上,我会推荐@CharlesWilliams解决方案。 – 2012-07-10 00:53:25

+0

不幸的是我遇到了同样的问题。我没有深入挖掘,但我让它在m2.4xlarge RDS卷上过夜运行,并且没有完成。好吧。 – 2012-07-10 01:01:45

0

@Peter如果你愿意,你可以坚持使用Excel。只需使用http://xllarray.codeplex.com即可。你想要的公式是=AVERAGE(ARRAY.MASK((A:A>A1 + 1000)*(A:A<A1 - 1000), B:B)。我的Junky笔记本电脑上的1MM行数在1秒内计算。确保按Ctrl-Shift-将它作为数组公式输入。

如果你不希望建立的代码,你可以抓住该加载项和帮助文件从我的SkyDrive:http://sdrv.ms/JtaMIV

+0

如果你的Junky笔记本电脑在1秒内计算出100万份公式,然后我需要一份! – 2012-07-09 23:23:14

0

@Charles。啊,不。它只是一个公式。误读规范。

如果你想计算推到C++和揭露它作为一个XLL,这里是你会怎么做:

#include <algorithm> 
#include <numeric> 
#include "xll/xll.h" 

using namespace xll; 

typedef traits<XLOPER12>::xword xword; 

static AddIn12 xai_windowed_average(
    L"?xll_windowed_average", XLL_FP12 XLL_FP12 XLL_FP12 XLL_DOUBLE12, 
    L"WINDOWED.AVERAGE", L"Time, Value, Window" 
); 
_FP12* WINAPI 
xll_windowed_average(_FP12* pt, _FP12* pv, double dt) 
{ 
#pragma XLLEXPORT 
static xll::FP12 a(size(*pt), 1); 

double* bt0 = &pt->array[0]; 
double* bv0 = &pv->array[0]; 
double* bt = std::lower_bound(begin(*pt), end(*pt), *bt0 - dt); 
double* et = std::lower_bound(begin(*pt), end(*pt), *bt0 + dt); 

for (xword i = 0; i < size(*pt); ++i) { 
    a[i] = (bt == et) ? 0 : std::accumulate(bv0 + (bt - bt0), bv0 + (et - bt0), 0)/(et - bt); 

    // update the window 
    bt = std::lower_bound(bt, end(*pt), pt->array[i] - dt); 
    et = std::lower_bound(bt, end(*pt), pt->array[i] + dt); 
} 

return a.get(); 
}