2012-07-08 52 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(); 
}