sqlite3 Setting an "ON CONFLICT" clause to "DELETE FROM table" - sqlite

In sqlite there is an ON CONFLICT clause for inserts and updates, which allows you to do something if an insert or update causes a key violation: http://www.sqlite.org/lang_conflict.html
I want to know if its possible anyway (maybe with triggers) to emulate an UPDATE ON CONFLICT DELETE clause, which is the opposite (kind of) of UPDATE ON CONFLICT REPLACE. Basically, if updating row X creates a conflict with pre-existing row Y, I want to delete row X (because its non-updated form is an error). The REPLACE keyword seems to delete all the Ys and then adds in X. I need other columns (that are not in conflict) of the Y row to remain, not the new X row, so I cannot use REPLACE.

There is no built-in ON CONFLICT clause that implements this algorithm.
You have to implement the duplicate check and the deletion/update in your code.

Related

Correct usage of the SQLite ON CONFLICT clause

I have a SQLite database which, amongst other things, has the following table.
CREATE TABLE IF NOT EXISTS biases
(
data INTEGER NOT NULL,
link INTEGER DEFAULT 0,
bias_type INTEGER,
ignores INTEGER DEFAULT 0,
desists INTEGER DEFAULT 0,
encashes INTEGER DEFAULT 0,
accesses INTEGER DEFAULT 0,
scraps INTEGER DEFAULT 0,
CONSTRAINT pk_bias_mix PRIMARY KEY(data,link,bias_type)
);
The constraint pk_bias_mix is being used to ensure that no two rows can have the same values for all three columns data, link and bias_type columns. So suppose I do
INSERT INTO biases (data,link,bias_type,ignores) VALUES(1,1,1,1);
things work as expected - a new row is inserted in the table. If I issue the same INSERT again I get the error
UNIQUE CONSTRAINT FAILED: biases.data,biases.link,biases.bias_type
just as expected. I tried to use the SQLite ON CONFLICT clause thus
INSERT INTO biases (data,link,bias_type,ignores) VALUES(1,1,1,1)
ON CONFLICT(data,link,bias_type) DO UPDATE SET ignores = ignores + 1;
and it worked as I had hoped - instead of adding a new row or throwing up an error SQLite incremented the value of the ignores column in the row with the matching data, link and bias_type values.
However, this is just the result of an experiment. It is not immediately clear to me from the SQLite docs that this is indeed how ON CONFLICT is supposed to behave - i.e it can be given two or more conflict constraints to be checked. What I mean by two or more constraints is specifying multiple, comma separated, columns inside CONFLICT(...) as I have done in the example above.
I suspect that this is the right usage since I am merely specifying a CONFLICT condition that replicates my indicated CONSTRAINT. However, I cannot see this explained explicitly anywhere in the docs. I'd be much obliged to anyone who might be able to confirm this.
From UPSERT:
UPSERT is a special syntax addition to INSERT that causes the INSERT
to behave as an UPDATE or a no-op if the INSERT would violate a
uniqueness constraint.
and:
The special UPSERT processing happens only for uniqueness constraint
on the table that is receiving the INSERT.
So the DO UPDATE part is not triggered by any constraint conflict but only by a unique constraint violation.
Also:
The syntax that occurs in between the "ON CONFLICT" and "DO" keywords
is called the "conflict target". The conflict target specifies a
specific uniqueness constraint that will trigger the upsert.
So it is not possible to have two or more conflict constraints to be checked in one statement.
However you can use separate UPSERT statements to check for 2 different unique constraint violations.
See a simplified demo where I added 1 more UNIQUE constraint to your table:
CONSTRAINT con_scraps UNIQUE(scraps)

Is SQLite "Insert or Replace" slower than just "Insert"?

I am copying millions of rows to a table in another database. I am doing a few things with the data in-between and have duplicates on a certain column that is used as a key in the destination table. Ignoring all the other solutions to fix this, I am testing out using "Insert or Replace" and so far processing is going smooth, but I am not sure whether this is faster than a normal "Insert" (given a case where there are no PK duplicates)?
The OR REPLACE clause works only if there is some UNIQUE (or PRIMARY KEY) constraint that could be violated.
This means that the database always has to check whether there is a duplicate, the only difference is what happens when a duplicate is found: report an error, or delete the old row.

