TABLE_A a INNER JOIN TABLE_B b a.KEY = b.KEYをもとにしたSQLをC++で書き実際に実行させてその実行時間をみてみましょう。
■実験の環境
<追記>この記事のコメント欄の指摘(サーバーとクライアントマシンが分かれていない)および環境が変わったので再現できない関係で、新しく環境を構築してテストをやり直しました。 今回実験したテスト環境を示します。■実験するSQLとプログラムの概要
以下のSQLを実行させ、カンマをセパレーターとして標準出力へ出力させます。 CSVファイルへの出力を想定したSQL、JOINの部分は生島さんがコメント欄で指摘したSQLそのものになっています。このSQLをもとにJOIN部分をC++でやらせてみます。SELECT Price.CODE, RDATE, OPEN, CLOSE, NAME FROM Price INNER JOIN Company ON (Price.CODE = Company.CODE)
■実験1 素直にSQL側でJOINをさせたものを実行
以下のコードのとおり、実験する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);
// テーブルからデータの取得
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(最速のテーブルアクセス)が実行されていることを確認しました。
■実験2 C++側でネステッドループで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と比べて何十倍も遅いのでしょうか?
■実験3 C++側でハッシュJOINさせてみる
件のコメント欄で生島さんが難しいとおっしゃっていた、ハッシュJOINですが、実は特段、難しいものではありません。 以下のようにすっきりと実装できます。 ちなみにコード中に出てきますmapというのはバイナリサーチを行います。なので、正確にはハッシュJOINではありません。 C++でハッシュ検索を行うには、Boost等のライブラリを使う必要があります。 つまり今回のコードはある意味、最適化の余地を残しているのですが、ここではテストの再現性(環境設定)の手間を考えてmapを使います。
#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のような事実はあくまでも知識としてしておきたいところです。 追記、コメント欄の議論を踏まえて再度記事をアップしました。 追記、コメント欄の指摘(ローカルマシンで動かしている)を受けまして再度環境を作成して実験しました。 追記、まとめ記事を作成しました。