如何在Android的SQlite中使用准备好的语句?


100

如何在Android的SQlite中使用准备好的语句?


9
考虑更改接受的答案。
Suragch 2015年

9
同意-考虑更改答案...当存在更好的解决方案时,从众心态投票赞成接受的答案。
goodguys_activate 2015年

4
Pablo,请更改接受的答案...给出的示例甚至无法运行。而且我们的不足票数还不足以解开它。
SparK '16

Answers:


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/...
思茅

9
那么您应该如何使用准备好的语句来查询数据?
Juan Mendes

12
请注意,它SQLiteStatement.bindXXX()具有从1开始的索引,而不像大多数索引那样从0开始。
模仿

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

通常,您将运行SQLiteDatabasequery()方法来获取游标。

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 '16

为什么使用clearBindings()?您绑定所有值而没有任何条件。对我来说,先将它们设置为null并将其设置为实际值没有意义。
令人难以置信的

@TheincredibleJan,我不确定。可能没有必要,您可以在不清除绑定的情况下进行尝试,以查看它是否有所作为。但是,也就是说,调用clearBindings()不仅将它们设置为null(请参见源代码)。我将其视为清除状态,因此上一循环中没有影响它的状态。不过,也许这不是必需的。我很高兴有人知道发表评论。
苏拉奇

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

如果您想要更完整的摘要,请参阅此摘要。请注意,这是一个参数化的SQL查询,因此从本质上讲,它是一个准备好的语句。


上面代码中的小错误:应该是“ new String [] {strCountryCode}”,而不是“ new String {strCountryCode}”。
Pierre-Luc Simard

您需要先移动光标才能检索数据
Chin

9

jasonhudgins示例不起作用。您不能使用查询执行stmt.execute()并获取值(或Cursor返回)。

您只能预编译完全不返回任何行的语句(例如insert或create table语句)或单个行和列(并使用simpleQueryForLong()simpleQueryForString())。


1

要获取游标,不能使用compiledStatement。但是,如果要使用完整的SQL语句,建议您改用jbaez的方法... using db.rawQuery()代替db.query()

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.