🌊

C++BuilderでSQLITEを使ってみた

2023/08/11に公開

SQLITEとは

組込DBなどとも呼ばれる非常に軽量なDBシステム。SQLITE自体はDLL1個をロードするだけで利用できる。パブリックドメインであり、ソフトウェア本体の著作権が放棄されており実のところ様々なソフトで利用されている。C++Builder、ひいてはDelphiの環境にも標準で搭載されている。
DBエンジンをユーザアプリケーションの中に組込むことで、OSに側にDBアプリケーションをインストールしなくてもDBを利用できる利点が生まれる。

Firebridとの比較

SQLITEは1ファイルが1データベースを構成しており、この部分では筆者が良く利用するFirebridと設計思想が似ている。データベースとしての仕組みはFirebirdの方がより厳密であるが、SQLITEはインストール不要、インメモリDBとして利用できる等、小規模システムやキャッシュなどの用途も存在し、エンジニアに柔軟な選択肢を与えてくれる。
FirebirdもEmbedded Serverという仕組みで組込DBとしても利用できるが、DLL1個で完結するSQLITEに比べるとファイル数も多く少々扱いづらい。またインメモリDBとしては利用できない。
ただしSQLITEは同時書き込みができないため、複数コネクションから一括でのINSERTで高速処理を行うようなことはできない。しかし最近、できる構文がふえたとかなんとか。。
以下は簡単な比較表である。

SQLITE Firebird
組込DB利用
必要なファイル DLL1個 複数個
同時読込
同時書込 ×
インメモリDB利用 ×

使い方

大変楽に使用できる。特にFireDACが勝手にCREATE DATABASEしてくれるため、エンジニアはファイル名を決めるだけでいい。
以下に簡単にデータベースの作成方法、テーブル定義、INSERT, SELECTの使い方を記載する。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	TFDConnection *connection = new TFDConnection(NULL);
	connection->DriverName = "SQLite";
	connection->Params->Database = GetCurrentDir() + "\\MyDb.db";	// データベースファイルの場所を指定(バックグランドでCREATE DATABASEが動く)

	TFDQuery *query = new TFDQuery(NULL);
	query->Connection = connection;

	// テーブル定義
	query->SQL->Clear();
	query->SQL->Text = "CREATE TABLE MyTable(id integer, name text, PRIMARY KEY(id))";
	query->ExecSQL();

	// テストデータ投入
	query->SQL->Clear();
	query->SQL->Text = "INSERT INTO MyTable(id, name) VALUES (1,'namae');";
	query->ExecSQL();

	// クエリ
	query->SQL->Clear();
	query->SQL->Text = "SELECT * FROM MyTable;";
	query->Open();
	int count = query->RecordCount;	// count = 1になる

	delete connection;
	delete query;
}

インメモリDBとして使用する

ファイルパスを":memory:"にすることで、データベースファイルをメインメモリ上に展開できる。
キャッシュ等の一時的なデータストアとして利用することが多い。ただし、インメモリDBには1つのコネクションからしかアクセスできない。複数のコネクションからアクセスする方法は後述する。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	TFDConnection *connection = new TFDConnection(NULL);
	connection->DriverName = "SQLite";
	// connection->Params->Database = GetCurrentDir() + "\\MyDb.db";	// データベースファイルの場所を指定(バックグランドでCREATE DATABASEが動く)
	connection->Params->Database = ":memory:";    // インメモリDB指定

	TFDQuery *query = new TFDQuery(NULL);
	query->Connection = connection;

	// テーブル定義
	query->SQL->Clear();
	query->SQL->Text = "CREATE TABLE MyTable(id integer, name text, PRIMARY KEY(id))";
	query->ExecSQL();

	// テストデータ投入
	query->SQL->Clear();
	query->SQL->Text = "INSERT INTO MyTable(id, name) VALUES (1,'namae');";
	query->ExecSQL();

	// クエリ
	query->SQL->Clear();
	query->SQL->Text = "SELECT * FROM MyTable;";
	query->Open();
	int count = query->RecordCount;	// count = 1になる

	delete connection;
	delete query;
}

インメモリDBに複数のコネクションから接続する

ファイルパスを"file::memory:"にすることで、インメモリDBに対して複数のコネクションからアクセスできる。以下では、もう2つのコネクションから同じデータにアクセスできることを試している。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	TFDConnection *connection = new TFDConnection(NULL);
	connection->DriverName = "SQLite";
	//connection->Params->Database = GetCurrentDir() + "\\MyDb.db";	// データベースファイルの場所を指定(バックグランドでCREATE DATABASEが動く)
	connection->Params->Database = "file::memory:";

	TFDQuery *query = new TFDQuery(NULL);
	query->Connection = connection;

	// テーブル定義
	query->SQL->Clear();
	query->SQL->Text = "CREATE TABLE MyTable(id integer, name text, PRIMARY KEY(id))";
	query->ExecSQL();

	// テストデータ投入
	query->SQL->Clear();
	query->SQL->Text = "INSERT INTO MyTable(id, name) VALUES (1,'namae');";
	query->ExecSQL();

	// クエリ
	query->SQL->Clear();
	query->SQL->Text = "SELECT * FROM MyTable;";
	query->Open();
	int count = query->RecordCount;	// count = 1になる

	// データベース名を以下にすることで、複数のコネクションからインメモリDBにアクセスできる
	TFDConnection *connection2 = new TFDConnection(NULL);
	connection2->DriverName = "SQLite";
	//connection->Params->Database = GetCurrentDir() + "\\MyDb.db";	// データベースファイルの場所を指定(バックグランドでCREATE DATABASEが動く)
	connection2->Params->Database = "file::memory:";
	TFDQuery *query2 = new TFDQuery(NULL);
	query2->Connection = connection2;
	query2->SQL->Clear();
	query2->SQL->Text = "SELECT * FROM MyTable;";
	query2->Open();
	int count2 = query2->RecordCount;	// count = 1になる

	delete connection;
	delete query;
	delete connection2;
	delete query2;
}

Discussion