ホーム < ゲームつくろー! < SQLite超入門編

その3 サンプルコードを動かしてみよう


 さて、前章でSQLiteのインストール作業も終り、いよいよSQLiteを用いてデータベースを操作できそうです。しかし、何をどうすれば良いのかさっぱりわかりません。そこでSQLiteのオフィシャルページに赴いた所、Documentationの「SQLite In 5 Minutes Or Less」にサンプルコードがありました。また幾つかのサイトにもサンプルが掲載されていました。ここではそれらを参考にして作った極短サンプルでSQLiteの機能を見てみようと思います。極短なので分かりやすいと思いますよ〜。



@ サンプルプログラムを動かしてみよう

 以下がこの章で検討する極端サンプルプログラムです。まずはざっと眺めてみてください:

SQLite極短サンプルコード
#pragma comment( lib, "sqlite3.lib" )

#include <tchar.h>
#include <sqlite3.h>
#include <stdio.h>

// 抽出結果が返るコールバック関数
static int callback(void *NotUsed, int argc, char **argv, char **azColName){
   int i;
   for(i=0; i<argc; i++)
      printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
   return SQLITE_OK;
}

int _tmain(int argc, _TCHAR* argv[])
{
   sqlite3 *db;
   char *zErrMsg = 0;

   // データベースファイルを新規生成
   int rc = sqlite3_open("Sample.db", &db);

   // テーブル生成SQL文
   char create_sql[] = "CREATE TABLE sample ( "
                       "               id     INTEGER PRIMARY KEY, "
                       "               worker TEXT    NOT NULL,    "
                       "               place  TEXT    NOT NULL     "
                       "             )                             ";

   // テーブルを生成
   rc = sqlite3_exec(db, create_sql, 0, 0, &zErrMsg);

   // データ追加SQL文
   char insert_sql[] = "INSERT INTO sample ( id, worker, place )"
                       "            values (%d, '%s', '%s')     ";

   // 追加SQL文を生成
   char insert_record[3][256];
   sprintf( insert_record[0], insert_sql, 0, "IKD"  , "Tokyo" );
   sprintf( insert_record[1], insert_sql, 1, "Maru" , "Kanagawa" );
   sprintf( insert_record[2], insert_sql, 2, "Nacky", "Hukuoka" );

   // データ追加
   int i;
   for( i = 0; i < 3; i++ )
      rc = sqlite3_exec(db, insert_record[i], 0, 0, &zErrMsg);

   // "sample"テーブルから"worker"を抽出して列挙
   rc = sqlite3_exec(db, "SELECT worker FROM sample", callback, 0, &zErrMsg);

   // データベースを閉じる
   sqlite3_close(db);
   return 0;
}

このソースはコンソールアプリケーションです。コピペしてビルド可能です。もしTCHARが云々というエラーが出たら、プロジェクトをUnicodeからマルチバイト文字セットに変更してください。

 短い中にぎゅぎゅっと収めるために、エラー判定は省いています。このソースコードは、まず新しいデータベースファイルを生成して、そこに"sample"というテーブルを作成し、そのテーブルに3つのデータを追加しています。そしてsampleテーブル内の"worker"というカラム(列)を列挙して画面に表示しています。非常にシンプルです。

 では、ソースのポイントとなる箇所を中心にじっくり見ていきましょう。



○ sqlite3_open関数

 メイン関数に入って一番に来るコードはsqlite3という型のポインタを作成している部分です:

sqlite3 *db;

sqlite3はSQLiteの中核となるデータベースハンドルです。これはデータベースを操作する様々な関数に渡されます。このハンドルを生成するのがsqlite3_open関数です。

int rc = sqlite3_open("Sample.db", &db);

