SQLite3で配列を扱う:BLOB型 vs. JSON型
C++で配列をSQLite3の1つの列に格納する方法
方法1:BLOB型として格納する
- 配列をバイナリデータに変換します。
- 変換したバイナリデータをSQLite3のBLOB型として格納します。
- データを取り出すときは、BLOB型データを配列に変換します。
例:
#include <sqlite3.h>
// 配列をバイナリデータに変換する関数
std::vector<uint8_t> to_blob(const int* array, int size) {
std::vector<uint8_t> blob(size * sizeof(int));
memcpy(blob.data(), array, size * sizeof(int));
return blob;
}
// バイナリデータを配列に変換する関数
std::vector<int> from_blob(const uint8_t* blob, int size) {
std::vector<int> array(size / sizeof(int));
memcpy(array.data(), blob, size);
return array;
}
int main() {
// データベースを開く
sqlite3* db;
sqlite3_open("database.db", &db);
// テーブルを作成する
sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS data (array BLOB)");
// 配列を準備する
int array[] = {1, 2, 3, 4, 5};
// 配列をBLOB型として格納する
sqlite3_stmt* stmt;
sqlite3_prepare(db, "INSERT INTO data (array) VALUES (?)", -1, &stmt, nullptr);
sqlite3_bind_blob(stmt, 1, to_blob(array, sizeof(array) / sizeof(int)).data(),
to_blob(array, sizeof(array) / sizeof(int)).size(),
SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
// データを取り出す
stmt = sqlite3_prepare(db, "SELECT array FROM data", -1, &stmt, nullptr);
sqlite3_step(stmt);
const uint8_t* blob = sqlite3_column_blob(stmt, 0);
int size = sqlite3_column_bytes(stmt, 0);
std::vector<int> array2 = from_blob(blob, size);
// データベースを閉じる
sqlite3_close(db);
return 0;
}
- 配列をJSONに変換します。
#include <sqlite3.h>
#include <nlohmann/json.hpp>
int main() {
// データベースを開く
sqlite3* db;
sqlite3_open("database.db", &db);
// テーブルを作成する
sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS data (array TEXT)");
// 配列を準備する
int array[] = {1, 2, 3, 4, 5};
// 配列をJSONに変換する
nlohmann::json json = array;
// JSONをTEXT型として格納する
sqlite3_stmt* stmt;
sqlite3_prepare(db, "INSERT INTO data (array) VALUES (?)", -1, &stmt, nullptr);
sqlite3_bind_text(stmt, 1, json.dump().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
// データを取り出す
stmt = sqlite3_prepare(db, "SELECT array FROM data", -1, &stmt, nullptr);
sqlite3_step(stmt);
const char* json_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
nlohmann::json json2 = nlohmann::json::parse(json_str);
// データベースを閉じる
sqlite3_close(db);
return 0;
}
**どちら
方法1:BLOB型として格納する
#include <sqlite3.h>
// 配列をバイナリデータに変換する関数
std::vector<uint8_t> to_blob(const int* array, int size) {
std::vector<uint8_t> blob(size * sizeof(int));
memcpy(blob.data(), array, size * sizeof(int));
return blob;
}
// バイナリデータを配列に変換する関数
std::vector<int> from_blob(const uint8_t* blob, int size) {
std::vector<int> array(size / sizeof(int));
memcpy(array.data(), blob, size);
return array;
}
int main() {
// データベースを開く
sqlite3* db;
sqlite3_open("database.db", &db);
// テーブルを作成する
sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS data (array BLOB)");
// 配列を準備する
int array[] = {1, 2, 3, 4, 5};
// 配列をBLOB型として格納する
sqlite3_stmt* stmt;
sqlite3_prepare(db, "INSERT INTO data (array) VALUES (?)", -1, &stmt, nullptr);
sqlite3_bind_blob(stmt, 1, to_blob(array, sizeof(array) / sizeof(int)).data(),
to_blob(array, sizeof(array) / sizeof(int)).size(),
SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
// データを取り出す
stmt = sqlite3_prepare(db, "SELECT array FROM data", -1, &stmt, nullptr);
sqlite3_step(stmt);
const uint8_t* blob = sqlite3_column_blob(stmt, 0);
int size = sqlite3_column_bytes(stmt, 0);
std::vector<int> array2 = from_blob(blob, size);
// データベースを閉じる
sqlite3_close(db);
return 0;
}
方法2:JSON型として格納する
#include <sqlite3.h>
#include <nlohmann/json.hpp>
int main() {
// データベースを開く
sqlite3* db;
sqlite3_open("database.db", &db);
// テーブルを作成する
sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS data (array TEXT)");
// 配列を準備する
int array[] = {1, 2, 3, 4, 5};
// 配列をJSONに変換する
nlohmann::json json = array;
// JSONをTEXT型として格納する
sqlite3_stmt* stmt;
sqlite3_prepare(db, "INSERT INTO data (array) VALUES (?)", -1, &stmt, nullptr);
sqlite3_bind_text(stmt, 1, json.dump().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
// データを取り出す
stmt = sqlite3_prepare(db, "SELECT array FROM data", -1, &stmt, nullptr);
sqlite3_step(stmt);
const char* json_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
nlohmann::json json2 = nlohmann::json::parse(json_str);
// データベースを閉じる
sqlite3_close(db);
return 0;
}
ポイント
- 方法1は、バイナリデータとして直接格納するため、比較的処理速度が速い。
- 方法2は、JSONとして格納するため、人間が読みやすく、編集しやすい。
- どちらの方法を使うかは、データの性質や用途によって決める。
- サンプルコードでは、int型の配列を格納していますが、他の型にも同様に適用できます。
- 配列だけでなく、その他のデータ型も同様に格納できます。
C++で配列をSQLite3の1つの列に格納する方法
方法3:シリアライズライブラリを使う
Boost.Serializationやcerealなど、C++用のシリアライズライブラリを使って、配列をバイナリデータにシリアライズし、SQLite3に格納することができます。
#include <sqlite3.h>
#include <boost/serialization/vector.hpp>
int main() {
// データベースを開く
sqlite3* db;
sqlite3_open("database.db", &db);
// テーブルを作成する
sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS data (array BLOB)");
// 配列を準備する
std::vector<int> array = {1, 2, 3, 4, 5};
// 配列をシリアライズする
std::stringstream ss;
boost::archive::binary_oarchive oa(ss);
oa << array;
// シリアライズ結果をBLOB型として格納する
sqlite3_stmt* stmt;
sqlite3_prepare(db, "INSERT INTO data (array) VALUES (?)", -1, &stmt, nullptr);
sqlite3_bind_blob(stmt, 1, ss.str().data(), ss.str().size(), SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
// データを取り出す
stmt = sqlite3_prepare(db, "SELECT array FROM data", -1, &stmt, nullptr);
sqlite3_step(stmt);
const uint8_t* blob = sqlite3_column_blob(stmt, 0);
int size = sqlite3_column_bytes(stmt, 0);
// シリアライズ結果から配列をデシリアライズする
std::stringstream ss2(std::string(reinterpret_cast<const char*>(blob), size));
boost::archive::binary_iarchive ia(ss2);
std::vector<int> array2;
ia >> array2;
// データベースを閉じる
sqlite3_close(db);
return 0;
}
方法4:カスタムデータ型を使う
SQLite3には、独自のデータ型を定義する機能があります。この機能を使って、配列を格納するためのカスタムデータ型を定義することができます。
#include <sqlite3.h>
// カスタムデータ型の登録
static int register_array_type(sqlite3* db) {
sqlite3_stmt* stmt;
sqlite3_prepare(db, "CREATE TABLE IF NOT EXISTS data (array ARRAY)", -1, &stmt, nullptr);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_create_function(db, "array_size", 1, SQLITE_INTEGER, nullptr, [](sqlite3_context* ctx, int argc, sqlite3_value** argv) {
// 配列のサイズを取得する
int size = sqlite3_array_size(argv[0]);
sqlite3_result_int(ctx, size);
}, nullptr);
sqlite3_create_function(db, "array_get", 2, SQLITE_INTEGER, nullptr, [](sqlite3_context* ctx, int argc, sqlite3_value** argv) {
// 配列の要素を取得する
int index = sqlite3_value_int(argv[1]);
int value = sqlite3_array_get_int(argv[0], index);
sqlite3_result_int(ctx, value);
}, nullptr);
return 0;
}
int main() {
// データベースを開く
sqlite3* db;
sqlite3_open("database.db", &db);
// カスタムデータ型の登録
register_array_type(db);
// 配列を準備する
int array[] = {1, 2, 3, 4, 5};
// 配列を格納する
sqlite3_stmt* stmt;
sqlite3_prepare(db, "INSERT INTO data (array) VALUES (?)", -1, &stmt, nullptr);
sqlite3_bind_array(stmt, 1, array, sizeof(array) / sizeof(int));
sqlite3_step
c++ sqlite