目前,我正在使用以下语句在 Android 设备上的 SQLite 数据库中创建一张表。
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
。此外,这也不是我首选的解决方案。
编辑:
@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 数据库时,相同的列仍然会被删除并插入新的 id。 我不明白为什么会发生这种情况,之前以为是我的冲突子句导致的。但文档说明相反。
在 INSERT 或 UPDATE 的 OR 子句中指定的算法将覆盖在 CREATE TABLE 中指定的任何算法。 如果没有在任何位置指定算法,则使用 ABORT 算法。
此次尝试的另一个缺点是你会丢失由 insert 语句返回的 id 值。 为了补偿这一点,我最终找到了一个选项来请求last_insert_rowid
。如 dtmilano 和 swiz 的帖子所述。不过,我不确定这是否安全,因为可能会有另一次插入操作发生在它们之间。
execSQL("INSERT OR REPLACE...")
而不是insert()
,其中...
被替换为其余的SQL语句。例如:getWritableDatabase().execSQL("INSERT OR REPLACE INTO notes (position, prose) VALUES (?, ?)", args);
- CommonsWareContentValues
对象以获取列名及其值。你认为有什么办法可以避免这种情况吗? - JJD