1. ホーム
  2. データベース
  3. エスキューエルライト

SQLiteチュートリアル(II)。C/C++インターフェイスの紹介

2022-01-23 14:17:53

I. 概要

    SQLiteが提供するC/C++インターフェースのうち、5つのAPIがコアインターフェースとなっています。このブログでは、それらの使い方と、database_connection や prepared_statement など、それらが関係する SQLite のコアオブジェクトに焦点をあてて説明します。SQLiteが提供するインターフェイスは、OCIやMySQL APIなどの他のデータベースエンジンAPIと比較して、非常に理解しやすく、使いこなすのが容易です。
II. コア・オブジェクトとインターフェース

    1. コア・オブジェクト
    database_connection オブジェクトは sqlite3_open() インターフェース関数によって生成され返されます。アプリケーションが他の SQLite インターフェース関数を使用して database_connection オブジェクトを取得する前に、この関数が呼び出されなければなりません。この関数は、その後の他の API の呼び出しが仕事をするための入力パラメータとして必要とされます。prepare_statement については、単純にコンパイルされた SQL 文と考えることができます。したがって、SQL 文の実行に関連するすべての関数は、指定された SQL 操作を完了するために、入力パラメータとしてこのオブジェクトも必要とします。
    2. コア・インターフェース
    1). sqlite3_open
    この関数は前述したように、SQLiteデータベースを操作するための入り口となる関数です。この関数が返す database_connection オブジェクトは、他の多くの SQLite API のハンドル引数です。この関数では、既存のデータベースファイルを開くことも、新しいファイルを作成することもできることに注意してください。この関数が返す database_connection オブジェクトは,複数のスレッド間でオブジェクトのポインタを共有し,任意のデータベース関連操作を実行することができます.しかし、マルチスレッド環境では、各スレッドに個別の database_connection オブジェクトを作成することをより推奨します。SQLite に付属する ATTACH コマンドを使えば、1 つの接続で複数のデータベースに簡単にアクセスできるため、複数のデータベースにアクセスするために複数のデータベース接続オブジェクトを作成する必要はありません。
    2). sqlite3_prepare
    この関数は、SQLテキストをprepared_statementオブジェクトに変換し、関数実行後にそのオブジェクトへのポインタを返します。実際,この関数はSQL文を指定するためのパラメータを評価するのではなく,単にSQL文を実行される状態に初期化するだけです.最後に、新しいアプリケーションでは、この関数の代わりにsqlite3_prepare_v2インタフェース関数を使用して同じ作業を行うことができることに注意すべきです。
    3). sqlite3_step
    この関数はsqlite3_prepare関数から返されたprepared_statementオブジェクトを評価するために使用されます。この関数が実行された後、prepared_statementオブジェクトの内部ポインタは、それが返す結果セットの最初の行を指すようになります。それ以降の行を反復処理したい場合、全ての行を走査するまでこの関数を何度も呼び出す必要があります。しかし、INSERT、UPDATE、DELETEなどのDML文では、この関数は一度だけ実行されます。
    4). sqlite3_column
    この関数は、現在の行の指定されたカラムのデータを取得するために使われます。しかし、厳密に言うと、この関数は SQLite のインターフェース関数には存在せず、関数を完成させるための関連インターフェース関数のセットで、それぞれが次のような異なるタイプのデータを返します。

コピーコード コードは以下の通りです。

    sqlite3_column_blob
    sqlite3_column_bytes
    sqlite3_column_bytes16
    sqlite3_column_double
    sqlite3_column_int
    sqlite3_column_int64
    sqlite3_column_text
    sqlite3_column_text16
    sqlite3_column_type
    sqlite3_column_value
    sqlite3_column_count

    ここで、sqlite3_column_count関数は、現在の結果セットのフィールドデータを取得するために使用されます。以下は、sqlite3_step関数とsqlite3_column関数を使用して結果セットの各行のデータを反復処理する疑似コードです。なお、ここではサンプルコードとしてフィールド型の判定を簡略化しています。
コピーコード コードは以下の通りです。

     int fieldCount = sqlite3_column_count(...) ;
     while (sqlite3_step(...) <> EOF) {
         for (int i = 0; i < fieldCount; ++i) {
             int v = sqlite3_column_int(... ,i);
         }
     }

    5). sqlite3_finalize
    この関数は、メモリリークの原因となるプリペアドステートメントオブジェクトを破棄するために使用されます。
    6). sqlite3_close
    この関数は、以前に開いた database_connection オブジェクトを閉じるために使用します。このとき、オブジェクトに関連付けられたすべての prepared_statements オブジェクトは、事前に破棄しておく必要があります。

iii. パラメータバインディング

    ほとんどのリレーショナルデータベースと同様に、SQLiteのSQLテキストは、SQL文が動的に解析される回数を減らし、それによってデータクエリとデータ操作の効率を向上させるために、変数バインディングをサポートします。これを行うには、SQLiteが提供する他の2つのインタフェースAPI、sqlite3_resetとsqlite3_bindを使う必要があります。以下の例を参照してください。

コピーコード コードは以下の通りです。

void test_parameter_binding() {
        //1. Insert multiple pieces of data without parameter binding.
        char strSQL[128];
        for (int i = 0; i < MAX_ROWS; ++i) {
            sprintf(strSQL,"insert into testtable values(%d)",i);
            sqlite3_prepare_v2(... , strSQL);
            sqlite3_step(prepared_stmt);
            sqlite3_finalize(prepared_stmt);
        }
        //2. Insert multiple data in case of parameter binding.
        string strSQLWithParameter = "insert into testtable values(?) ";
        sqlite3_prepare_v2(... , strSQL);
        for (int i = 0; i < MAX_ROWS; ++i) {
            sqlite3_bind(... ,i);
            sqlite3_step(prepared_stmt);
            sqlite3_reset(prepared_stmt);
        }
        sqlite3_finalize(prepared_stmt);
    }

ここでまず注目したいのは、SQL文の "insert into testtable values(?) " のクエスチョンマーク(?)はパラメータ変数のプレースホルダを示すもので、多くのリレーショナルデータベースで一貫しているルールなので、やはりデータベース移行作業には都合がいいということです。

    上記のコード例から、毎回異なるSQL文を生成するよりもパラメータバインディングの方が効率的であること、つまり1)よりも2)の方が圧倒的に効率的であることがわかりますので、以下に2つのアプローチを比較します。

    1). プログラムの表面だけ見ると、前者の方が文字列の充填、SQL文の準備、prepared_statementオブジェクトの解放など、forループ内でより多くのタスクを実行しています。
    2). SQLiteの公式文書では、sqlite3_prepare_v2はsqlite3_stepよりも実行効率が悪い傾向があると明記しています。
    3). より大量のデータを挿入する場合、後者による効率向上はまだかなりのものです。