AndroidのSQliteで準備済みステートメントを使用するにはどうすればよいですか?


100

AndroidのSQliteで準備済みステートメントを使用するにはどうすればよいですか?


9
受け入れられた回答を変更することを検討してください。
Suragch 2015年

9
合意-回答を変更することを検討してください...より良い解決策が存在する場合、群れの考え方は受け入れられた回答を支持しました
goodguys_activate

4
パブロ、受け入れられた答えを変更してください...与えられた例は実行さえしません。そして、私たちの反対投票は、それを固定解除するのに十分ではありません。
SparK 2016

回答:


21

私は常にAndroidでプリペアドステートメントを使用しています。これは非常に簡単です。

SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();

81
この回答がどのようにして多くの票を獲得することができるか私にはわかりません。SQLIteStatement#executeは、SQLクエリでは使用せず、ステートメントのみで使用してください。developer.android.com/reference/android/database/sqlite/…を
simao

9
それでは、データのクエリに準備済みステートメントをどのように使用する予定ですか?
ファンメンデス

12
SQLiteStatement.bindXXX()は1ベースのインデックスを持っていることに注意してください。ほとんどのインデックスのように0ベースではありません。
Simulant 2013年

6
@jasonhudgins SELECTをINSERTに置き換えてみませんか?私はあなたが初心者を混乱させたこのスレッドから来た
キーヤー

35
完全に正しくない回答を賛成して受け入れる集団の考え方の完璧な例

165

Androidで準備されたSQLiteステートメントにはSQLiteStatementがあります。準備されたステートメントは、パフォーマンスを高速化するのに役立ち(特に、複数回実行する必要があるステートメントの場合)、インジェクション攻撃を回避するのにも役立ちます。準備されたステートメントに関する一般的な議論については、この記事を参照してください。

SQLiteStatement複数の値を返さないSQLステートメントで使用するためのものです。(つまり、ほとんどのクエリでそれらを使用しないことを意味します。)以下に例を示します。

テーブルを作成する

String sql = "CREATE TABLE table_name (column_1 INTEGER PRIMARY KEY, column_2 TEXT)";
SQLiteStatement stmt = db.compileStatement(sql);
stmt.execute();

このexecute()メソッドは値を返さないため、CREATEおよびDROPでの使用は適切ですが、これらの戻り値のため、SELECT、INSERT、DELETE、およびUPDATEでの使用は意図されていません。(しかし、この質問を参照してください。)

値を挿入する

String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();

executeInsert()メソッドではなくが使用されていることに注意してくださいexecute()。もちろん、すべての行に常に同じものを入力する必要はありません。そのためには、バインディングを使用できます

String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
SQLiteStatement statement = db.compileStatement(sql);

int intValue = 57;
String stringValue = "hello";

statement.bindLong(1, intValue); // 1-based: matches first '?' in sql string
statement.bindString(2, stringValue);  // matches second '?' in sql string

long rowId = statement.executeInsert();

通常、準備済みステートメントは、何か(INSERTなど)をすばやく繰り返したい場合に使用します。準備されたステートメントは、SQLステートメントを毎回解析してコンパイルする必要がないようにします。トランザクションを使用すると、さらにスピードを上げることができます。これにより、すべての変更を一度に適用できます。次に例を示します。

String stringValue = "hello";
try {

    db.beginTransaction();
    String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
    SQLiteStatement statement = db.compileStatement(sql);

    for (int i = 0; i < 1000; i++) {
        statement.clearBindings();
        statement.bindLong(1, i);
        statement.bindString(2, stringValue + i);
        statement.executeInsert();
    }

    db.setTransactionSuccessful(); // This commits the transaction if there were no exceptions

} catch (Exception e) {
    Log.w("Exception:", e);
} finally {
    db.endTransaction();
}

トランザクションとデータベース挿入の高速化に関するいくつかのより良い情報については、これらのリンクをチェックしてください。

行を更新する

これは基本的な例です。上記のセクションの概念を適用することもできます。

String sql = "UPDATE table_name SET column_2=? WHERE column_1=?";
SQLiteStatement statement = db.compileStatement(sql);

int id = 7;
String stringValue = "hi there";

statement.bindString(1, stringValue);
statement.bindLong(2, id);

int numberOfRowsAffected = statement.executeUpdateDelete();

行を削除する

このexecuteUpdateDelete()メソッドはDELETEステートメントにも使用でき、API 11で導入されましこのQ&Aを参照してください

例を示します。

