Android SQLite插入或更新

Lui*_*ves 59 android android-sqlite

从文档中可以看出,插入或更新的语法是:INSERT OR REPLACE INTO <table> (<columns>) VALUES (<values>),我的问题是有没有合并以下的函数?

public long insert (String table, String nullColumnHack, ContentValues values) 
public int update (String table, ContentValues values, String whereClause, String[] whereArgs)
Run Code Online (Sandbox Code Playgroud)

或者必须使用准备好的SQL语句和rawQuery来完成?

在Android中插入或更新的最佳做法是什么?

the*_*ion 87

我相信您正在询问如何在一个步骤中插入新行或更新现有行.虽然在本答案中讨论的单个原始SQL中可以实现这一点,但我发现在Android中使用SQLiteDatabase.insertWithOnConflict()使用CONFLICT_IGNORE for conflictAlgorithm 在两个步骤中更容易实现.

ContentValues initialValues = new ContentValues();
initialValues.put("_id", 1); // the execution is different if _id is 2
initialValues.put("columnA", "valueNEW");

int id = (int) yourdb.insertWithOnConflict("your_table", null, initialValues, SQLiteDatabase.CONFLICT_IGNORE);
if (id == -1) {
    yourdb.update("your_table", initialValues, "_id=?", new String[] {"1"});  // number 1 is the _id here, update to variable for your code
}
Run Code Online (Sandbox Code Playgroud)

此示例假定表键为"_id"列设置,您知道记录_id,并且已经存在行#1(_id = 1,columnA ="valueA",columnB ="valueB").这是使用insertWithOnConflict与CONFLICT_REPLACE和CONFLICT_IGNORE的区别

  • CONFLICT_REPLACE会将其他列中的现有值覆盖为null(即.columnB将变为NULL,结果将为_id = 1,columnA ="valueNEW",columnB = NULL).您丢失现有数据,我不会在我的代码中使用它.
  • CONFLICT_IGNORE将跳过现有行#1的SQL INSERT,您将在下一步中SQL更新此行,保留所有其他列的内容(即结果将为_id = 1,columnA ="valueNEW",columnB =" VALUEB").

当您尝试插入尚未存在的新行#2时,代码将仅在第一个语句insertWithOnConflict中执行SQL INSERT(即,结果将是_id = 2,columnA ="valueNEW",columnB = NULL).

请注意这个导致SQLiteDatabase.CONFLICT_IGNORE在API10(可能是API11)上出现故障的错误.我在Android 2.2上测试时,查询返回0而不是-1.

如果您不知道记录键_id或者您的条件不会产生冲突,则可以将逻辑反转为UPDATE或INSERT.这将在UPDATE期间保留记录键_id或在INSERT期间创建新记录_id.

int u = yourdb.update("yourtable", values, "anotherID=?", new String[]{"x"});
if (u == 0) {
    yourdb.insertWithOnConflict("yourtable", null, values, SQLiteDatabase.CONFLICT_REPLACE);
}
Run Code Online (Sandbox Code Playgroud)

上面的示例假设您只想在记录中更新时间戳值,例如.如果首先调用insertWithOnConflict,则由于时间戳条件的不同,INSERT将创建新记录_id.

  • @aleb - 当查询未指定表中的所有列时,CONFLICT_IGNORE和CONFLICT_REPLACE之间存在巨大差异.当您使用CONFLICT_REPLACE时,查询将把NULL值放入所有没有在ContentValues变量中指定的值的列中.在我的情况下,我只需要更新五列中的一列,而不知道其余四列的值.使用CONFLICT_REPLACE强制其他四列擦除值. (4认同)
  • 请注意,CONFLICT_REPLACE将删除现有行并插入新值.因此,如果您只想使用新值更新一个列并且不为其他列传递值,则它们最终将为null (4认同)

kde*_*iry 43

这是您的方法SQLiteDatabase.insertWithOnConflict().要理解它在sqlite 上引用的这个文档

  • 应该使用`CONFLICT_REPLACE`标志调用此方法,以解决问题. (19认同)
  • 使用`FOREIGN KEY`约束时,使用`ON DELETE CASCADE`时,`insertWithOnConflict`不正确,因为当发生冲突时,将删除其他表中引用该表中主键的所有记录. (7认同)
  • @NikkyD`updateWithOnConflict`如果不存在则不会插入新行.其中`insertWithOnConflict`的工作方式如下:如果行存在(冲突),则更新它,否则(不冲突)插入新的行.我认为这就是问题所在. (4认同)
  • 但是,使用什么索引来确定它是否存在?我没有看到“ where”子句 (2认同)
  • 如果插入将破坏某些PRIMARY KEY或UNIQUE KEY约束,则会引发冲突.如果发生冲突,将使用新数据更新具有导致冲突的UNIQUE KEY或PRIMARY KEY的行. (2认同)

ale*_*leb 9

SQLiteDatabase.replace()执行此操作,它基本上调用:

insertWithOnConflict(table, nullColumnHack, initialValues, CONFLICT_REPLACE);
Run Code Online (Sandbox Code Playgroud)

太糟糕了,文档不是很清楚.


z1l*_*V3r 5

该操作的名称为“ upsert”,而我的解决方法是识别表中构成行唯一的列。

示例:_id,姓名,工作,工作时间

我们将使用的列是名称和工作。

private int getID(String name, String job){
    Cursor c = dbr.query(TABLE_NAME,new String[]{"_id"} "name =? AND job=?",new String[]{name,job},null,null,null,null);
    if (c.moveToFirst()) //if the row exist then return the id
        return c.getInt(c.getColumnIndex("_id"));
    return -1;
}
Run Code Online (Sandbox Code Playgroud)

在数据库管理器类中:

public void upsert(String name, String job){
    ContentValues values = new ContentValues();
    values.put("NAME",name);
    values.put("JOB",job);

    int id = getID(name,job);
    if(id==-1)
        db.insert(TABLE_NAME, null, values);
    else
        db.update(TABLE_NAME, values, "_id=?", new String[]{Integer.toString(id)});
}
Run Code Online (Sandbox Code Playgroud)

  • 问题是关于合并插入和更新功能的功能,而不是如何使用它们。 (2认同)

Die*_*ano 3

SQLiteDatabase.replace()可能就是您正在寻找的。我还没有尝试过,但文档说它返回新插入的行的行 ID,所以它可能有效。