SQLite - UPSERT *not* INSERT or REPLACE
Assuming three columns in the table: ID, NAME, ROLE
BAD: This will insert or replace all columns with new values for ID=1:
INSERT OR REPLACE INTO Employee (id, name, role)
VALUES (1, 'John Foo', 'CEO');
BAD: This will insert or replace 2 of the columns... the NAME column will be set to NULL or the default value:
INSERT OR REPLACE INTO Employee (id, role)
VALUES (1, 'code monkey');
GOOD: Use SQLite On conflict clause
UPSERT support in SQLite! UPSERT syntax was added to SQLite with version 3.24.0!
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. UPSERT is not standard SQL. UPSERT in SQLite follows the syntax established by PostgreSQL.
GOOD but tedious: 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 the default (NULL).
INSERT OR REPLACE INTO Employee (id, role, name)
VALUES ( 1,
'code monkey',
(SELECT name FROM Employee WHERE id = 1)
);
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.
INSERT OR REPLACE INTO Employee (id, name, role)
VALUES ( 1,
'Susan Bar',
COALESCE((SELECT role FROM Employee WHERE id = 1), 'Benchwarmer')
);
SQLite UPSERT / UPDATE OR INSERT
This is a late answer. Starting from SQLIte 3.24.0, released on June 4, 2018, there is finally a support for UPSERT clause following PostgreSQL syntax.
INSERT INTO players (user_name, age)
VALUES('steven', 32)
ON CONFLICT(user_name)
DO UPDATE SET age=excluded.age;
Note: For those having to use a version of SQLite earlier than 3.24.0, please reference this answer below (posted by me, @MarqueIV).
However if you do have the option to upgrade, you are strongly encouraged to do so as unlike my solution, the one posted here achieves the desired behavior in a single statement. Plus you get all the other features, improvements and bug fixes that usually come with a more recent release.
SQLite INSERT OR REPLACE INTO vs. UPDATE ... WHERE
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.
SQLite - update (not replace) if exists, else insert
In SQLite you can do INSERT OR REPLACE
rather than a normal INSERT
if you've defined a primary key on your table. If another row in the table already exists with the primary key you're attempting to insert, it gets overwritten.
So, if you made a compound primary key on your table consisting of word1
and word2
, you could do:
INSERT OR REPLACE INTO link (word1, word2, n)
SELECT
x.word1, x.word2, x.n + COALESCE(l.n, 0)
FROM ( SELECT '%s' AS word1, '%s' AS word2, %d AS n ) x
LEFT JOIN link l ON x.word1 = l.word1 AND x.word2 = l.word2
SQLite INSERT OR REPLACE with condition
This is what i use.
First attempt the update with your condition
then, if not updated, Perform the insert...
Check one example below. calling upsertCategory
/*
* Update a category
*/
public long updateCategory(Category category) {
ContentValues values = new ContentValues();
values.put(COL_CATEGORYDESCRIPTION, category.getDescription());
values.put(COL_CATEGORYSTATUS, category.getStatus());
values.put(COL_CATEGORYCREATEDAT, category.getCreatedAt().getDate());
values.put(COL_CATEGORYUPDATEDAT, category.getUpdatedAt().getDate());
long id = db.update(TABLE_CATEGORIES, values, KEY_ID + "=" + category.getId(), null);
return id;
}
/*
* Update or Insert a category
*/
public long upsertCategory(Category category) {
// update row
long id = updateCategory(category);
// 0 rows affected
if (id == 0) {
// insert row
id = insertCategory(category);
}
return id;
}
How do I use UPSERT in sqlite such that created_at time is preserved?
If there is a unique constraint for the column url
then the syntax for UPSERT is:
INSERT INTO urls(url, title, excerpt, created_at, updated_at) VALUES (?, ?, ?, ?, ?)
ON CONFLICT(url) DO UPDATE
SET title = EXCLUDED.title,
excerpt = EXCLUDED.excerpt,
updated_at = EXCLUDED.updated_at;
If there is also a column count
:
INSERT INTO urls(url, title, excerpt, created_at, updated_at, count) VALUES (?, ?, ?, ?, ?, 1)
ON CONFLICT(url) DO UPDATE
SET title = EXCLUDED.title,
excerpt = EXCLUDED.excerpt,
updated_at = EXCLUDED.updated_at,
count = count + 1;
or if you have defined count
with a default value of 1 it can be omitted from the INSERT list:
INSERT INTO urls(url, title, excerpt, created_at, updated_at) VALUES (?, ?, ?, ?, ?)
ON CONFLICT(url) DO UPDATE
SET title = EXCLUDED.title,
excerpt = EXCLUDED.excerpt,
updated_at = EXCLUDED.updated_at,
count = count + 1;
Sqlite INSERT OR REPLACE is incrementing rowid
This is how INSERT OR REPLACE
works.
If a violation of a unique index/constraint occurs then the existing row is deleted and the new row is inserted, so a new rowid
is assigned for the new row.
If you don't want this to happen you must use UPSERT
:
insert into tablename(Name, Age, CustomerId) values ('Bob', 23, 5)
on conflict(CustomerId) do update
set Name = excluded.Name,
Age = excluded.Age;
See the demo.
UPSERT in SQLite
SQLite has no built-in UPSERT-like statement that doesn't delete the old record.
You should check the number of changes in your program, and execute the INSERT conditionally.
However, if you really want to do this in SQL, it's possible; but you have to use the INSERT ... SELECT ...
form so that you are able to insert zero records, if needed:
BEGIN;
UPDATE t SET a = 'pdf' WHERE id = 2;
INSERT INTO t(id, a) SELECT 2, 'pdf' WHERE changes() = 0;
COMMIT;
Related Topics
Can't Connect to MySQL Server Error 111
Calculate a Running Total in SQL Server
Insert Text With Single Quotes in Postgresql
Passing a Varchar Full of Comma Delimited Values to a SQL Server in Function
Is There a Combination of "Like" and "In" in Sql
Difference Between Single and Double Quotes in Sql
Select N Random Rows from SQL Server Table
SQL Server 2012 Column Identity Increment Jumping from 6 to 1000+ on 7Th Entry
In Sql, How to "Group By" in Ranges
How to Query SQL For a Latest Record Date For Each User
How to Find the MySQL My.Cnf Location
Update Statement With Inner Join on Oracle
How to Request a Random Row in Sql
Get a List of Dates Between Two Dates
Oracle SQL Query: Retrieve Latest Values Per Group Based on Time
Equivalent of Limit and Offset For SQL Server
Does Postgresql Support "Accent Insensitive" Collations
How to Get List of Dates Between Two Dates in MySQL Select Query