В настоящее время я использую следующий оператор для создания таблицы в базе данных SQLite на устройстве Android.
CREATE TABLE IF NOT EXISTS 'locations' (
'_id' INTEGER PRIMARY KEY AUTOINCREMENT, 'name' TEXT,
'latitude' REAL, 'longitude' REAL,
UNIQUE ( 'latitude', 'longitude' )
ON CONFLICT REPLACE );
Конфликт-предложение в конце приводит к тому, что строки отбрасываются, когда новые вставки выполняются с одинаковыми координатами. Документация SQLite содержит дополнительную информацию о разделе конфликта.
Вместо этого я хотел бы сохранить прежние строки и просто обновить их столбцы. Каков наиболее эффективный способ сделать это в среде Android/SQLite?
- В качестве условия конфликта в инструкции
CREATE TABLE
. - В качестве триггера
INSERT
. - В качестве условного предложения в методе
ContentProvider#insert
. - ... лучше вы можете подумать
Я бы подумал, что более эффективно обрабатывать такие конфликты в базе данных. Кроме того, мне сложно переписать метод ContentProvider#insert
для рассмотрения сценария вставки-обновления. Вот код метода INSERT
:
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long id = db.insert(DatabaseProperties.TABLE_NAME, null, values);
return ContentUris.withAppendedId(uri, id);
}
Когда данные поступают из бэкэнд, все, что я делаю, вставляет данные следующим образом.
getContentResolver.insert(CustomContract.Locations.CONTENT_URI, contentValues);
У меня проблемы с тем, как использовать альтернативный вызов ContentProvider#update
здесь. Кроме того, это не мое одобренное решение в любом случае.
Edit:
@CommonsWare: я попытался использовать ваше предложение для использования INSERT OR REPLACE
. Я придумал этот уродливый кусок кода.
private static long insertOrReplace(SQLiteDatabase db, ContentValues values, String tableName) {
final String COMMA_SPACE = ", ";
StringBuilder columnsBuilder = new StringBuilder();
StringBuilder placeholdersBuilder = new StringBuilder();
List<Object> pureValues = new ArrayList<Object>(values.size());
Iterator<Entry<String, Object>> iterator = values.valueSet().iterator();
while (iterator.hasNext()) {
Entry<String, Object> pair = iterator.next();
String column = pair.getKey();
columnsBuilder.append(column).append(COMMA_SPACE);
placeholdersBuilder.append("?").append(COMMA_SPACE);
Object value = pair.getValue();
pureValues.add(value);
}
final String columns = columnsBuilder.substring(0, columnsBuilder.length() - COMMA_SPACE.length());
final String placeholders = placeholderBuilder.substring(0, placeholdersBuilder.length() - COMMA_SPACE.length());
db.execSQL("INSERT OR REPLACE INTO " + tableName + "(" + columns + ") VALUES (" + placeholders + ")", pureValues.toArray());
// The last insert id retrieved here is not safe. Some other inserts can happen inbetween.
Cursor cursor = db.rawQuery("SELECT * from SQLITE_SEQUENCE;", null);
long lastId = INVALID_LAST_ID;
if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) {
lastId = cursor.getLong(cursor.getColumnIndex("seq"));
}
cursor.close();
return lastId;
}
Однако, когда я проверяю базу данных SQLite, равные столбцы все еще удаляются и вставляются с новыми идентификаторами. Я не понимаю, почему это происходит, и подумал, что причиной является моя конфликтная статья. Но в документации указано обратное.
Алгоритм, указанный в предложении OR для INSERT или UPDATE переопределяет любой алгоритм, указанный в CREATE TABLE. Если алгоритм отсутствует указывается в любом месте, используется алгоритм ABORT.
Другим недостатком этой попытки является то, что вы теряете значение id, которое является возвратом оператором insert. Чтобы компенсировать это, я наконец нашел возможность запросить last_insert_rowid
. Это объясняется в сообщениях dtmilano и swiz. Я, однако, не уверен, что это безопасно, поскольку между ними может произойти другая вставка.