Select用関数を前回は実装していきましたが、条件も無しに、全部大量に検索されてしまいましたね。では次は、条件を付けてみましょう。とりあえず、北海道のデータだけを引っ張って来るように考えてみましょう。
まず、SQL文を考えないといけないですね。条件を指定してSELECTする場合用いるのは、WHERE句です。
まあとにかく具体例を考えていきましょう。条件文はどのようになるでしょうか?
えーと、件名を出している列名は・・・なんでしたっけ?CSVファイルを確認すると、「都道府県」ですね。なので条件文は
"都道府県" = '北海道'
というような感じになるでしょうか。日本語を扱う場合は、列名にダブルクォーテーションで囲んでいることに注意してください。
さて、WHERE句付きのSELECT文を実装します。すでに、NBSQLiteAccessor::Select()が実装されていますので、引数付きSelect文をオーバーロードしましょう。
最終的には、文字列を引数で受けて、そのWhere句を引数指定できるようにしますが、まずは本当に、これで北海道だけとれるか試してみましょう。
オーバーロードして実装したselect関数だけまずは掲載してみます。赤字の部分を見てください。
std::vector<std::vector<std::string>> NBSQLiteAccessor::Select(std::string& whereCondition) const
{
std::vector<std::vector<std::string>> tableData;
char *errorMessage = nullptr;
auto select = "SELECT * FROM " + SQL_TABLE_NAME + " WHERE \"都道府県\" = \'北海道\';";
std::cout << select << std::endl;
auto ret = sqlite3_exec(m_db, select.c_str(), callback, &tableData, &errorMessage);
if (ret != SQLITE_OK)
{
std::cout << "Failed to insert data : " << errorMessage << std::endl;
}
return tableData;
}
そして、以下が実行結果です。素晴らしい!北海道だけみごとに検索できました。
さて、続いて、ちょっと検索にかかる時間を見てみたいので、結果表示を一度コメントアウトして、時間を見てみましょう。
そして以下が結果です。select実行の区間を見てみると、14msecとなっています。まあ、遅いっていうほどではないですね。
SELECTの高速化を狙う場合は、やはりINDEXの作成が王道でしょうか?しかし、そもそもで14msecしかかかっていないので、効果はどうなるでしょう。
INDEXを作るには、対象のテーブルに対して、INDEXをはる列を指定してCREATE INDEXを実行します
というような感じです。試してみましょう。
あとで完全なソースコードは掲載するとして、以下のような感じにとりあえず、CreateIndex関数を作ってみました。
bool NBSQLiteAccessor::CreateIndeex(std::vector<std::string>& titleData) const
{
char *errorMessage = nullptr;
std::string sql_create = SQL_CREATE_INDEX + SQL_INDEX_NAME + "ON " + SQL_TABLE_NAME + "(\"都道府県\");";
std::cout << sql_create << std::endl;
int ret = sqlite3_exec(m_db, sql_create.c_str(), NULL, NULL, &errorMessage);
if (ret != SQLITE_OK)
{
std::cout << errorMessage << std::endl;
sqlite3_close(m_db);
sqlite3_free(errorMessage);
return false;
}
return true;
}
Lap Time3 ~ Lap Time 4 が、Create Indexを実行しています。
で、Lap Time 4~ Lap Time 5がSelectですが・・・。10msecうーん。早くなったのか・・・な?何度が繰り返しましたが、ほぼ10msecで完了しますので、一応ちょっとだけ、早くなっていそうです。
しかしです。CREATE INDEX の実行時間が、46msec で結果的にはこっちの方が大きく足を引っ張ります。というわけで、CREATE INDEXは諸刃の件だということは覚えておきましょう。
ちょっと気になるのが、今回はInsertが全て終わった後にCreateIndexを呼び出しましたが、Insert する前だとどうなるでしょう?InsertごとにIndexが作成されるので、同様に遅くなるはずではあります。
以下では、Lap Time2 ~ 3がCreate Index, Lap Time 3 ~ 4 がInsert処理, Lap Time 4 ~ 5 がSelectになる。Create Indexは、8msecと早くなりましたね。データが無いためです。そして、Insertが遅くなりました。最後のselectは11msecと同等です。今回程度の規模のテーブルではあまりINDEXをはっても、意味がなさそうです。
最後に、ソースコードを綺麗にしていきます。
本当はソースコードをそのままアップロードしたいところですが、無料版だとできないみたいなので、ソースコードをそのままテキストで掲載。
<ヘッダーファイル>
- NBCsvData.h
- NBSQLiteAccessor.h
<ソースファイルファイル>
- NBCsvData.cpp
- NBSQLiteAccessor.cpp
- SQLiteTest3.cpp
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <Windows.h>
class NBCsvData {
public:
std::vector<std::string> titleData;
std::vector<std::vector<std::string>> tableData;
public:
NBCsvData(const std::wstring& filename);
NBCsvData(const NBCsvData& csvData) = delete;
virtual ~NBCsvData() {}
private:
std::vector<std::string> split(std::string &originalStr) const;
std::string WStringToString(std::wstring &oWString) const;
std::string trim(const std::string& string) const;
};
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <sqlite3.h>
class NBSQLiteAccessor {
public:
NBSQLiteAccessor();
NBSQLiteAccessor(const NBSQLiteAccessor& sqlite) = delete;
virtual ~NBSQLiteAccessor();
virtual bool CreateTable(std::vector<std::string>& titleData) const;
virtual bool InsertValues(std::vector<std::vector<std::string>>& tableData, const bool TransactionOpt = false) const;
virtual bool CreateIndeex(std::vector<std::string>& titleData, bool exec = false) const;
virtual std::vector<std::vector<std::string>> Select() const;
virtual std::vector<std::vector<std::string>> Select(std::string& whereCondition) const;
private:
sqlite3 *m_db;
};
#include "NBCsvData.h"
#define CSV_DELIMITER L','
const std::string TRIM_CHAR_SET = " \t\v\r\n";
NBCsvData::NBCsvData(const std::wstring& filename)
{
setlocale(LC_CTYPE, "ja_JP.UTF-8");
FILE* fpr = nullptr;
wchar_t tmp[1024];
std::wstring lineTitle;
_wfopen_s(&fpr, filename.c_str(), L"r, ccs=UTF-8");
fgetws(tmp, 1024, fpr);
lineTitle = tmp;
std::string cvtTitleData = WStringToString(lineTitle);
std::vector<std::string> strvec = split(cvtTitleData);
for (auto title : strvec)
{
printf("TITLE = %s\n", title.c_str());
titleData.push_back(title);
}
while (fgetws(tmp, 1024, fpr) != NULL)
{
std::vector<std::string> rowData;
lineTitle = tmp;
std::string cvtStringData = WStringToString(lineTitle);
std::vector<std::string> strvec = split(cvtStringData);
for (auto title : strvec)
{
rowData.push_back(title);
}
tableData.push_back(rowData);
}
fclose(fpr);
}
std::string NBCsvData::trim(const std::string& strData) const
{
std::string result;
std::string::size_type left = strData.find_first_not_of(TRIM_CHAR_SET);
std::string::size_type right = strData.find_last_not_of(TRIM_CHAR_SET);
result = (left != std::string::npos) ? strData.substr(left, right - left + 1) : "";
return result;
}
std::vector<std::string> NBCsvData::split(std::string &originalStr) const
{
int topOfStr = 0;
int endOfStr = 0;
std::vector<std::string> result;
do {
endOfStr = (int)originalStr.find_first_of(CSV_DELIMITER, topOfStr);
if (endOfStr == std::string::npos)
{
endOfStr = (int)originalStr.size();
}
std::string subStr(originalStr, topOfStr, (endOfStr - topOfStr));
result.push_back(trim(subStr));
topOfStr = endOfStr + 1;
} while (topOfStr < originalStr.size());
return result;
}
std::string NBCsvData::WStringToString(std::wstring &oWString) const
{
setlocale(LC_ALL, "Japanese");
int bufferSize = WideCharToMultiByte(CP_ACP, 0, oWString.c_str(),
-1, (char *)NULL, 0, NULL, NULL);
std::unique_ptr<CHAR> cpMultiByte = std::make_unique<CHAR>(bufferSize);
WideCharToMultiByte(CP_ACP, 0, oWString.c_str(), -1, cpMultiByte.get(),
bufferSize, NULL, NULL);
std::string result(cpMultiByte.get(), cpMultiByte.get() + bufferSize - 1);
return(result);
}
#include "NBSQLiteAccessor.h"
#define MY_TABLE_NAME "test_table "
#define MY_DB_NAME "./test.db"
#define MY_INDEX_NAME "test_index "
const std::string SQL_CREATE_TABLE = "CREATE TABLE ";
const std::string SQL_CREATE_INDEX = "CREATE INDEX ";
const std::string SQL_INSERT = "INSERT INTO ";
const std::string SQL_WHERE = "WHERE ";
const std::string SQL_TABLE_NAME = MY_TABLE_NAME;
const std::string SQL_INDEX_NAME = MY_INDEX_NAME;
static int callback(void *tableData, int argc, char **argv, char **columnName)
{
std::vector<std::string> row;
for (int i = 0; i < argc; i++)
{
std::string tmpData = argv[i] ? argv[i] : "*****";
row.push_back(tmpData);
}
*1
{
sql_insert += ";";
int ret = sqlite3_exec(m_db, sql_insert.c_str(), NULL, NULL, &errorMessage);
if (ret != SQLITE_OK)
{
std::cout << "Failed to insert data (" << sql_insert.c_str() << ") : " << ret << std::endl;
std::cout << errorMessage << std::endl;
sqlite3_close(m_db);
sqlite3_free(errorMessage);
return false;
}
sql_insert = SQL_INSERT + SQL_TABLE_NAME + " values(";
}
else
{
sql_insert += ", (";
}
}
if (TransactionOpt)
{
sqlite3_exec(m_db, "COMMIT;", 0, NULL, &errorMessage);
}
return true;
}
bool NBSQLiteAccessor::CreateIndeex(std::vector<std::string>& titleData, bool exec) const
{
char *errorMessage = nullptr;
std::string sql_create = SQL_CREATE_INDEX + SQL_INDEX_NAME + "ON " + SQL_TABLE_NAME + "(";
for (int i = 0; i < titleData.size(); i++)
{
std::string tmp = titleData[i];
sql_create += "\"" + tmp + "\"";
sql_create += *2;
if *3
{
printf(" | ");
}
}
printf("\n");
}
}
int main()
{
int exec_number = 1;
START_TIMER();
NBCsvData csvData(CSV_FILE_NAME_L);
LAP(exec_number++, KEYWORD_CREATE_TABLE);
std::unique_ptr<NBSQLiteAccessor> accessor = std::make_unique<NBSQLiteAccessor>();
accessor->CreateTable(csvData.titleData);
LAP(exec_number++, KEYWORD_INSERT_DATA);
accessor->CreateIndeex(csvData.titleData, true);
LAP(exec_number++, KEYWORD_CREATE_INDEX);
accessor->InsertValues(csvData.tableData, true);
LAP(exec_number++, KEYWORD_SELECT);
std::string condition = "\"都道府県\" = \'北海道\';";
std::vector<std::vector<std::string>> tableData = accessor->Select(condition);
LAP(exec_number++, KEYWORD_FINISH);
PRINT_RESULT(tableData, true);
END_TIMER();
}
ちなみに、ソリューションは以下のような感じ。
*1:std::vector<std::vector<std::string>>*)tableData)->push_back(row);
return SQLITE_OK;
}
NBSQLiteAccessor::NBSQLiteAccessor()
{
// 2回目以降の実行前にファイルを削除しておく。
std::remove(MY_DB_NAME);
int ret = sqlite3_open(MY_DB_NAME, &m_db);
if (ret != SQLITE_OK)
{
std::cout << "Failed to open Database File : " << ret << std::endl;
return;
}
}
NBSQLiteAccessor::~NBSQLiteAccessor()
{
sqlite3_close(m_db);
}
bool NBSQLiteAccessor::CreateTable(std::vector<std::string>& titleData) const
{
char *errorMessage = nullptr;
const std::string tableName = MY_TABLE_NAME;
std::string sql_create = SQL_CREATE_TABLE + tableName + "(";
for (int i = 0; i < titleData.size(); i++)
{
std::string tmp = titleData[i];
sql_create += "\"" + tmp + "\"";
sql_create += ((i + 1) < (titleData.size())) ? ", " : ");";
}
int ret = sqlite3_exec(m_db, sql_create.c_str(), NULL, NULL, &errorMessage);
if (ret != SQLITE_OK)
{
std::cout << "Failed to create table (" << tableName.c_str() << ") : " << ret << std::endl;
std::cout << errorMessage << std::endl;
sqlite3_close(m_db);
sqlite3_free(errorMessage);
return false;
}
return true;
}
bool NBSQLiteAccessor::InsertValues(std::vector<std::vector<std::string>>& tableData, const bool TransactionOpt) const
{
char *errorMessage = nullptr;
if (TransactionOpt)
{
sqlite3_exec(m_db, "BEGIN TRANSACTION", 0, NULL, &errorMessage);
}
std::string sql_insert = SQL_INSERT + SQL_TABLE_NAME + " values(";
for (int i = 0; i < tableData.size(); i++)
{
std::vector<std::string> tmpVec = tableData[i];
for (int j = 0; j < tmpVec.size(); j++)
{
std::string tmp = tmpVec[j];
sql_insert += "\"" + tmp + "\"";
sql_insert += ((j + 1) < (tmpVec.size())) ? ", " : ")";
}
if ((i + 1) % 1000 == 0 || (i + 1) >= tableData.size(
*2:i + 1) < (titleData.size())) ? ", " : ");";
}
int ret = sqlite3_exec(m_db, sql_create.c_str(), NULL, NULL, &errorMessage);
if (ret != SQLITE_OK)
{
std::cout << errorMessage << std::endl;
sqlite3_close(m_db);
sqlite3_free(errorMessage);
return false;
}
return true;
}
std::vector<std::vector<std::string>> NBSQLiteAccessor::Select() const
{
std::vector<std::vector<std::string>> tableData;
char *errorMessage = nullptr;
auto select = "SELECT * FROM " + SQL_TABLE_NAME;
auto ret = sqlite3_exec(m_db, select.c_str(), callback, &tableData, &errorMessage);
if (ret != SQLITE_OK)
{
std::cout << "Failed to insert data : " << errorMessage << std::endl;
}
return tableData;
}
std::vector<std::vector<std::string>> NBSQLiteAccessor::Select(std::string& whereCondition) const
{
std::vector<std::vector<std::string>> tableData;
char *errorMessage = nullptr;
auto select = "SELECT * FROM " + SQL_TABLE_NAME + SQL_WHERE + whereCondition;
auto ret = sqlite3_exec(m_db, select.c_str(), callback, &tableData, &errorMessage);
if (ret != SQLITE_OK)
{
std::cout << "Failed to insert data : " << errorMessage << std::endl;
}
return tableData;
}
#include <chrono>
#include "NBCsvData.h"
#include "NBSQLiteAccessor.h"
std::chrono::system_clock::time_point _start, _end;
const std::wstring CSV_FILE_NAME_L = L"humanFlow_zenkoku.csv";
const std::string KEYWORD_CREATE_TABLE = "Create Table";
const std::string KEYWORD_INSERT_DATA = "Insert Data";
const std::string KEYWORD_CREATE_INDEX = "Create Index";
const std::string KEYWORD_SELECT = "Select";
const std::string KEYWORD_FINISH = "Finish";
static void START_TIMER()
{
_start = std::chrono::system_clock::now(); // 計始時間
}
static void LAP(unsigned int count, const std::string& keyword)
{
_end = std::chrono::system_clock::now();
double elapsed = (double)std::chrono::duration_cast<std::chrono::milliseconds>(_end - _start).count();
std::cout << "Lap Time [" << keyword << "]" << count << " = " << elapsed << std::endl;
}
static void END_TIMER()
{
_end = std::chrono::system_clock::now();
double elapsed = (double)std::chrono::duration_cast<std::chrono::milliseconds>(_end - _start).count();
std::cout << "Fin Time = " << elapsed << std::endl;
}
static void PRINT_RESULT(std::vector<std::vector<std::string>>& tableData, bool exec = false)
{
if (!exec) return;
for (auto rowData : tableData)
{
printf("DATA = ");
for (int i = 0; i < rowData.size(); i++)
{
auto data = rowData[i];
printf("%s", data.c_str(
*3:i + 1) < rowData.size(