Wie verwende ich vorbereitete Anweisungen in SQlite in Android?

100

Wie verwende ich vorbereitete Anweisungen in SQlite in Android?

pupeno
quelle
9
Überlegen Sie, ob Sie die akzeptierte Antwort ändern möchten.
Suragch
9
vereinbart - erwägen Sie, die Antwort zu ändern ... Herdenmentalität hat die akzeptierte Antwort positiv bewertet, wenn es eine bessere Lösung gibt.
goodguys_activate
4
Pablo, bitte ändere die akzeptierte Antwort ... das gegebene Beispiel läuft nicht einmal. Und unsere Abstimmungen reichen nicht aus, um sie zu lösen.
SparK

Antworten:

21

Ich verwende die ganze Zeit vorbereitete Anweisungen in Android, es ist ganz einfach:

SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();
Jasonhudgins
quelle
81
Ich weiß nicht, wie diese Antwort so viele Stimmen haben kann. SQLIteStatement # execute sollte nicht für SQL-Abfragen verwendet werden, sondern nur für Anweisungen. Bitte überprüfen Sie developer.android.com/reference/android/database/sqlite/…
simao
9
Wie sollen Sie dann eine vorbereitete Anweisung zum Abfragen von Daten verwenden?
Juan Mendes
12
Beachten Sie, dass SQLiteStatement.bindXXX()es einen 1-basierten Index gibt, der nicht wie die meisten auf 0 basiert.
Simulant
6
@jasonhudgins Warum nicht einfach dein SELECT durch ein INSERT ersetzen? Ich bin gerade aus diesem Thread gekommen, in dem Sie einen Anfänger verwirrt haben
Keyser
35
perfektes Beispiel für die Herdenmentalität, die eine völlig falsche Antwort positiv bewertet und akzeptiert
165

Für vorbereitete SQLite-Anweisungen in Android gibt es SQLiteStatement . Mit vorbereiteten Anweisungen können Sie die Leistung beschleunigen (insbesondere bei Anweisungen, die mehrmals ausgeführt werden müssen) und Einspritzangriffe vermeiden. In diesem Artikel finden Sie eine allgemeine Diskussion zu vorbereiteten Aussagen.

SQLiteStatementsoll mit SQL-Anweisungen verwendet werden, die nicht mehrere Werte zurückgeben. (Das bedeutet, dass Sie sie für die meisten Abfragen nicht verwenden würden.) Nachfolgend einige Beispiele:

Erstellen Sie eine Tabelle

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

Die execute()Methode gibt keinen Wert zurück, daher ist die Verwendung mit CREATE und DROP angemessen, jedoch nicht die Verwendung mit SELECT, INSERT, DELETE und UPDATE, da diese Werte zurückgeben. (Aber siehe diese Frage .)

Werte einfügen

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

Beachten Sie, dass die executeInsert()Methode anstelle von verwendet wird execute(). Natürlich möchten Sie nicht immer in jeder Zeile die gleichen Dinge eingeben. Dafür können Sie Bindungen verwenden .

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();

Normalerweise verwenden Sie vorbereitete Anweisungen, wenn Sie etwas (wie ein INSERT) schnell viele Male wiederholen möchten. Die vorbereitete Anweisung sorgt dafür, dass die SQL-Anweisung nicht jedes Mal analysiert und kompiliert werden muss. Mit Transaktionen können Sie die Dinge noch schneller machen . Dadurch können alle Änderungen gleichzeitig übernommen werden. Hier ist ein Beispiel:

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();
}

Weitere Informationen zu Transaktionen und zur Beschleunigung von Datenbankeinfügungen finden Sie unter diesen Links.

Zeilen aktualisieren

Dies ist ein grundlegendes Beispiel. Sie können auch die Konzepte aus dem obigen Abschnitt anwenden.

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();

Zeilen löschen

Die executeUpdateDelete()Methode kann auch für DELETE-Anweisungen verwendet werden und wurde in API 11 eingeführt. Siehe diese Fragen und Antworten .

Hier ist ein Beispiel.

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();
}

Abfrage

