首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么insertWithOnConflict(.,CONFLICT_IGNORE)返回-1 (错误)?

为什么insertWithOnConflict(.,CONFLICT_IGNORE)返回-1 (错误)?
EN

Stack Overflow用户
提问于 2012-11-15 05:17:27
回答 3查看 13K关注 0票数 22

我有一张SQLite桌:

代码语言:javascript
复制
CREATE TABLE regions (_id INTEGER PRIMARY KEY, name TEXT, UNIQUE(name));

还有一些Android代码:

代码语言:javascript
复制
Validate.notBlank(region);
ContentValues cv = new ContentValues();
cv.put(Columns.REGION_NAME, region);
long regionId = 
    db.insertWithOnConflict("regions", null, cv, SQLiteDatabase.CONFLICT_IGNORE);
Validate.isTrue(regionId > -1,
    "INSERT ON CONFLICT IGNORE returned -1 for region name '%s'", region);

在重复行上,insertWithOnConflict()返回-1,表示一个错误,然后验证抛出:

代码语言:javascript
复制
INSERT ON CONFLICT IGNORE returned -1 for region name 'Overseas'

强调地雷( 关于冲突文件的SQLite )指出:

当发生适用的约束冲突时,忽略解析算法跳过包含约束违反的一行,并继续处理SQL语句的后续行,就好像没有出错一样。包含约束冲突的行之前和之后的其他行通常被插入或更新。使用忽略冲突解决算法时不返回错误。

Android insertWithOnConflict()文档指出:

如果输入参数'conflictAlgorithm‘= CONFLICT_IGNORE或-1 (如果有任何错误),则返回新插入行的行ID或现有行的主键

CONFLICT_REPLACE不是一个选项,因为替换行将更改它们的主键,而不只是返回现有的键:

代码语言:javascript
复制
sqlite> INSERT INTO regions (name) VALUES ("Southern");
sqlite> INSERT INTO regions (name) VALUES ("Overseas");
sqlite> SELECT * FROM regions;
1|Southern
2|Overseas
sqlite> INSERT OR REPLACE INTO regions (name) VALUES ("Overseas");
sqlite> SELECT * FROM regions;
1|Southern
3|Overseas
sqlite> INSERT OR REPLACE INTO regions (name) VALUES ("Overseas");
sqlite> SELECT * FROM regions;
1|Southern
4|Overseas

我认为,在重复的行中,insertWithOnConflict()应该返回重复行的主键(_id列)--因此我永远不会收到该插入的错误。为什么insertWithOnConflict()抛出一个错误?我需要调用什么函数才能始终得到有效的行ID?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-02-25 10:26:25

不幸的是,您的问题的答案是,这些文档是完全错误的,并且没有这样的功能。

2010年的一个打开的bug确实解决了这个问题,尽管80+的人也曾在其中担任主角,但安卓团队并没有对此做出官方回应。

问题是也在这里讨论过

如果您的用例冲突很重(即大多数情况下您希望找到一个现有记录并希望返回该ID),那么您建议的解决方法似乎是可行的。另一方面,如果您的用例在大多数情况下都没有现有记录,那么下面的解决方法可能更合适:

代码语言:javascript
复制
try {
  insertOrThrow(...)
} catch(SQLException e) {
  // Select the required record and get primary key from it
} 

下面是这个解决方案的一个独立实现:

代码语言:javascript
复制
public static long insertIgnoringConflict(SQLiteDatabase db,
                                          String table,
                                          String idColumn,
                                          ContentValues values) {
    try {
        return db.insertOrThrow(table, null, values);
    } catch (SQLException e) {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT ");
        sql.append(idColumn);
        sql.append(" FROM ");
        sql.append(table);
        sql.append(" WHERE ");

        Object[] bindArgs = new Object[values.size()];
        int i = 0;
        for (Map.Entry<String, Object> entry: values.valueSet()) {
            sql.append((i > 0) ? " AND " : "");
            sql.append(entry.getKey());
            sql.append(" = ?");
            bindArgs[i++] = entry.getValue();
        }

        SQLiteStatement stmt = db.compileStatement(sql.toString());
        for (i = 0; i < bindArgs.length; i++) {
            DatabaseUtils.bindObjectToProgram(stmt, i + 1, bindArgs[i]);
        }

        try {
            return stmt.simpleQueryForLong();
        } finally {
            stmt.close();
        }
    }
}
票数 31
EN

Stack Overflow用户

发布于 2013-02-22 16:02:32

虽然您对insertWithOnConflict行为的期望似乎是完全合理的(您应该获得冲突行的pk ),但这并不是它的工作方式。实际发生的情况是:尝试插入,它不能插入一行,但没有错误信号,框架计算插入的行数,发现插入数为0,并显式地返回-1。

编辑添加:

顺便说一句,这个答案是基于最终实现insertWithOnConflict的代码:

代码语言:javascript
复制
int err = executeNonQuery(env, connection, statement);
return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
        ? sqlite3_last_insert_rowid(connection->db) : -1;

SQLITE_DONE是很好的状态;sqlite3_changes是上次调用中插入的数量,sqlite3_last_insert_rowid是新插入的行(如果有的话)的rowid。

编辑回答第二个问题:

在重新阅读了这个问题之后,我认为您正在寻找的是这样一种方法:

  • 在数据库中插入新行(如果可能的话)。
  • 如果不能插入行,则失败并返回冲突的现有行的rowid (不更改rowid)。

关于替换的整个讨论似乎是一种“红鲱鱼”。

那么,你的第二个问题的答案是,没有这样的功能。

票数 3
EN

Stack Overflow用户

发布于 2015-09-30 05:35:46

问题已经解决了,但这可能是解决我问题的一个选择。只是将最后一个参数更改为CONFLICT_REPLACE。

代码语言:javascript
复制
long regionId = 
db.insertWithOnConflict("regions", null, cv, SQLiteDatabase.CONFLICT_REPLACE);

希望能帮上忙。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13391915

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档