2009-01-11 10 views
82

¿Cómo uso declaraciones preparadas en SQlite en Android?¿Cómo uso declaraciones preparadas en SQlite en Android?

+7

Considere cambiar la respuesta aceptada. – Suragch

+8

estuvo de acuerdo - considere cambiar la respuesta ... la mentalidad de rebaño subió la respuesta aceptada, cuando existe una solución mejor. – LamonteCristo

+3

Pablo, por favor, cambie la respuesta aceptada ... el ejemplo dado ni siquiera se ejecuta. Y nuestros downvotes no son suficientes para destrabarlo. – SparK

Respuesta

30

utilizo declaraciones preparadas en Android todo el tiempo, es bastante simple:

SQLiteDatabase db = dbHelper.getWritableDatabase(); 
SQLiteStatement stmt = db.compileStatement("SELECT * FROM Country WHERE code = ?"); 
stmt.bindString(1, "US"); 
stmt.execute(); 
+0

¿Es posible con declaraciones preparadas poner argumentos que no son valores? como "SELECT * FROM?"? – Pablo

+70

No sé cómo esta respuesta puede tener tantos votos. SQLIteStatement # execute no se debe usar para consultas Sql, solo declaraciones. Por favor, consulte http://developer.android.com/reference/android/database/sqlite/SQLiteStatement.html#execute() – simao

+1

Sí, ese es un mal ejemplo que di, no funcionará para consultas como declaraciones SELECT ... – jasonhudgins

9

jasonhudgins ejemplo no funcionará. No se puede ejecutar una consulta con stmt.execute() y obtener un valor (o un Cursor) de nuevo.

Solo puede precompilar sentencias que no devuelven ninguna fila (como una inserción o una declaración de tabla) o una sola fila y columna (y use simpleQueryForLong() o simpleQueryForString()).

22

Si quieres un cursor en cambio, entonces usted podría considerar algo como esto:

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

ver este fragmento Genscripts en caso de que quiera una más completa. Tenga en cuenta que esta es una consulta SQL parametrizada, por lo que, en esencia, es una declaración preparada.

+0

Pequeño error en el código anterior: debe ser "new String [] {strCountryCode}, "en lugar de" new String {strCountryCode} ". –

+0

Necesita mover el cursor antes de recuperar los datos – Chin

1

Para obtener un cursor, no se puede utilizar un compiledStatement. Sin embargo, si desea utilizar una declaración SQL completamente preparada, recomiendo una adaptación del método de jbaez ... Usando db.rawQuery() en lugar de db.query().

131

En cuanto a datos SQLite preparados en Android hay SQLiteStatement. declaraciones preparadas le ayudan a acelerar el rendimiento (especialmente para los estados que necesitan ser ejecutados varias veces) y también ayudar a evitar los ataques de inyección. Ver this article para una discusión general sobre declaraciones preparadas.

SQLiteStatement está destinado a utilizarse con sentencias SQL que no devuelven valores múltiples. (Esto significa que no los utilizaría para la mayoría de las consultas.) A continuación se presentan algunos ejemplos:

Create a table

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

El método execute() no devuelve un valor por lo que es apropiado utilizar con CREATE y DROP pero no está destinado a usarse con SELECT, INSERT, DELETE y UPDATE porque estos valores de retorno. (Pero vea this question.)

Insert values

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

Tenga en cuenta que el método executeInsert() se utiliza en lugar de execute(). Por supuesto, no querrá siempre ingresar las mismas cosas en cada fila. Para eso puedes usar bindings.

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); // These match to the two question marks in the sql string 
statement.bindString(2, stringValue); 

long rowId = statement.executeInsert(); 

Por lo general, usted utiliza declaraciones preparadas cuando desea repetir rápidamente algo (como un INSERT) muchas veces. La sentencia preparada hace que la sentencia SQL no tenga que ser analizada y compilada cada vez. Puede acelerar aún más las cosas usando transactions. Esto permite que todos los cambios se apliquen a la vez. Aquí está un ejemplo:

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

visita estos links para un poco más de buena información sobre las transacciones y la aceleración de las inserciones de bases de datos.

Update rows

Este es un ejemplo básico. También puede aplicar los conceptos de la sección anterior.

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

Nota: executeUpdateDelete() también se puede utilizar para los estados eliminar y se introdujo en la API 11. See this Q&A.

Query

Normalmente, cuando se ejecuta una consulta, que desea obtener un cursor de nuevo con una gran cantidad de filas. Sin embargo, eso no es lo que SQLiteStatement es. No ejecuta una consulta con él a menos que sólo necesita un simple resultado, al igual que el número de filas en la base de datos, lo que se puede hacer con simpleQueryForLong()

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

Por lo general, que se ejecutará el método de SQLiteDatabasequery() para obtener un cursor

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

Ver this answer para más detalles acerca de las consultas.

+1

Solo un recordatorio: los métodos .bindString/.bindLong/... están basados ​​en 1. –

+0

¡Respuesta asombrosa! gracias @Suragch –

+0

Estaba mirando debajo de los métodos de conveniencia de Android como .query, .insert y .delete y noté que usan SQLiteStatement bajo el capó. ¿No sería más fácil simplemente usar métodos de conveniencia en lugar de construir sus propias declaraciones? –

Cuestiones relacionadas