今回から、プロジェクトを新しく作っていくので、複数のプロジェクトから参照しやすいように、ちょっとSQLite のヘッダーファイルとLIbやDLLの配置を変えています。
ソリューションファイルが置かれた下に、sqlite3というフォルダを作成しています。
sqlite3下の配置は以下のような感じ。
sqlite3
|- include
| |- sqlite3.h
| |- sqliteext3.h
|- lib
|- sqlite3.lib
|- sqlite3.dll
これに伴い、プロジェクトのプロパティも変更。まず追加のインクルードディレクトリには、"..\..\SQLiteTest\sqlite3\include\" を追加。
追加のライブラリディレクトリには、"..\..\SQLiteTest\sqlite3\lib\"を追加。
これは前回と変わらないけど、追加の依存ファイルに、sqlite3.libを追加。
前回のソースコードが通るか確認して、これで準備完了!
とりあえず、Createして、データを1行Insertするコードを書いてみましょう。
後で、ソースコードを汎用的に整理することを意識して書いておきます。
#include <iostream>
#include <sqlite3.h>
#define MY_TABLE_NAME "test_table"
#define MY_DB_NAME "./test.db"
const std::string SQL_CREATE_TABLE = "CREATE TABLE ";
const std::string SQL_INSERT = "INSERT INTO ";
int main()
{
// 2回目以降の実行前にファイルを削除しておく。
std::remove(MY_DB_NAME);
sqlite3 *db = nullptr;
int ret = sqlite3_open("./test.db", &db);
if (ret != SQLITE_OK)
{
std::cout << "Failed to open Database File : " << ret << std::endl;
return -1;
}
char *errorMessage = nullptr;
const std::string tableName = "test_table";
std::string sql_create = SQL_CREATE_TABLE + tableName + "(item1, item2, item3);";
ret = sqlite3_exec(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(db);
sqlite3_free(errorMessage);
return -1;
}
std::string sql_insert = SQL_INSERT + tableName + " values(\"abc\", \"def\", \"zzz\");";
ret = sqlite3_exec(db, sql_insert.c_str(), NULL, NULL, &errorMessage);
if (ret != SQLITE_OK)
{
std::cout << "Failed to insert data (" << tableName.c_str() << ") : " << ret << std::endl;
std::cout << errorMessage << std::endl;
sqlite3_close(db);
sqlite3_free(errorMessage);
return -1;
}
sqlite3_close(db);
}
ビルドして実行してみましょう。以下のように、test.dbが作成されたら成功です。ちなみに、2回目以降に、test.dbが残ったままだと、途中でエラーになってしまうので、毎回、test.dbを削除するようにしています。
こんな感じに、test.dbが作成されれば成功です。sqlite3コマンドを用いて、中身を確認してみましょう。sqlite3コマンドは、PATHを通しておくと良いかもしれないです。
とりあえずよさそうですね。面白みはそこまでありませんが。
そこで、面白そうな、でかいデータで、程よいオープンされたデータはないかなと探していましたが、以下のサイトにいい感じのでかいデータがありました。
新型コロナウイルス感染症(COVID-19)の対応について|内閣官房新型コロナウイルス等感染症対策推進室
上記、CSVファイルを取得しておいて、プロジェクトフォルダにコピーしておきます。
ちなみに、データは、以下のような感じです。ちなみに、現時点で69121行あります。とりあえず、何も考えず、CSVファイルを読み込んで、文字列で登録してみましょう。
とにかく、読み込んでSQLiteのDBに突っ込むプログラムを組んでみました。
しかし遅い!!この程度に、8分かかっています。次回は、高速化を考えます。
#include <iostream>
#include <sqlite3.h>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <stdio.h>
#include <locale>
#include <codecvt>
#include <cstdio>
#include <chrono>
#include <Windows.h>
#define MY_TABLE_NAME "test_table"
#define MY_DB_NAME "./test.db"
#define CSV_DELIMITER L','
const std::string SQL_CREATE_TABLE = "CREATE TABLE ";
const std::string SQL_INSERT = "INSERT INTO ";
class csv_data {
public:
std::vector<std::string> titleData;
std::vector<std::vector<std::string>> tableData;
public:
csv_data(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);;
}
~csv_data() {}
private:
std::vector<std::string> split(std::string &originalStr)
{
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(subStr);
topOfStr = endOfStr + 1;
} while (topOfStr < originalStr.size());
return result;
}
std::string WStringToString(std::wstring &oWString)
{
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);
}
};
int main()
{
std::chrono::system_clock::time_point start, end;
start = std::chrono::system_clock::now(); // 計始時間
std::wstring fileName(L"humanFlow_zenkoku.csv");
csv_data csvData(fileName);
end = std::chrono::system_clock::now();
double elapsed = (double)std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "Lap Time 01 = " << elapsed << std::endl;
// 2回目以降の実行前にファイルを削除しておく。
std::remove(MY_DB_NAME);
sqlite3 *db = nullptr;
int ret = sqlite3_open(MY_DB_NAME, &db);
if (ret != SQLITE_OK)
{
std::cout << "Failed to open Database File : " << ret << std::endl;
return -1;
}
char *errorMessage = nullptr;
const std::string tableName = MY_TABLE_NAME;
std::string sql_create = SQL_CREATE_TABLE + tableName + "(";
for (int i =0; i < csvData.titleData.size();i++)
{
std::string tmp = csvData.titleData[i];
sql_create += "\"" + tmp + "\"";
sql_create += ((i + 1) < (csvData.titleData.size())) ? ", " : ");";
}
std::cout << sql_create << std::endl;
ret = sqlite3_exec(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(db);
sqlite3_free(errorMessage);
return -1;
}
end = std::chrono::system_clock::now();
elapsed = (double)std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "Lap Time 02 = " << elapsed << std::endl;
for (int i = 0; i < csvData.tableData.size(); i++)
{
std::vector<std::string> tmpVec = csvData.tableData[i];
std::string sql_insert = SQL_INSERT + tableName + " values(";
for (int j = 0; j < tmpVec.size(); j++)
{
std::string tmp = tmpVec[j];
sql_insert += "\"" + tmp + "\"";
sql_insert += ((j + 1) < (tmpVec.size())) ? ", " : ");";
}
ret = sqlite3_exec(db, sql_insert.c_str(), NULL, NULL, &errorMessage);
if (ret != SQLITE_OK)
{
std::cout << "Failed to insert data (" << tableName.c_str() << ") : " << ret << std::endl;
std::cout << errorMessage << std::endl;
sqlite3_close(db);
sqlite3_free(errorMessage);
return -1;
}
}
end = std::chrono::system_clock::now();
elapsed = (double)std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "Fin Time = " << elapsed << std::endl;
sqlite3_close(db);
}