4

パフォーマンス上の利点を得るためにプリペアド ステートメントを使用する必要があるアプリケーションを作成していますが、何百ものプリペアド ステートメントではないにしても数十ものプリペアド ステートメントを含むアプリケーションがある場合、混乱したグローバル コードにならないようにどのように管理すればよいのでしょうか。 ? コンストラクター/関数内のすべてのステートメントを、それらが使用されている場所とはまったく異なる場所で準備する必要がありますか?

クエリは実際に使用している場所にあるので sqlite_exec を使用するのは良いことですが、準備されたステートメントを使用すると、コードのまったく異なる領域にそれらを配置することになり、関数でどの変数をバインドする必要があるかについて曖昧/混乱します。実際にそれらを使用します。

具体的には、現在、プライベート変数を持つデータベースシングルトンがあります。

sqlite3_stmt *insertPacketCount;
sqlite3_stmt *insertPortContacted;
sqlite3_stmt *insertPacketSize;
sqlite3_stmt *computePacketSizeVariance;
 ...

コンストラクターが sqlite3_prepare_v2 を何十回も呼び出すと、

// Set up all our prepared queries
res = sqlite3_prepare_v2(db,
    "INSERT OR IGNORE INTO packet_counts VALUES(?1, ?2, ?3, 1);",
    -1, &insertPacketCount,  NULL);
expectReturnValueAndFail(SQLITE_OK);
 ...

そして、それらを他の場所で使用する実際の関数は、

void Database::IncrementPacketCount(std::string ip, std::string interface, std::string type, uint64_t increment)
{
    int res;

    res = sqlite3_bind_text(incrementPacketCount, 1, ip.c_str(), -1, SQLITE_STATIC);
    expectReturnValue(SQLITE_OK);
    res = sqlite3_bind_text(incrementPacketCount, 2, interface.c_str(), -1, SQLITE_STATIC);
    expectReturnValue(SQLITE_OK);
    res = sqlite3_bind_text(incrementPacketCount, 3, type.c_str(), -1, SQLITE_STATIC);
    expectReturnValue(SQLITE_OK);
    res = sqlite3_bind_int(incrementPacketCount, 4, increment);
    expectReturnValue(SQLITE_OK);

    res = sqlite3_step(incrementPacketCount);
    expectReturnValue(SQLITE_DONE);
    res = sqlite3_reset(incrementPacketCount);
    expectReturnValue(SQLITE_OK);
}

準備されたクエリを使用する関数の内部では、バインド インデックスが何であるかを把握するために準備ステートメントに戻るのが面倒です。

これがあいまいな場合は申し訳ありませんが、パフォーマンス/安全性を犠牲にすることなくこの種のものを整理するためのライブラリまたはメソッドはありますか (文字列を追加して sqlite_exec を呼び出すなど)?

4

1 に答える 1

3

クエリ文字列をクエリ実行から移動しないでください。最初に必要になったときにのみステートメントを準備するヘルパー関数を作成すると、最初にすべてのステートメントを準備する必要がなくなります。

void Database::PrepareStatementIfNeeded(sqlite3_stmt **stmt,
                                        const std::string& sql)
{
    if (*stmt == NULL) {
        res = sqlite3_prepare_v2(db, sql.c_str(), -1, stmt, NULL);
        ...
    }
}

(ただし、それらすべてを確定する必要があります。)

さらに、数値の代わりにパラメータ名を指定すると、sqlite3_bind_parameter_indexを使用して適切なインデックスを取得できます。

void BindParam(sqlite3_stmt *stmt,
               const char *name,
               const std::string& value) // overload for other types
{
    int index = sqlite3_bind_parameter_index(stmt, name);
    if (index == 0)
        error...;
    int res = sqlite3_bind_text(stmt, index, value.c_str(), -1, SQLITE_TRANSIENT);
    ...
}

void Database::IncrementPacketCount(...)
{
    PrepareStatementIfNeeded(&incrementPacketCount,
                             "INSERT OR IGNORE INTO packet_counts"
                             " VALUES(:ip, :intf, :type, 1)");
    BindParameter(incrementPacketCount, "ip",   ip);
    BindParameter(incrementPacketCount, "intf", interface);
    BindParameter(incrementPacketCount, "type", type);
    ...
}

(そして、これらのsqlite3_step/sqlite3_reset呼び出しは、何度も繰り返しているように見えます。それらのヘルパー関数も作成してください。)

于 2013-04-24T08:57:07.947 に答える