void replace_regex_all(
string &srcstr, const char *fstr, const char *repstr, string &dststr)
{
string tmp;
boost::regex fnd(fstr);
ostringstream t(ios::out | ios::binary);
ostream_iterator oi(t);
boost::regex_replace(
oi,
srcstr.begin(),
srcstr.end(),
fnd,
repstr,
boost::match_default | boost::format_all);
dststr = t.str();
}
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に合わせて編集してますので、変なところで改行が入っていますが御勘弁を。
若干ですが、コードの説明を、
stmt, "SELECT ・・・・
とか
result.empty()
stmt.next()
の部分が私が作成したライブラリになります。といってもODBC APIを呼び出しているだけになります。そう特異なものでもないかと思います。
実行結果ですが、
Execute time is 131sec. Record count is 4671568.となりました。プログラムは標準出力に出力していますが、実行に際しては標準出力をファイルにリダイレクトしています。その方が実行速度は速くなります。 比較元のデータが無いので何とも言えませんが、1秒間に約3万5千件のデータがCSVファイルへ落とされているのでマシンスペックを考えますとまずまずでしょう。 ちなみに、実行ブランを確認しましたが、CompanyテーブルへのアクセスのTYPEはeq_refでユニークキーによるJOIN(最速のテーブルアクセス)が実行されていることを確認しました。
#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.これはものすごく遅いですね。生島さんが、 『SQLにすると数十倍速くなる』 といっていたのは、実験1のコードと実験2のコードを比べて言っていたと思われます。 では、これ以上に速くさせる方法はないのでしょうか? 生島さんの言うとおり、OO言語はSQLと比べて何十倍も遅いのでしょうか?
#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のような事実はあくまでも知識としてしておきたいところです。 追記、コメント欄の議論を踏まえて再度記事をアップしました。 追記、コメント欄の指摘(ローカルマシンで動かしている)を受けまして再度環境を作成して実験しました。 追記、まとめ記事を作成しました。