http://en.wikipedia.org/wiki/Upsert
Insert Update stored proc on SQL Server
Is there some clever way to do this in SQLite that I have not thought of?
Basically I want to update three out of four columns if the record exists, If it does not exists I want to INSERT the record with the default (NUL) value for the fourth column.
The ID is a primary key so there will only ever be one record to UPSERT.
(I am trying to avoid the overhead of SELECT in order to determin if I need to UPDATE or INSERT obviously)
Suggestions?
I cannot confirm that Syntax on the SQLite site for TABLE CREATE. I have not built a demo to test it, but It doesnt seem to be supported..
If it was, I have three columns so it would actually look like:
CREATE TABLE table1(
id INTEGER PRIMARY KEY ON CONFLICT REPLACE,
Blob1 BLOB ON CONFLICT REPLACE,
Blob2 BLOB ON CONFLICT REPLACE,
Blob3 BLOB
);
but the first two blobs will not cause a conflict, only the ID would So I asusme Blob1 and Blob2 would not be replaced (as desired)
UPDATEs in SQLite when binding data are a complete transaction, meaning Each sent row to be updated requires: Prepare/Bind/Step/Finalize statements unlike the INSERT which allows the use of the reset function
The life of a statement object goes something like this:
- Create the object using sqlite3_prepare_v2()
- Bind values to host parameters using sqlite3_bind_ interfaces.
- Run the SQL by calling sqlite3_step()
- Reset the statement using sqlite3_reset() then go back to step 2 and repeat.
- Destroy the statement object using sqlite3_finalize().
UPDATE I am guessing is slow compared to INSERT, but how does it compare to SELECT using the Primary key?
Perhaps I should use the select to read the 4th column (Blob3) and then use REPLACE to write a new record blending the original 4th Column with the new data for the first 3 columns?
Eric B’s answer is OK if you want to preserve just one or maybe two columns from the existing row. If you want to preserve a lot of columns, it gets too cumbersome fast.
Here’s an approach that will scale well to any amount of columns on either side. To illustrate it I will assume the following schema:
Note in particular that
name
is the natural key of the row –id
is used only for foreign keys, so the point is for SQLite to pick the ID value itself when inserting a new row. But when updating an existing row based on itsname
, I want it to continue to have the old ID value (obviously!).I achieve a true
UPSERT
with the following construct:The exact form of this query can vary a bit. The key is the use of
INSERT SELECT
with a left outer join, to join an existing row to the new values.Here, if a row did not previously exist,
old.id
will beNULL
and SQLite will then assign an ID automatically, but if there already was such a row,old.id
will have an actual value and this will be reused. Which is exactly what I wanted.In fact this is very flexible. Note how the
ts
column is completely missing on all sides – because it has aDEFAULT
value, SQLite will just do the right thing in any case, so I don’t have to take care of it myself.You can also include a column on both the
new
andold
sides and then use e.g.COALESCE(new.content, old.content)
in the outerSELECT
to say “insert the new content if there was any, otherwise keep the old content” – e.g. if you are using a fixed query and are binding the new values with placeholders.Following Aristotle Pagaltzis and the idea of
COALESCE
from Eric B’s answer, here it is an upsert option to update only few columns or insert full row if it does not exist.In this case, imagine that title and content should be updated, keeping the other old values when existing and inserting supplied ones when name not found:
NOTE
id
is forced to be NULL whenINSERT
as it is supposed to be autoincrement. If it is just a generated primary key thenCOALESCE
can also be used (see Aristotle Pagaltzis comment).So the general rule would be, if you want to keep old values, use
COALESCE
, when you want to update values, usenew.fieldname
Assuming 3 columns in the table.. ID, NAME, ROLE
BAD: This will insert or replace all columns with new values for ID=1:
BAD: This will insert or replace 2 of the columns... the NAME column will be set to NULL or the default value:
GOOD: This will update 2 of the columns. When ID=1 exists, the NAME will be unaffected. When ID=1 does not exist, the name will be default (NULL).
This will update 2 of the columns. When ID=1 exists, the ROLE will be unaffected. When ID=1 does not exist, the role will be set to 'Benchwarmer' instead of the default value.
if
COUNT(*) = 0
else if
COUNT(*) > 0
Having just read this thread and been disappointed that it wasn't easy to just to this "UPSERT"ing, I investigated further...
You can actually do this directly and easily in SQLITE.
Instead of using:
INSERT INTO
Use:
INSERT OR REPLACE INTO
This does exactly what you want it to do!
If you are generally doing updates I would ..
If you are generally doing inserts I would
This way you avoid the select and you are transactionally sound on Sqlite.