TABLE_A a INNER JOIN TABLE_B b a.KEY = b.KEY
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;
}
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のような事実はあくまでも知識としてしておきたいところです。
追記、コメント欄の議論を踏まえて再度記事をアップしました。
追記、コメント欄の指摘(ローカルマシンで動かしている)を受けまして再度環境を作成して実験しました。
追記、まとめ記事を作成しました。
/**********************************************************************
数値の3桁区切り
試したコンパイル環境
VC++ .NET 2003 / WINDOWS XP Professional 64 bit edition.
GCC C++ 3.3.6 / glibc 2.3.4 / Vine Linux 4.2
**********************************************************************/
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <sstream>
std::string formatNumber(int num)
{
std::vector<int> sepnum;
int number = abs(num);
int sgn = num >= 0 ? 1 : -1;
while ( number / 1000 ) {
sepnum.push_back(number % 1000);
number /= 1000;
}
std::stringstream ss;
ss << number * sgn;
for ( std::vector<int>::reverse_iterator i = sepnum.rbegin();
i < sepnum.rend(); i++ ) {
ss << "," << std::setfill('0') << std::setw(3) << *i;
}
return std::string(ss.str());
}
using namespace std;
int main(int argc, char* argv[])
{
cout << formatNumber(0) << endl;
cout << formatNumber(1) << endl;
cout << formatNumber(12) << endl;
cout << formatNumber(123) << endl;
cout << formatNumber(1234) << endl;
cout << formatNumber(12345) << endl;
cout << formatNumber(123456) << endl;
cout << formatNumber(1234567) << endl;
cout << formatNumber(12345678) << endl;
cout << formatNumber(123456789) << endl;
cout << formatNumber(1234567890) << endl;
cout << formatNumber(-1) << endl;
cout << formatNumber(-12) << endl;
cout << formatNumber(-123) << endl;
cout << formatNumber(-1234) << endl;
cout << formatNumber(-12345) << endl;
cout << formatNumber(-123456) << endl;
cout << formatNumber(-1234567) << endl;
cout << formatNumber(-12345678) << endl;
cout << formatNumber(-123456789) << endl;
cout << formatNumber(-1234567890) << endl;
}
/**********************************************************************
CSVファイルの読み込みサンプル
試したコンパイル環境
VC++ .NET 2003 / WINDOWS XP Professional 64 bit edition.
GCC C++ 3.3.6 / glibc 2.3.4 / Vine Linux 4.2
**********************************************************************/
#include <functional>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
/**********************************************************************
クオート文字の定義構造体
**********************************************************************/
struct quote{
char start; // 開始文字
char end; // 終了文字
char escape; // エスケープ文字
quote( char start_, char end_, char escape_) :
start(start_), end(end_), escape(escape_) {}
};
struct cmpStartQuote : public std::binary_function<quote,char,bool> {
bool operator()(const quote &l_s1, const char &l_s2) const {
return l_s1.start == l_s2;
}
};
/**********************************************************************
CSVの読取
**********************************************************************/
bool readCSV(
std::vector< std::string > &result, // 結果格納ベクター
std::istreambuf_iterator<char> &i, // 入力文字イテレータ
std::string &separator, // 区切り文字
std::vector<quote> "es) // クオート文字ベクター
{
bool retval = false;
result.clear();
std::string item;
char bc = '\0';
bool first = true;
std::vector<quote>::iterator ff = quotes.end();
while ( i != std::istreambuf_iterator<char>() &&
(ff != quotes.end() || *i != '\n') ) {
if ( ff == quotes.end() &&
strchr( separator.c_str(), *i) != 0 ) {
result.push_back(item);
item.clear();
first = true;
retval = true;
} else {
if ( first &&
(ff = find_if( quotes.begin(),
quotes.end(),
bind2nd( cmpStartQuote(), *i)))
!= quotes.end() ) {
first = false;
} else if ( ff != quotes.end() && ff->end == *i ) {
if ( ff->end == ff->escape ) {
bc = *i++;
if ( i == std::istreambuf_iterator<char>() )
break;
if ( *i == ff->end ) {
item.push_back(*i);
} else {
ff = quotes.end();
continue;
}
} else {
if ( bc == ff->escape ) {
item.push_back(*i);
} else {
ff = quotes.end();
}
}
} else {
item.push_back(*i);
first = false;
}
}
bc = *i++;
}
result.push_back(item);
if ( i != std::istreambuf_iterator<char>() ) {
retval = true;
i++;
}
return retval;
}
using namespace std;
int main(int argc, char* argv[])
{
basic_ifstream<char> inputFile("access_log");
istreambuf_iterator<char> sin(inputFile);
vector< string > rec;
// APACHEのアクセスログ
string separator(" ");
vector< quote > quotes;
// クオート文字(")の設定("を表示したいときは\"になる)
quotes.push_back( quote( '"', '"', '\\') );
// 時間部分のクオート文字([]で括る)
quotes.push_back( quote( '[', ']', 0 ) );
while ( readCSV( rec, sin, separator, quotes) ) {
vector< string >::iterator i;
for ( i = rec.begin(); i < rec.end(); i++ ) {
cout << i - rec.begin() << ":" << *i << endl;
}
cout << endl;
}
return 0;
}
string separator(",");
vector< quote > quotes;
quotes.push_back( quote( '"', '"', '"') );
#include <iostream>
#include <vector>
using namespace std;
int main(void)
{
vector<int> array1;
array1.reserve(10);
for ( int i = 0; i < array1.capacity(); i++ ) {
array1.push_back(i);
}
int &last_value = array1.back(); // コンテナの要素の参照を得る
cout << "last_value: " << last_value << endl;
array1.push_back(10); // ここでlast_valueは無効になる
cout << "last_value: " << last_value << endl;
return 0;
}
last_value: 9
last_value: -572662307
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
と出る。デバッグ版のライブラリをリンクしていると上記メッセージの前にダイアログが出てそのままデバッグできる。
#include <iostream>
#include <string>
#include <new>
#include <eh.h>
using namespace std;
/**********************************************************************
bad_alloc
**********************************************************************/
int test_bad_alloc()
{
int ret = 0;
try {
int *ptr = new int[511*1024*1024UL];
if ( ptr == 0 ) {
ret = 1;
}
} catch ( bad_alloc e) {
ret = 2;
} catch ( ... ) {
ret = 3;
}
return ret;
}
/**********************************************************************
bad_cast
VC 2003 ランタイム型情報を有効にしないとコンパイルできない
**********************************************************************/
class base_class {
public:
virtual void vfunc() const {}
};
class child_class: public base_class {
public:
virtual void virtualfunc() const {}
};
int test_bad_cast_ref()
{
int ret = 0;
base_class base;
base_class &ref_base = base;
try {
child_class& ref_child = dynamic_cast<child_class&>(ref_base);
ret = 1;
} catch (bad_cast) {
ret = 2;
} catch ( ... ) {
ret = 3;
}
return ret;
}
int test_bad_cast_ptr()
{
int ret = 0;
base_class base;
base_class *ref_base = &base;
try {
child_class *ref_child = dynamic_cast<child_class*>(ref_base);
ret = 1;
} catch (bad_cast) {
ret = 2;
} catch ( ... ) {
ret = 3;
}
return ret;
}
int test_bad_typeid()
{
int ret = 0;
base_class *b = 0;
try {
const type_info &t = typeid(*b);
cout << t.name();
ret = 1;
} catch ( bad_typeid ) {
ret = 2;
}
return ret;
}
/**********************************************************************
bad_exception
**********************************************************************/
void func() throw()
{
// warning C4297が出る(わざと例外仕様に反した例外を出す)。
throw "bad_exception!";
}
void convert_unexpected()
{
throw ;
}
int test_bad_exception()
{
int ret = 0;
// VC++ .NET 2003 SP1 で最適化を掛けるとconvert_unexpected関数が呼ばれない
// VC++ .NET 2008 Express Edition でも同様
unexpected_function back = set_unexpected(convert_unexpected);
try {
func();
ret = 1;
} catch ( char * ) {
ret = 2;
} catch ( bad_exception ) {
ret = 3;
} catch ( ... ) {
ret = 4;
}
set_unexpected(back);
return ret;
}
/**********************************************************************
null pointer assigment
**********************************************************************/
int test_null_pointer_assigment()
{
int ret = 0;
// VC++ .NET 2003 SP1 で最適化を掛けるとcatch(...)に行かない。
// _set_se_translatorを使って構造化例外処理からC++例外処理へ
// 例外をコンバートできる
try {
char *ptr = 0;
*ptr = '\a';
ret = 1;
} catch ( ... ) {
ret = 3;
}
return ret;
}
int main(int argc, char* argv[])
{
cout << "test_bad_alloc:" << test_bad_alloc() << endl;
cout << "test_bad_cast_ref:" << test_bad_cast_ref() << endl;
cout << "test_bad_cast_ptr:" << test_bad_cast_ptr() << endl;
cout << "test_bad_typeid:" << test_bad_typeid() << endl;
cout << "test_bad_exception:" << test_bad_exception() << endl;
cout << "test_null_pointer_assigment:" << test_null_pointer_assigment() << endl;
return 0;
}
C:\>cl /GR /GX cpp_exception.cpp
Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 13.10.6030 for 80x86
Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.
cpp_exception.cpp
cpp_exception.cpp(98) : warning C4297: 'func' : 例外をスローしないはずだがそれを
する関数。
__declspec(nothrow) または throw() が関数で指定されました。
Microsoft (R) Incremental Linker Version 7.10.6030
Copyright (C) Microsoft Corporation. All rights reserved.
/out:cpp_exception.exe
cpp_exception.obj
C:\>cpp_exception
test_bad_alloc:2
test_bad_cast_ref:2
test_bad_cast_ptr:1
test_bad_typeid:2
test_bad_exception:2
test_null_pointer_assigment:3