SQLite: how does REPLACE INTO determine if a row exists?

I found this post explaining the difference between UPDATE and "INSERT OR REPLACE INTO". It explains that
INSERT OR REPLACE INTO names (id, name) VALUES (1, "John")
will insert a new row if no record with id =1 exists, and will replace the row with id = 1 if it does exist. My question is: how does SQLite know or decide that 'id' is the field whose values determine if records already exist or not?
In other words, why wouldn't sqlite search for a record with name = "John" and replace the id value? Does this depend on an index that's not being talked about in the above example, or does SQLite give special treatment to fields named 'id' or fields named first in a row of field names?
See the CONFLICT clause documentation for how this is dealt with. Essentially, it is based on UNIQUE and NOT NULL constraints (primary keys being rather usual as a constraint to select whether to update or insert).
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. If a NOT NULL constraint violation occurs, the REPLACE conflict resolution replaces the NULL value with he default value for that column, or if the column has no default value, then the ABORT algorithm is used. If a CHECK constraint violation occurs, the REPLACE conflict resolution algorithm always works like ABORT.
When the REPLACE conflict resolution strategy deletes rows in order to satisfy a constraint, delete triggers fire if and only if recursive triggers are enabled.
The update hook is not invoked for rows that are deleted by the REPLACE conflict resolution strategy. Nor does REPLACE increment the change counter. The exceptional behaviors defined in this paragraph might change in a future release.

Only update column if new value is higher

I want to add a constraint, so a column only updates its value if the new value passed in is greater than its current value. Otherwise it should silently ignore. In pseudocode:
CREATE TABLE t (col INTEGER CHECK (new.value > col.value) ON CONFLICT IGNORE)
SQLite 3.7.4.
I have decided to use MAX():
UPDATE t SET col = MAX(col, newval) [, col2 = xxx, ...]
Although technically it does overwrite the value, at least it can never be lowered.
When using a WHERE clause to enforce this constraint, other columns (e.g. col2) affected by the UPDATE would not be updated if the clause prohibited a match based on col's value.
I could not work out the correct syntax for a trigger to ignore the UPDATE if the constraint was violated. Regardless, I'd imagine using a trigger would incur much greater overhead than a simple MAX() call.

SQLite "INSERT OR REPLACE INTO" vs. "UPDATE ... WHERE"

I've never seen the syntax INSERT OR REPLACE INTO names (id, name) VALUES (1, "John") used in SQL before, and I was wondering why it's better than UPDATE names SET name = "John" WHERE id = 1. Is there any good reason to use one over the other. Is this syntax specific to SQLite?
UPDATE will not do anything if the row does not exist.
Where as the INSERT OR REPLACE would insert if the row does not exist, or replace the values if it does.
Another fact to notice: INSERT OR REPLACE will replace any values not supplied in the statement.
For instance if your table contains a column "lastname" which you didn't supply a value for, INSERT OR REPLACE will nullify the "lastname" if possible (if constraints allow it), or fail.
REPLACE INTO table(column_list) VALUES(value_list);
is a shorter form of
INSERT OR REPLACE INTO table(column_list) VALUES(value_list);
For REPLACE to execute correctly your table structure must have unique rows, whether a simple primary key or a unique index.
REPLACE deletes, then INSERTs the record and will cause an INSERT Trigger to execute if you have them setup. If you have a trigger on INSERT, you may encounter issues.
This is a work around.. not checked the speed..
INSERT OR IGNORE INTO table (column_list) VALUES(value_list);
followed by
UPDATE table SET field=value,field2=value WHERE uniqueid='uniquevalue'
This method allows a replace to occur without causing a trigger.
The insert or replace query would insert a new record if id=1 does not already exist.
The update query would only oudate id=1 if it aready exist, it would not create a new record if it didn't exist.

Resources