この関数は既存のデータベースファイルをオープンします。第1引数がデータベースファイル名で、オープンに成功したらdbにそのハンドルが返ります。戻り値はSQLiteのエラーフラグで、正しくオープンできた場合はSQLITE_OKというフラグが戻されます。指定した"Sample.db"というファイルが無い場合、新しくファイルが生成されます。通常のファイルオープンと似た感覚ですね。

 データベースオープンにはもう1つメモリ上に仮想ファイルを作る方法もあります。

int rc = sqlite3_open(":memory:", &db);

こうするとデータベースがメモリ上に作成されます。これはファイルを介する事無くメモリ上だけでデータベースを構築したい場合に便利です。


○ テーブル生成SQL文

 次に来るのがテータベースの基本である「テーブル」を生成するSQL文です:

// テーブル生成SQL文
char create_sql[] = "CREATE TABLE sample ( "
                    "               id     INTEGER PRIMARY KEY, "
                    "               worker TEXT    NOT NULL,    "
                    "               place  TEXT    NOT NULL     "
                    "             )                             ";

データベースのテーブルはExcelの表をイメージするとわかりやすいと思います。SQLはデータベースに対して色々な命令をする事が出来るスクリプトのような言語です。これについてご存じない方も大丈夫。難しい部分は何もありません。まず「CREATE TABLE」で「テーブル作るよ〜」と命令します。1つのテーブルは必ずその名前を持ちます。上の場合は「sample」というテーブル名にしました。1つのテーブルは幾つかので構成されます。上では「id」「worker」「place」という列名のテーブルとなります。今後この列にデータが格納されていく事になります。各列に格納するデータには色々と属性を付ける事ができまして、例えばworler列は「TEXT(文字列)でNOT NULL(空っぽデータは無し)」という属性になっています。PRIMARY KEYというのは「主キー」と言い、固有IDの列である事を表します。今のところはこの位まで押さえておけば十分です。

 このSQL文を次のsqlite3_exec関数に渡すと、データベースにテーブルを新規作成してくれます。


○ sqlite3_exec関数でテーブル生成

 sqlite3_exec関数はテーブル作るのもそうですが、その他のデータ抽出、追加、削除などのデータベースを操作する入り口となる超重要な関数です:

rc = sqlite3_exec(db, create_sql, 0, 0, &zErrMsg);

sqlite3_exec関数は次のように定義されています:

sqlite3_exec関数定義
int sqlite3_exec(
   sqlite3* db,
   const char *sql,
   int (*callback)(void*,int,char**,char**),
  void *ptr,
   char **errmsg
)

dbは先ほど得たデータベースハンドルポインタを渡します。
sqlにはデータベースに対するSQL文字列を投げます。ここでテーブルの作成や情報の抽出を記述するわけです。
callbackは抽出結果を受ける関数ポインタを渡します。このコールバックの意味については後ほど見てみます。
ptrはコールバック関数の第1引数に渡されます。コールバック関数内で自由に使えます。普通得た情報を受けるために使いますね。
errmsgには抽出にエラーがあった時に返されるエラーメッセージを受けるポインタを渡します。

 テーブルを作る時は、データベースからデータを抽出する必要が無いため、コールバック関数は必要なく、第3引数にNULL(0)が代入されています。


○ データ追加SQL文

 この段階でまっさらなテーブルが出来ました。ここにデータを追加するため、続いてデータ追加SQL文を作っています:

char insert_sql[] = "INSERT INTO sample ( id, worker, place )"
                    "            values (%d, '%s', '%s')     ";

 追加は「INSERT INTO」という命令を使います。「〜の中に追加」という意味です。ここでは先ほど作成した"sample"テーブルにデータ(レコード)を追加します。追加する時には列の名前を指定します。上ではid、worker、placeと先に定義した列を全部指定していますが、一部の列でも構いません。ただし「NOT NULL」を指定した列は空データが許されないので必ず指定する必要があります。続いて"values"で追加するデータを指定します。上の場合はprintf関数で汎用的に使いたいので「%s」と書式指定しています。注目は「%s」と「'%s'」という2つの書き方がある点です。想像がつくかもしれませんが、「%s」は数値、「'%s'」は文字列です。ちなみに、直値で書くならこんな感じになります:

