如何在DuckDb与DuckLake中实现Upsert操作?
在DuckLake中实现Upsert(插入或更新)的方案
由于DuckLake不支持主键约束,也无法使用ON CONFLICT DO UPDATE这类常规Upsert语法,直接插入会导致重复ID的记录被新增而非更新。针对批量的新增和更新需求,可采用以下几种方案:
1. 应用层拆分批量操作
先查询目标表中已存在的ID集合,将待处理数据拆分为「新增ID组」和「已有ID组」,分别执行批量插入和批量更新:
- 第一步:查询已有ID
SELECT "Id" FROM ducklakeexample.demo WHERE "Id" IN ('f3c21234-8e2b-4e1d-b9d2-a11122334455', 'abcd1234-5678-90ab-cdef-112233445566'); - 第二步:对新增ID执行批量插入
INSERT INTO ducklakeexample.demo ("Date","Id","Title", "Quantity") VALUES ('2025-07-02 09:00:00+00', 'abcd1234-5678-90ab-cdef-112233445566', 'Another Title', 75); - 第三步:对已有ID执行批量更新
注意:该方式需考虑并发修改场景,若有其他进程同时操作同ID数据,可能出现不一致,需结合业务做并发控制。UPDATE ducklakeexample.demo SET "Quantity" = 0, "Date" = '2025-07-01 13:44:58.11+00' WHERE "Id" = 'f3c21234-8e2b-4e1d-b9d2-a11122334455'::UUID;
2. 临时表+合并查询(推荐批量场景)
通过临时表统一导入待Upsert数据,再合并到目标表,无需应用层做复杂判断,效率更高:
- 创建临时表并导入数据
CREATE TEMP TABLE temp_demo ( "Date" TIMESTAMP WITH TIME ZONE, "Id" UUID, "Title" TEXT, "Quantity" INTEGER ); INSERT INTO temp_demo VALUES ('2025-07-02 09:00:00+00', 'abcd1234-5678-90ab-cdef-112233445566', 'Another Title', 75), ('2025-07-01 13:44:58.11+00','f3c21234-8e2b-4e1d-b9d2-a11122334455','Some Name',0); - 批量更新已有ID的记录
UPDATE ducklakeexample.demo d SET "Quantity" = t."Quantity", "Date" = t."Date" FROM temp_demo t WHERE d."Id" = t."Id"; - 插入新增ID的记录
INSERT INTO ducklakeexample.demo SELECT t.* FROM temp_demo t LEFT JOIN ducklakeexample.demo d ON t."Id" = d."Id" WHERE d."Id" IS NULL; - 清理临时表
DROP TABLE temp_demo;
3. 基于Time Travel的查询兼容(仅用于读层)
若已产生重复记录,可利用DuckLake的Time Travel特性,在查询时获取每个ID的最新版本:
SELECT DISTINCT ON ("Id") * FROM ducklakeexample.demo ORDER BY "Id", "Date" DESC;
该查询会返回每个ID的最新记录(按Date倒序取第一条),但仅能解决读层数据一致性问题,写入时仍会产生重复,不适合替代Upsert的写入逻辑。
内容的提问来源于stack exchange,提问作者dimButTries
相关产品推荐
相关产品推荐

