プログラミング C++ 関連の技術メモ【SQLite の 活用方法(その4)】

SQLite 技術メモ : プログラム実践

 

今回から、プロジェクトを新しく作っていくので、複数のプロジェクトから参照しやすいように、ちょっと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);
}