TABLE_A a INNER JOIN TABLE_B b a.KEY = b.KEYをもとにしたSQLをC++で書き実際に実行させてその実行時間をみてみましょう。
SELECT Price.CODE, RDATE, OPEN, CLOSE, NAME FROM Price INNER JOIN Company ON (Price.CODE = Company.CODE)
#include <iostream>
#include <time.h>
#include "../kz_odbc.h"
using namespace std;
int main(void)
{
kz_odbc db("DSN=Trade",true);
kz_stmt stmt(&db);
time_t t = time(NULL);
// テーブルからデータの取得
stmt, "SELECT Price.CODE, RDATE, OPEN, CLOSE, NAME "
" FROM Price INNER JOIN Company ON (Price.CODE=Company.CODE)"
, endsql;
kz_string_array result = stmt.next();
int cnt = 0;
while ( !result.empty() ) {
cout << result[0] << "," << result[1] << ","
<< result[2] << "," << result[3] << ","
<< result[4] << "\n";
result = stmt.next();
cnt++;
}
cerr << "Execute time is " << time(0) - t << "sec." << endl;
cerr << "Record count is " << cnt << "." << endl;
return 0;
}
コードですが、Wordpressに合わせて編集してますので、変なところで改行が入っていますが御勘弁を。Execute time is 131sec. Record count is 4671568.となりました。プログラムは標準出力に出力していますが、実行に際しては標準出力をファイルにリダイレクトしています。その方が実行速度は速くなります。
#include <iostream>
#include <time.h>
#include "../kz_odbc.h"
using namespace std;
int main(void)
{
kz_odbc db("DSN=Trade",true);
kz_stmt stmt(&db);
time_t t = time(NULL);
// テーブルからデータの取得
stmt, "SELECT CODE,RDATE,OPEN,CLOSE FROM Price", endsql;
kz_string_array result = stmt.next();
int cnt = 0;
while ( !result.empty() ) {
// JOINの実行(ネステッドループ)
kz_stmt stmt2(&db);
stmt2, "SELECT NAME FROM Company WHERE CODE = ? "
, result[0].c_str(), endsql;
kz_string_array result2 = stmt2.next();
cout << result[0] << "," << result[1] << ","
<< result[2] << "," << result[3] << ","
<< result2[0] << "\n";
result = stmt.next();
cnt++;
}
cerr << "Execute time is " << time(0) - t << "sec." << endl;
cerr << "Record count is " << cnt << "." << endl;
return 0;
}
実行結果は以下のとおりです。Execute time is 1714sec. Record count is 4671568.これはものすごく遅いですね。生島さんが、
#include <iostream>
#include <time.h>
#include "../kz_odbc.h"
using namespace std;
int main(void)
{
kz_odbc db("DSN=Trade",true);
kz_stmt stmt(&db);
time_t t = time(NULL);
// マスターの取得・マップの作成
map< string, string> company;
stmt, "SELECT CODE, NAME FROM Company ", endsql;
kz_string_array result = stmt.next();
while ( !result.empty() ) {
company.insert( pair< string, string>( result[0], result[1]) );
result = stmt.next();
}
// テーブルからデータの取得
stmt, "SELECT CODE,RDATE,OPEN,CLOSE FROM Price ", endsql;
result = stmt.next();
int cnt = 0;
while ( !result.empty() ) {
cout << result[0] << "," << result[1] << ","
<< result[2] << "," << result[3] << ","
<< company[ result[0] ] << "\n";
result = stmt.next();
cnt++;
}
cerr << "Execute time is " << time(0) - t << "sec." << endl;
cerr << "Record count is " << cnt << "." << endl;
return 0;
}
結果ですが、以下のとおり、実験1のコードよりも早くなっております。Execute time is 108sec. Record count is 4671568.
実験1(SQL) | 131秒 |
実験2(C++側でネステッドループ) | 1714秒 |
実験3(C++側でハッシュ) | 108秒 |
明確に結果が出ているかと思います。こんなに単純なテストの結果からでも
「SQLをばらしてJOINをC++で行えば速くなる場合がある」
ということは理解していただけれるかと思います。
また、
『SQLはオブジェクト指向言語の数十倍の効率』
というのは、単純に
「OO言語側の最適化が不十分である可能性がある」
ということも言えるでしょう。
ただ、実験3では、高々十数%しか速くなっていません。
ということであれば、通常はやはり実験1のようなコードの方がトータル(開発効率と実行効率を考えると)としては良いと思われる。実験3のような事実はあくまでも知識としてしておきたいところです。
追記、コメント欄の議論を踏まえて再度記事をアップしました。
追記、コメント欄の指摘(ローカルマシンで動かしている)を受けまして再度環境を作成して実験しました。
追記、まとめ記事を作成しました。
2011-01-03 20:11:05
久々に昔のブックマークを漁ってたら、生島さんの@ITコラムが出てきたので辿ってきてみました。
半年も前のブログにコメントしてスミマセン…
予想した通りやっぱりWhere句が無かったw
でも、生島さんと同じ結論(実験1のようなコードの方がトータルとしては良い)になってますね。
実験3が速い理由はこんな感じでしょうか?
・Where句無しの全件出力(結合する前の時点で無駄データがほぼ取得されない)
・オプティマイザによる結合方法の判定自体が省かれる。(誤差?)
単位株数が2000株のものだけ抽出、とかになると、また話が変わってしまうわけです。
ご存知の通り、ハッシュJOIN等の機能が元々RDB側にあり、
これをCとかJavaとかの言語側で再開発するのは非効率です。(その分のドキュメントが要るため)
逆に、オプティマイザやRDBとのI/Fといったオーバーヘッドが足を引っ張ることもあります。(この記事の例)
後者がでかくなるのは、デカルト積でデータ増幅させる時のネットワーク負荷とかかな…
トータルでは後者は誤差だと思いますが、あのコラムは無駄な炎上とかするよりも
上記のトレードオフとかの議論に発展して欲しかった…