char my_insert_sql[] = "INSERT INTO sample ( id, worker, place )"
                    "            values (0, 'IKD', 'Tokyo')     ";

 追加用SQL文の雛形ができました。続いてprintf関数を使って追加SQL文を完成させています。

char insert_record[3][256];
sprintf( insert_record[0], insert_sql, 0, "IKD"  , "Tokyo" );
sprintf( insert_record[1], insert_sql, 1, "Maru" , "Kanagawa" );
sprintf( insert_record[2], insert_sql, 2, "Nacky", "Hukuoka" );

これを見れば、何をしたかったかわかっていただけると思います。


○ テーブルにデータ追加

 上で作ったSQL文3本使って、次に指定したデータをデータベースに追加します:

for( i = 0; i < 3; i++ )
   rc = sqlite3_exec(db, insert_record[i], 0, 0, &zErrMsg);

データベースの窓口であるsqlite3_exec関数をここでも用います。追加に関してもコールバック関数は必要ないのでNULL指定です。

 もしエラーがあるとzErrMsg(char*型)にエラー文字列が返され、関数はSQLITE_OK以外を返します。このエラーチェックは色々な関数で共通しています。


○ データ抽出

 続いて、データ抽出の例を示すために、作成したデータベースのテーブルから"worker"列のみを抽出することにしました。このSQL文は短いので直に書き込みました:

// "sample"テーブルから"worker"を抽出して列挙
rc = sqlite3_exec(db, "SELECT worker FROM sample", callback, 0, &zErrMsg);

 「SELECT」は指定のテーブルから列を選択します。上ではworker列を選択したい事を告げています。「どのテーブルから?」に当たるのがFROM以下で、sampleテーブルが指定されているのが分かると思います。

 上で極めて重要なのがcallbackコールバック関数です。これはSQLite自体が抽出したデータを引き渡すために呼びます。このコールバック関数は自作します。例えばサンプルプログラムでは次のように定義しています:

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
   int i;
   for(i=0; i<argc; i++){
      printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
   }
   printf("\n");
   return SQLITE_OK;
}

 NotUsedとなっている第1引数にはsqlite3_exec関数の第4引数に渡したポインタが渡されます。通常このポインタを通して呼び出し側とやり取りをするのですが、今回は何もしないのでNULLが渡されています。
argcには抽出された1レコードの列数が返ります。抽出したデータの数ではありませんので注意してください。コールバック関数は当てはまったデータレコードが見つかるたびに呼び出されます。argvには抽出されたデータレコードの文字列の配列(要素数argc)、azColNameにはそのカラム(列)名の配列が返ります。

 上の例では、抽出されたデータ列をコンソールに出力しています。データがない場合はNULLという文字を変わりに表示させています。

 コールバック関数は一つの型であるため、引数の型と戻り値が厳密に一致していないといけません。SQLiteのコールバックの型定義は以下の通りです:

int (*callback)(void*, int, char**, char**);


○ sqlite3_close関数

 データを取得し終わりデータベースを必要としなくなったらsqlite3_close関数でデータベースを閉じます。

sqlite3_close(db);

こは必ず呼ぶ必要があります。


 ということで、データベースファイルを作り、SQLでデータを追加・抽出し、それを取り出して画面に出してデータベースを閉じるというデータベース操作の一連の流れを見てきました。SQLさえ間違わなければ、案外あっさりとデータベース操作ができてしまいました。SQLiteはとても扱いやすいライブラリです(^-^)

 では早速次章でゲームを視野に入れてみたいと思います。データベースとして使えそうな例としてRPGのアイテムがありますね。剣や盾、薬草や呪付き、それらのお値段、現在までの取得数など、アイテムの属性はデータベースの勉強に最適です。次回はそんなRPGのアイテムデータベースから色々な情報を抽出する操作にチャレンジしてみましょう。