Normalerweise möchten Sie beim Ausführen einer Abfrage einen Cursor mit vielen Zeilen zurückerhalten. Dafür ist es aber nicht SQLiteStatement. Sie führen damit keine Abfrage aus, es sei denn, Sie benötigen nur ein einfaches Ergebnis, z. B. die Anzahl der Zeilen in der Datenbank, mit denen Sie arbeiten könnensimpleQueryForLong()

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

Normalerweise führen Sie die query()Methode SQLiteDatabase aus , um einen Cursor zu erhalten.

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);

In dieser Antwort finden Sie weitere Informationen zu Abfragen.

Suragch
quelle
5
Nur zur Erinnerung: Die Methoden .bindString / .bindLong / ... basieren alle auf 1.
Denys Vitali
Ich habe unter der Haube von Android-Convenience-Methoden wie .query, .insert und .delete gesucht und festgestellt, dass sie SQLiteStatement unter der Haube verwenden. Wäre es nicht einfacher, nur Convenience-Methoden zu verwenden, anstatt eigene Aussagen zu erstellen?
Nicolás Carrasco
@ NicolásCarrasco, es ist eine Weile her, seit ich daran gearbeitet habe, also bin ich jetzt ein bisschen verrostet. Für Abfragen und einzelne Einfügungen, Aktualisierungen und Löschungen würde ich definitiv die praktischen Methoden verwenden. Wenn Sie jedoch Masseneinfügungen, Aktualisierungen oder Löschungen vornehmen, würde ich vorbereitete Anweisungen zusammen mit einer Transaktion in Betracht ziehen. Ich kann nicht darüber sprechen, ob SQLiteStatement unter der Haube verwendet wird und ob die Convenience-Methoden gut genug sind. Ich denke, ich würde sagen, wenn die Convenience-Methoden für Sie schnell genug sind, dann verwenden Sie sie.
Suragch
Warum verwenden Sie clearBindings ()? Sie binden alle Ihre Werte bedingungslos. Es macht für mich keinen Sinn, sie zuerst auf Null und dann auf den tatsächlichen Wert zu setzen.
Der unglaubliche
@ TheincredibleJan, ich weiß es nicht genau. Es ist möglicherweise nicht erforderlich und Sie können es versuchen, ohne die Bindungen zu löschen, um festzustellen, ob es einen Unterschied macht. Der Aufruf clearBindings()setzt sie jedoch nicht nur auf null(siehe Quellcode ). Ich betrachte es als Löschen des Zustands, so dass nichts ihn von der vorherigen Schleife beeinflusst. Vielleicht ist das aber nicht nötig. Ich würde mich über jemanden freuen, der Kommentare abgeben kann.
Suragch
22

Wenn Sie bei der Rückkehr einen Cursor wünschen, können Sie Folgendes in Betracht ziehen:

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"));

Sehen Sie sich dieses Snippet Genscripts an, falls Sie ein vollständigeres wünschen. Beachten Sie, dass dies eine parametrisierte SQL-Abfrage ist, also im Wesentlichen eine vorbereitete Anweisung.

jbaez
quelle
Kleiner Fehler im obigen Code: Es sollte "neuer String [] {strCountryCode}" anstelle von "neuer String {strCountryCode}" sein.
Pierre-Luc Simard
Sie müssen den Cursor bewegen, bevor Sie die Daten abrufen können
Chin
9

Jasonhudgins Beispiel wird nicht funktionieren. Sie können eine Abfrage nicht mit ausführen stmt.execute()und einen Wert (oder a Cursor) zurückerhalten.

Sie können nur Anweisungen vorkompilieren, die entweder überhaupt keine Zeilen zurückgeben (z. B. eine Einfügung oder eine Tabellenanweisung erstellen) oder eine einzelne Zeile und Spalte (und simpleQueryForLong()oder verwenden simpleQueryForString()).

Rotbarsch64
quelle
1

Um einen Cursor zu erhalten, können Sie kein compiledStatement verwenden. Wenn Sie jedoch eine vollständig vorbereitete SQL-Anweisung verwenden möchten, empfehle ich eine Anpassung der Methode von jbaez ... Verwenden von db.rawQuery()anstelle von db.query().

Aaron
quelle