Saltar al contenido

Android / SQLite: columnas de la tabla Insertar-Actualizar para mantener el identificador

Poseemos la mejor información que hallamos on line. Nosotros esperamos que te resulte útil y si quieres comentarnos algo que nos pueda ayudar a crecer puedes hacerlo..

Solución:

Puedo entender la noción percibida de que es mejor para el rendimiento hacer toda esta lógica en SQL, pero ¿quizás la solución más simple (menos código) es la mejor en este caso? ¿Por qué no intentar la actualización primero y luego usar insertWithOnConflict() con CONFLICT_IGNORE para hacer la inserción (si es necesario) y obtener la identificación de fila que necesita:

public Uri insert(Uri uri, ContentValues values) 
    final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    String selection = "latitude=? AND longitude=?"; 
    String[] selectionArgs = new String[] values.getAsString("latitude"),
                values.getAsString("longitude");

    //Do an update if the constraints match
    db.update(DatabaseProperties.TABLE_NAME, values, selection, null);

    //This will return the id of the newly inserted row if no conflict
    //It will also return the offending row without modifying it if in conflict
    long id = db.insertWithOnConflict(DatabaseProperties.TABLE_NAME, null, values, CONFLICT_IGNORE);        

    return ContentUris.withAppendedId(uri, id);

Una solución más simple sería verificar el valor de retorno de update() y solo haga la inserción si el recuento afectado fue cero, pero entonces habría un caso en el que no podría obtener la identificación de la fila existente sin una selección adicional. Esta forma de inserción siempre le devolverá la identificación correcta para devolverla en el Uriy no modificará la base de datos más de lo necesario.

Si desea hacer una gran cantidad de estos a la vez, puede mirar el bulkInsert() en su proveedor, donde puede ejecutar múltiples inserciones dentro de una sola transacción. En este caso, dado que no necesita devolver el id del registro actualizado, la solución “más simple” debería funcionar bien:

public int bulkInsert(Uri uri, ContentValues[] values) 
    final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    String selection = "latitude=? AND longitude=?";
    String[] selectionArgs = null;

    int rowsAdded = 0;
    long rowId;
    db.beginTransaction();
    try 
        for (ContentValues cv : values) 
            selectionArgs = new String[] cv.getAsString("latitude"),
                cv.getAsString("longitude");

            int affected = db.update(DatabaseProperties.TABLE_NAME, 
                cv, selection, selectionArgs);
            if (affected == 0) 
                rowId = db.insert(DatabaseProperties.TABLE_NAME, null, cv);
                if (rowId > 0) rowsAdded++;
            
        
        db.setTransactionSuccessful();
     catch (SQLException ex) 
        Log.w(TAG, ex);
     finally 
        db.endTransaction();
    

    return rowsAdded;

En realidad, el código de transacción es lo que acelera las cosas al minimizar el número de veces que se escribe la memoria de la base de datos en el archivo. bulkInsert() solo permite múltiples ContentValues para pasar con una sola llamada al proveedor.

Una solución es crear una vista para el locations tabla con un desencadenador INSTEAD OF en la vista, luego insértelo en la vista. Así es como se vería eso:

Vista:

CREATE VIEW locations_view AS SELECT * FROM locations;

Desencadenar:

CREATE TRIGGER update_location INSTEAD OF INSERT ON locations_view FOR EACH ROW 
  BEGIN 
    INSERT OR REPLACE INTO locations (_id, name, latitude, longitude) VALUES ( 
       COALESCE(NEW._id, 
         (SELECT _id FROM locations WHERE latitude = NEW.latitude AND longitude = NEW.longitude)),
       NEW.name, 
       NEW.latitude, 
       NEW.longitude
    );
  END;

En lugar de insertar en el locations tabla, se inserta en la locations_view vista. El gatillo se encargará de proporcionar la correcta _id valor mediante el uso de la sub-selección. Si, por alguna razón, el inserto ya contiene un _id los COALESCE lo mantendrá y anulará uno existente en la tabla.

Probablemente querrá comprobar cuánto afecta la sub-selección al rendimiento y compararlo con otros posibles cambios que podría hacer, pero le permite mantener esta lógica fuera de su código.

Probé algunas otras soluciones que involucran desencadenantes en la propia tabla en función de INSERTAR O IGNORAR, pero parece que los desencadenadores ANTES y DESPUÉS solo se activan si realmente se insertan en la tabla.

Puede encontrar útil esta respuesta, que es la base del desencadenante.

Editar: Debido a que los activadores ANTES y DESPUÉS no se activan cuando se ignora una inserción (que podría haberse actualizado en su lugar), necesitamos reescribir la inserción con un activador INSTEAD OF. Desafortunadamente, esos no funcionan con tablas, tenemos que crear una vista para usarlo.

INSERT OR REPLACE funciona como ON CONFLICT REPLACE. Eliminará la fila si la fila con la columna única ya existe y luego lo inserta. Nunca se actualiza.

Le recomendaría que se quede con su solución actual, cree una tabla con ON CONFLICT clausula, pero cada vez que inserta una fila y se produce la violación de la restricción, su nueva fila tendrá una nueva _id como fila de origen se eliminará.

O puede crear una tabla sin ON CONFLICT clausula y uso INSERT OR REPLACE, puede usar el método insertWithOnConflict () para eso, pero está disponible desde el nivel de API 8, requiere más codificación y conduce a la misma solución que la tabla con ON CONFLICT clausula.

Si aún desea mantener su fila de origen, significa que desea mantener la misma _id Tendrá que realizar dos consultas, la primera para insertar una fila, la segunda para actualizar una fila si falla la inserción (o viceversa). Para preservar la coherencia, debe ejecutar consultas en una transacción.

    db.beginTransaction();
    try 
        long rowId = db.insert(table, null, values);
        if (rowId == -1) 
            // insertion failed
            String whereClause = "latitude=? AND longitude=?"; 
            String[] whereArgs = new String[] values.getAsString("latitude"),
                    values.getAsString("longitude");
            db.update(table, values, whereClause, whereArgs);
            // now you have to get rowId so you can return correct Uri from insert()
            // method of your content provider, so another db.query() is required
        
        db.setTransactionSuccessful();
     finally 
        db.endTransaction();
    

Nos puedes asistir nuestro estudio poniendo un comentario o puntuándolo te lo agradecemos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *