You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Supabase Upsert在带默认值的非空字段上执行失败求助

Supabase Upsert混合操作触发created_at非空约束错误的解决

问题背景

使用以下代码执行Supabase Upsert操作:

export const updateChecklistData = async (values: BlueprintChecklistInsert[]) => {
    const { error } = await supabase.getClient().from('blueprint_checklist').upsert(values, { onConflict: 'id' });
    if (error) {
        throw error;
    }
};
  • 单独插入新数据(无created_at字段)时操作正常:
{
    "title": "Keyboard",
    "blueprint_id": "f373e251-da55-4d3d-b4f4-69b4c7b26c7a",
    "id": "030d70a9-e681-476d-a9c4-c3a9ef1399ec",
    "order": 1,
    "parent_id": "9a09f8c7-b255-4725-a228-e5b8f79404c1",
    "event_type": "creation",
    "workspace_id": "7db6e71c-a24c-4710-b63a-728c9413a0cf"
}
  • 同时更新已有记录(含created_at)和插入新记录时,抛出错误:

null value in column "created_at" of relation "blueprint_checklist" violates not-null constraint

传入的混合数据示例:

[
    {
        "id": "b8b1f216-13c9-446c-b2d5-84881ff5cac4",
        "created_at": "2024-07-27T19:10:04.526662+00:00",
        "title": "Laptop",
        "blueprint_id": "f373e251-da55-4d3d-b4f4-69b4c7b26c7a",
        "workspace_id": "7db6e71c-a24c-4710-b63a-728c9413a0cf",
        "event_type": "creation",
        "description": null,
        "order": 0,
        "parent_id": "9a09f8c7-b255-4725-a228-e5b8f79404c1",
        "remind": null,
        "archived": false
    },
    {
        "title": "Keyboard",
        "blueprint_id": "f373e251-da55-4d3d-b4f4-69b4c7b26c7a",
        "id": "030d70a9-e681-476d-a9c4-c3a9ef1399ec",
        "order": 1,
        "parent_id": "9a09f8c7-b255-4725-a228-e5b8f79404c1",
        "event_type": "creation",
        "workspace_id": "7db6e71c-a24c-4710-b63a-728c9413a0cf"
    }
]

表结构定义:

create table "public"."blueprint_checklist" (
    "id" uuid not null default gen_random_uuid(),
    "created_at" timestamp with time zone not null default now(),
    -- 其他字段省略
);

created_at为非空字段,默认值为now()。

原因分析

Supabase的upsert默认采用合并更新逻辑,当批量操作中部分记录包含created_at、部分不包含时,PostgreSQL会将created_at视为需要统一处理的字段。对于未传入created_at的新记录,系统不会自动使用表的默认值,而是将其视为null,触发非空约束。

解决方案

方案1:添加defaultToNull: false配置(推荐)

在upsert选项中设置defaultToNull: false,让未传入的字段自动使用表定义的默认值,而非设为null:

export const updateChecklistData = async (values: BlueprintChecklistInsert[]) => {
    const { error } = await supabase.getClient().from('blueprint_checklist').upsert(values, { 
        onConflict: 'id',
        defaultToNull: false
    });
    if (error) {
        throw error;
    }
};

方案2:手动给新记录补充created_at值

遍历批量数据,为缺失created_at的记录补充当前时间:

export const updateChecklistData = async (values: BlueprintChecklistInsert[]) => {
    const processedValues = values.map(item => 
        item.created_at ? item : { ...item, created_at: new Date().toISOString() }
    );
    const { error } = await supabase.getClient().from('blueprint_checklist').upsert(processedValues, { onConflict: 'id' });
    if (error) {
        throw error;
    }
};

方案3:拆分插入和更新操作

将新记录和已有记录分开处理,先插入新记录(自动使用默认值),再更新已有记录:

export const updateChecklistData = async (values: BlueprintChecklistInsert[]) => {
    const existing = values.filter(item => item.created_at);
    const newItems = values.filter(item => !item.created_at);

    if (newItems.length) {
        const { error: insertErr } = await supabase.getClient().from('blueprint_checklist').insert(newItems);
        if (insertErr) throw insertErr;
    }

    if (existing.length) {
        const { error: updateErr } = await supabase.getClient().from('blueprint_checklist').upsert(existing, { onConflict: 'id' });
        if (updateErr) throw updateErr;
    }
};

内容的提问来源于stack exchange,提问作者Alwaysblue

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 13:50:55