try {

    db.beginTransaction();
    String sql = "DELETE FROM " + table_name +
            " WHERE " + column_1 + " = ?";
    SQLiteStatement statement = db.compileStatement(sql);

    for (Long id : words) {
        statement.clearBindings();
        statement.bindLong(1, id);
        statement.executeUpdateDelete();
    }

    db.setTransactionSuccessful();

} catch (SQLException e) {
    Log.w("Exception:", e);
} finally {
    db.endTransaction();
}

クエリ

通常、クエリを実行すると、多くの行を含むカーソルが返されます。しかし、それはそういうことでSQLiteStatementはありません。データベースの行数などの単純な結果だけが必要な場合を除いて、それを使用してクエリを実行することはできません。simpleQueryForLong()

String sql = "SELECT COUNT(*) FROM table_name";
SQLiteStatement statement = db.compileStatement(sql);
long result = statement.simpleQueryForLong();

通常query()SQLiteDatabaseのメソッドを実行してカーソルを取得します。

SQLiteDatabase db = dbHelper.getReadableDatabase();
String table = "table_name";
String[] columnsToReturn = { "column_1", "column_2" };
String selection = "column_1 =?";
String[] selectionArgs = { someValue }; // matched to "?" in selection
Cursor dbCursor = db.query(table, columnsToReturn, selection, selectionArgs, null, null, null);

クエリの詳細については、この回答を参照してください。


5
ただの注意です。.bindString/ .bindLong / ...メソッドはすべて1ベースです。
Denys Vitali

私は.query、.insert、.deleteなどのAndroidの便利なメソッドの内部を調べていたところ、内部でSQLiteStatementを使用していることに気付きました。独自のステートメントを作成する代わりに、便利なメソッドを使用する方が簡単ではないでしょうか?
ニコラス・カラスコ

@NicolásCarrasco、私がこれに取り組んでから久しぶりなので、今は少し錆びています。クエリと単一の挿入、更新、削除については、必ず便利なメソッドを使用します。ただし、一括挿入、更新、または削除を行う場合は、トランザクションとともに準備されたステートメントを検討します。内部でSQLiteStatementが使用されていること、および便利なメソッドが十分に優れているかどうかについては、お話しできません。便利なメソッドのパフォーマンスが十分に高ければ、それを使用すると思います。
Suragch

clearBindings()を使用する理由 条件なしですべての値をバインドします。最初にそれらをnullに設定し、実際の値に設定することは、私には意味がありません。
信じられないほどの月

@TheincredibleJan、よくわからない。それは必要ではないかもしれません、そしてそれが違いを生むかどうか確かめるためにバインディングをクリアすることなくそれを試すことができます。ただし、呼び出しclearBindings()はそれらを単に設定するだけではありませんnullソースコードを参照)。私はそれを状態をクリアするものと見なし、前のループからの影響は何もないようにします。たぶんそれは必要ないかもしれません。コメントを知っている人がいてくれたら嬉しいです。
Suragch、2018年

22

戻り時にカーソルが必要な場合は、次のようなものを検討できます。

SQLiteDatabase db = dbHelper.getWritableDatabase();

public Cursor fetchByCountryCode(String strCountryCode)
{
    /**
     * SELECT * FROM Country
     *      WHERE code = US
     */
    return cursor = db.query(true, 
        "Country",                        /**< Table name. */
        null,                             /**< All the fields that you want the 
                                                cursor to contain; null means all.*/
        "code=?",                         /**< WHERE statement without the WHERE clause. */
        new String[] { strCountryCode },    /**< Selection arguments. */
        null, null, null, null);
}

/** Fill a cursor with the results. */
Cursor c = fetchByCountryCode("US");

/** Retrieve data from the fields. */
String strCountryCode = c.getString(cursor.getColumnIndex("code"));

/** Assuming that you have a field/column with the name "country_name" */
String strCountryName = c.getString(cursor.getColumnIndex("country_name"));

より完全なものが必要な場合は、このスニペットGenscriptsを参照してください。これはパラメーター化されたSQLクエリであるため、基本的には準備されたステートメントです。


上記のコードの小さな間違い:「new String {strCountryCode}」ではなく、「new String [] {strCountryCode}」である必要があります。
Pierre-Luc Simard

データを取得する前にカーソルを移動する必要があります
Chin

9

jasonhudginsの例は機能しません。でクエリを実行しstmt.execute()て値(またはCursor)を取得することはできません。

することができますのみ(例えばインサートとして、またはtable文を作成する)すべての行を戻さないいずれかのことをプリコンパイル文または単一の行と列、(および使用simpleQueryForLong()またはsimpleQueryForString())。


1

カーソルを取得するために、compiledStatementを使用することはできません。あなたは完全な準備されたSQL文を使用したい場合は、私が使用して... jbaezの方法の適応をお勧めしますdb.rawQuery()代わりにdb.query()

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.