When a UNIQUE
constraint violation occurs, the REPLACE
algorithm deletes pre-existing rows that are causing the constraint violation prior to inserting or updating the current row and the command continues executing normally. This causes the rowid
to change and creates the following problem:
Y:> sqlite3 test
SQLite version 3.7.4
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table b (c1 integer primary key, c2 text UNIQUE);
sqlite> insert or replace into b values (null,'test-1');
sqlite> select last_insert_rowid();
1
sqlite> insert or replace into b values (null,'test-2');
sqlite> select last_insert_rowid();
2
sqlite> insert or replace into b values (null,'test-1');
sqlite> select last_insert_rowid();
3
sqlite> select * from b;
2|test-2
3|test-1
The work around is to change the definition of the c2
column as follows:
create table b (c1 integer primary key, c2 text UNIQUE ON CONFLICT IGNORE);
and to remove the or replace
clause from your insert
s;
then when test after your insert
, you will need to execute the following sql: select last_insert_rowid(), changes();
sqlite> create table b (c1 integer primary key, c2 text UNIQUE ON CONFLICT IGNORE);
sqlite> insert into b values (null,'test-1');
sqlite> select last_insert_rowid(), changes();
1|1
sqlite> insert into b values (null,'test-2');
sqlite> select last_insert_rowid(), changes();
2|1
sqlite> insert into b values (null,'test-1');
sqlite> select last_insert_rowid(), changes();
2|0
The return value of changes after the 3rd insert
will be a notification to your application that you will need to lookup the rowid
of "test-1"
, since it was already on file. Of course if this is a multi-user system, you will need to wrap all this in a transaction as well.