TomNash
TomNash

Reputation: 3288

How to perform an upsert on a table with both a primary key and UNIQUE column

New to SQLite and trying to understand the upsert functionality.

I have a table with the following DDL:

CREATE TABLE contacts (
    contact_id INTEGER PRIMARY KEY,
    first_name TEXT NOT NULL,
    last_name TEXT NOT NULL,
    email TEXT NOT NULL UNIQUE,
    phone TEXT NOT NULL UNIQUE
);

Let's say I insert a record:

INSERT INTO contacts (contact_id, first_name, last_name, email, phone)
VALUES (1, 'John', 'Jones', '[email protected]', '888-867-5309');

How can I do an upsert that takes into account both the UNIQUE constraint (email) and the PK constraint ( contact_id) so that it handles either case since I don't know which constraint will fail.

I tried doing this:

INSERT INTO contacts (contact_id, first_name, last_name, email, phone)
VALUES (1, 'John', 'Jones', '[email protected]', '888-867-5309')
ON CONFLICT (contact_id, email) DO UPDATE
SET first_name='John', last_name='Jones', email='[email protected]', phone='888-867-5309'
WHERE contact_id=1;

But I get the error:

sqlite3.OperationalError: ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint

Doing them individually works just fine.

INSERT INTO contacts (contact_id, first_name, last_name, email, phone)
VALUES (1, 'John', 'Jones', '[email protected]', '888-867-5309')
ON CONFLICT (contact_id) DO UPDATE
SET first_name='John', last_name='Jones', email='[email protected]', phone='888-867-5309'
WHERE contact_id=1;
INSERT INTO contacts (contact_id, first_name, last_name, email, phone)
VALUES (1, 'John', 'Jones', '[email protected]', '888-867-5309')
ON CONFLICT (email) DO UPDATE
SET first_name='John', last_name='Jones', email='[email protected]', phone='888-867-5309'
WHERE email='[email protected]';

I understand I get the error because the combination of columns doesn't meet a single constraint, it encompasses two. But how would I take both into account?

Upvotes: 6

Views: 8421

Answers (1)

forpas
forpas

Reputation: 164174

Since you defined:

contact_id INTEGER PRIMARY KEY

contact_id is AUTOINCREMENT and you must not set explicitly a value for this column when you insert a new row (although SQLite would not complain if you do if there is no conflict).
So all you need is:

INSERT INTO contacts (first_name, last_name, email, phone)
VALUES ('John', 'Jones', '[email protected]', '888-867-5309')
ON CONFLICT (email) DO UPDATE
SET first_name='John', last_name='Jones', email='[email protected]', phone='888-867-5309'; 

But, you defined also:

phone TEXT NOT NULL UNIQUE

so there are 2 UNIQUE constraints in your table.
For this case if you want SQLite to deal with conflicts from both columns you can use (INSERT OR) REPLACE:

REPLACE INTO contacts (first_name, last_name, email, phone)
VALUES('Johny', 'Jones', '[email protected]', '888-867-5309')

You must know that REPLACE inserts the new row if there is no conflict (for the columns email and phone in your case), but if there is a conflict then deletes the conflicting row or rows (because there would be 2 conflicting rows one for email and the other for phone) and inserts the new row.

Upvotes: 9

Related Questions