【问题标题】:Android SQLite Insert or UpdateAndroid SQLite 插入或更新
【发布时间】:2012-10-29 23:43:36
【问题描述】:

从文档中可以看出,插入或更新的语法是: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)

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

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

【问题讨论】:

  • 您可以在特定行上调用更新。例如。更新 ___ where rowId = 4.. 如果更新返回 0 .. 你知道你没有那一行,所以插入它。

标签: android android-sqlite


【解决方案1】:

我相信您问的是如何一步插入新行或更新现有行。虽然这在this answer 中讨论的单个原始 SQL 中是可能的,但我发现在 Android 中使用SQLiteDatabase.insertWithOnConflict() 使用 CONFLICT_IGNORE 进行冲突算法更容易分两步执行此操作。

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
}

此示例假定为列“_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 UPDATE 该行,保留所有其他列的内容(即结果将是 _id=1,columnA = "valueNEW",列B =“值B”)。

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

当心this bug,它会导致 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);
}

上面的示例假设您只想更新记录中的时间戳值。如果先调用insertWithOnConflict,INSERT会因为时间戳条件的不同而创建新的记录_id。

【讨论】:

  • 正如其他人提到的那样,使用 CONFLICT_REPLACE 而不是 CONFLICT_IGNORE 会更简单。
  • @aleb - 当查询未指定表中的所有列时,CONFLICT_IGNORE 和 CONFLICT_REPLACE 之间存在巨大差异。当您使用 CONFLICT_REPLACE 时,查询会将 NULL 值放入没有在 ContentValues 变量中指定的任何值的所有列中。就我而言,我只需要更新五列中的一列,而不知道其余四列的值。使用 CONFLICT_REPLACE 强制其他四列擦除值。
  • 好的,很高兴知道,但问题是关于“插入或替换”
  • 是的,答案就是处理插入或替换数据。它克服了 CONFLICT_REPLACE 的限制,它将初始值参数中排除的列的值重置为 NULL。您提供的答案会破坏数据,因此请考虑对我的答案进行投票,而不是对其进行投票。谢谢。
  • 请注意 CONFLICT_REPLACE 将删除现有行并插入新值。因此,如果您只想用新值更新一列并且不为其他列传递值,它们最终会为 null
【解决方案2】:

这是你的方法SQLiteDatabase.insertWithOnConflict()。了解它在 sqlite 上引用 this document 的含义

【讨论】:

  • 应该使用CONFLICT_REPLACE 标志调用此方法,以按照问题所述工作。
  • @NikkyD updateWithOnConflict 不会插入不存在的新行。 insertWithOnConflict 的工作方式如下:如果该行存在(冲突)更新它,如果不存在(无冲突)插入一个新行。我认为这就是问题所在。
  • 但是用什么索引来判断它是否存在呢?我在那里没有看到“where”子句
  • 如果插入破坏了某些 PRIMARY KEY 或 UNIQUE KEY 约束,则会引发冲突。如果引发冲突,则会使用新数据更新具有 UNIQUE KEY 或 PRIMARY KEY 导致冲突的行。
  • insertWithOnConflict is not correct to use with CONFLICT_REPLACE flag when you have FOREIGN KEY constraint with ON DELETE CASCADE 因为其他表中引用该表中主键的所有记录在冲突时将被删除发生。
【解决方案3】:

SQLiteDatabase.replace() 这样做,它基本上调用:

insertWithOnConflict(table, nullColumnHack, initialValues, CONFLICT_REPLACE);

可惜文档不是很清楚。

【讨论】:

    【解决方案4】:

    它的操作名称是“upsert”,我解决它的方法是识别表中使行唯一的列。

    示例: _id, name, job, hours_worked

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

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

    在您的数据库管理器类中:

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

    【讨论】:

    • 问题是关于合并插入和更新函数的函数,而不是关于如何使用它们。
    • @aleb 这对我帮助很大。
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      我遇到了同样的问题,但我意识到当我的对象已经有一个 Id 时应该更新它,而当它没有 Id 时应该插入它,所以这是我为解决问题所做的一步一步:

      1- 在您的对象 getId 中使用 Integer 或按照您认为合适的方式初始化 Id:这是我的代码

      public Integer getId() {
          return id;
      }
      

      2- 将所有内容放入 ContentValues 后,检查方法中的 Id 以进行插入或更新:

      if(myObject.getId()!= null ){
              int count = db.update(TABLE_NAME,myContentValues,ID + " = ? ",
                      new String[]{String.valueOf(myObject.getId())});
              if(count<=0){
                  //inserting content values to db
                  db.insert(TABLE_NAME, null, myContentValues);
              }
          } else {
              db.insert(TABLE_NAME, null, myContentValues);
          }
      

      这里发生的是我检查 Id 如果确实存在我更新该行但是如果更新方法返回 -1 这意味着没有具有该 Id 的行所以我插入该行,如果它没有 Id 我插入它。

      希望这会有所帮助。

      【讨论】:

        【解决方案7】:

        怎么样 replaceOrThrow(String table, String nullColumnHack, ContentValues initialValues)

        文档说... 替换数据库中的一行的便捷方法。 如果行不存在,则插入新行。

        基本上它调用 insertWithOnConflict

        【讨论】:

          【解决方案8】:

          对我来说,如果您没有“_id”(主键),那么这些方法都不起作用

          你应该先调用更新,如果受影响的行为零,然后用忽略插入它:

           String selection = MessageDetailTable.SMS_ID+" =?";
           String[] selectionArgs =  new String[] { String.valueOf(md.getSmsId())};
          
           int affectedRows = db.update(MessageDetailTable.TABLE_NAME, values, selection,selectionArgs);
          
           if(affectedRows<=0) {
               long id = db.insertWithOnConflict(MessageDetailTable.TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_IGNORE);
           }
          

          【讨论】:

            【解决方案9】:

            你可以这样做。

                    sqliteDatabase?.let {
                    // 1. use sql to do this
                    // val sql = "insert or replace into $table (key, value) values ('$key', '${obj.toString()}')"
                    
                    // 2. use update and ContentValues
                    // it.execSQL(sql)
                    // it.replace(table, "", values)
            
                    // 3. try to update if affected row count is 0, do insert
                    val updateRow = it.update(table, values, "$column_key = ?", arrayOf(key))
                    Log.d(TAG, "update row: $updateRow")
                    if (updateRow == 0) {
                        val rowId = it.insertWithOnConflict(table, "", values, SQLiteDatabase.CONFLICT_IGNORE)
                        Log.d(TAG, "insert row: $rowId")
                    }
                }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-02-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-12-29
              • 1970-01-01
              • 2017-09-01
              相关资源
              最近更新 更多