如何对同时具有主键和 UNIQUE 列的表执行 upsert
Posted
技术标签:
【中文标题】如何对同时具有主键和 UNIQUE 列的表执行 upsert【英文标题】:How to perform an upsert on a table with both a primary key and UNIQUE column 【发布时间】:2020-02-13 07:56:27 【问题描述】:SQLite 新手并试图了解 upsert 功能。
我有一个包含以下 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
);
假设我插入一条记录:
INSERT INTO contacts (contact_id, first_name, last_name, email, phone)
VALUES (1, 'John', 'Jones', 'jjones@gmail.com', '888-867-5309');
我如何做一个考虑到 UNIQUE 约束 (email
) 和 PK 约束 (contact_id
) 的 upsert,以便它处理这两种情况,因为我不知道哪个约束会失败。
我试过这样做:
INSERT INTO contacts (contact_id, first_name, last_name, email, phone)
VALUES (1, 'John', 'Jones', 'john.jones@gmail.com', '888-867-5309')
ON CONFLICT (contact_id, email) DO UPDATE
SET first_name='John', last_name='Jones', email='john.jones@gmail.com', phone='888-867-5309'
WHERE contact_id=1;
但我得到了错误:
sqlite3.OperationalError: ON CONFLICT 子句不匹配任何 PRIMARY KEY 或 UNIQUE 约束
单独做就可以了。
INSERT INTO contacts (contact_id, first_name, last_name, email, phone)
VALUES (1, 'John', 'Jones', 'john.jones@gmail.com', '888-867-5309')
ON CONFLICT (contact_id) DO UPDATE
SET first_name='John', last_name='Jones', email='john.jones@gmail.com', phone='888-867-5309'
WHERE contact_id=1;
INSERT INTO contacts (contact_id, first_name, last_name, email, phone)
VALUES (1, 'John', 'Jones', 'john.jones@gmail.com', '888-867-5309')
ON CONFLICT (email) DO UPDATE
SET first_name='John', last_name='Jones', email='john.jones@gmail.com', phone='888-867-5309'
WHERE email='john.jones@gmail.com';
我知道我收到错误是因为列的组合不满足单个约束,它包含两个。但是我该如何兼顾两者呢?
【问题讨论】:
也许INSERT OR REPLACE
会做你想做的事。
【参考方案1】:
既然你定义了:
contact_id INTEGER PRIMARY KEY
contact_id
是 AUTOINCREMENT
并且您必须不在插入新行时显式设置此列的值(尽管如果没有冲突,SQLite 不会抱怨)。
所以你只需要:
INSERT INTO contacts (first_name, last_name, email, phone)
VALUES ('John', 'Jones', 'john.jones@gmail.com', '888-867-5309')
ON CONFLICT (email) DO UPDATE
SET first_name='John', last_name='Jones', email='john.jones@gmail.com', phone='888-867-5309';
但是,你也定义了:
phone TEXT NOT NULL UNIQUE
所以您的表中有 2 个UNIQUE
约束。
对于这种情况,如果您希望 SQLite 处理来自两列的冲突,您可以使用 (INSERT OR) REPLACE
:
REPLACE INTO contacts (first_name, last_name, email, phone)
VALUES('Johny', 'Jones', 'john.jones@gmail.com', '888-867-5309')
您必须知道,如果没有冲突,REPLACE
会插入新行(在您的情况下是 email
和 phone
列),但如果存在冲突,则删除冲突的行(因为将有 2 个冲突行,一个用于 email
,另一个用于 phone
)并插入新行。
【讨论】:
以上是关于如何对同时具有主键和 UNIQUE 列的表执行 upsert的主要内容,如果未能解决你的问题,请参考以下文章