带参数化删除查询的SQLite异常

时间:2012-02-23 23:28:11

标签: android sqlite

当我使用下面的代码时,我收到以下错误。

02-24 12:13:16.972: E/AndroidRuntime(11024): Caused by: android.database.sqlite.SQLiteException: near "?": syntax error: , while compiling: DELETE FROM messages WHERE ? NOT IN ( SELECT ? FROM ? WHERE ?=? ORDER BY ? DESC LIMIT ?) AND ?=?
02-24 12:13:16.972: E/AndroidRuntime(11024):    at android.database.sqlite.SQLiteCompiledSql.native_compile(Native Method)
02-24 12:13:16.972: E/AndroidRuntime(11024):    at android.database.sqlite.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:92)
02-24 12:13:16.972: E/AndroidRuntime(11024):    at android.database.sqlite.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:65)
02-24 12:13:16.972: E/AndroidRuntime(11024):    at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:83)
02-24 12:13:16.972: E/AndroidRuntime(11024):    at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:41)
02-24 12:13:16.972: E/AndroidRuntime(11024):    at android.database.sqlite.SQLiteDatabase.compileStatement(SQLiteDatabase.java:1149)
02-24 12:13:16.972: E/AndroidRuntime(11024):    at android.database.sqlite.SQLiteDatabase.delete(SQLiteDatabase.java:1623)

代码:

// query idea from
// http://stackoverflow.com/questions/6528117/keep-only-n-last-records-in-sqlite-database-sorted-by-date
// I have added the AND ?=? because I only want to delete records from a particular conversation thread, not the entire table.
String whereClause = "? NOT IN ( SELECT ? FROM ? WHERE ?=? ORDER BY ? DESC LIMIT ?) AND ?=?";

String[] whereArgs = new String[]{
        DbAdapter.COL_ROWID,
        DbAdapter.COL_ROWID,
        DBConstants.DB_TABLE_MESSAGES,
        DbAdapter.COL_CONVERSATION_ID,
        String.valueOf(conversationId),
        DbAdapter.COL_TIMESTAMP,
        String.valueOf(numMessagesToRetain),
        DbAdapter.COL_CONVERSATION_ID,
        String.valueOf(conversationId)
};

deleted = mDb.delete(DBConstants.DB_TABLE_MESSAGES, whereClause,
        whereArgs);

此外,关于whereArgs参数,Android documentation for the SQLite delete method似乎不完整。

public int delete (String table, String whereClause, String[] whereArgs)

Since: API Level 1
Convenience method for deleting rows in the database.
Parameters

table   the table to delete from
whereClause the optional WHERE clause to apply when deleting. Passing null will delete all rows.
Returns

the number of rows affected if a whereClause is passed in, 0 otherwise. To remove all rows and get a count pass "1" as the whereClause.

如果我使用以下where子句,它可以正常工作:

String whereClause = String
.format("%s NOT IN ( SELECT %s FROM %s WHERE %s=%s ORDER BY %s DESC LIMIT %s) AND %s=%s",
        DbAdapter.COL_ROWID,
        DbAdapter.COL_ROWID,
        DBConstants.DB_TABLE_MESSAGES,
        DbAdapter.COL_CONVERSATION_ID,
        String.valueOf(conversationId),
        DbAdapter.COL_TIMESTAMP,
        String.valueOf(numMessagesToRetain),
        DbAdapter.COL_CONVERSATION_ID,
        String.valueOf(conversationId));

我唯一能想到的是delete方法不支持参数化查询,但以下查询有效,因此它排除了这一点。

mDb.delete(DB_TABLE_MESSAGES, COL_ROWID + "=?",
                new String[]{ String.valueOf(rowId)});

解决方案

感谢antlersoft,解决方案是这样的:

String whereClause = String
        .format("(%s NOT IN ( SELECT %s FROM %s WHERE %s=? ORDER BY %s DESC LIMIT ?)) AND %s=?",
                DbAdapter.COL_ROWID,
                DbAdapter.COL_ROWID,
                DBConstants.DB_TABLE_MESSAGES,
                DbAdapter.COL_CONVERSATION_ID,
                DbAdapter.COL_TIMESTAMP,
                DbAdapter.COL_CONVERSATION_ID);

String[] whereArgs = new String[]{
        String.valueOf(conversationId),
        String.valueOf(numMessagesToRetain),
        String.valueOf(conversationId)
};

deleted = mDb.delete(DBConstants.DB_TABLE_MESSAGES, whereClause,
        whereArgs);

1 个答案:

答案 0 :(得分:1)

AFAIK,你试图在不允许的地方使用参数化。

您只能使用参数化来替换查询中的常量值。

您正在尝试使用它来替换标识符,这将无效。

如果您希望标识符是动态的,请在将查询传递给SQLite之前通过字符串操作替换它们;不要把它们当成?并期望SQLite理解查询..