postgresql - postgres upsert 的部分更新违反了约束

标签 postgresql

我希望能够在 postgres (9.5) 中进行部分更新插入,但是当并非所有约束都得到满足时(例如非空约束),部分更新插入似乎会失败

这是场景和错误的示例

CREATE TABLE jobs (
    id integer PRIMARY KEY,
    employee_name TEXT NOT NULL,
    address TEXT NOT NULL,
    phone_number TEXT
);

CREATE OR REPLACE FUNCTION upsert_job(job JSONB)
RETURNS VOID AS $$
BEGIN
INSERT INTO jobs AS origin VALUES(
    (job->>'id')::INTEGER,
    job->>'employee_name'::TEXT,
    job->>'address'::TEXT,
    job->>'phone_number'::TEXT
) ON CONFLICT (id) DO UPDATE SET
    employee_name = COALESCE(EXCLUDED.employee_name, origin.employee_name),
    address = COALESCE(EXCLUDED.address, origin.address),
    phone_number = COALESCE(EXCLUDED.phone_number, origin.phone_number);
END;
$$ LANGUAGE PLPGSQL SECURITY DEFINER;


--Full insert (OK)
SELECT upsert_job('{"id" : 1, "employee_name" : "AAA", "address" : "City, x street no.y", "phone_number" : "123456789"}'::jsonb);

--Partial update that fulfills constraint (Ok)
SELECT upsert_job('{"id" : 1,  "employee_name" : "BBB", "address" : "City, x street no.y"}'::jsonb);

--Partial update that doesn't fulfill constraint (FAILS)
SELECT upsert_job('{"id" : 1,  "phone_number" : "12345"}'::jsonb);

--ERROR:  null value in column "employee_name" violates not-null constraint
--DETAIL:  Failing row contains (1, null, null, 12345).

我该如何解决这个问题?

最佳答案

换个角度想,如果id不存在怎么办?您不能只插入一个电话号码,因为它没有姓名/地址,但这正是您告诉它要做的。所以约束变得疯狂并且它失败了,因为更新插入首先尝试插入然后在插入失败时更新。但是您的插入没有通过约束检查以查看它是否已经存在。

如果你想要部分,你可以做的是告诉它如何处理违反约束的部分。像这样的东西(这不完整并且不处理所有部分数据场景):

CREATE OR REPLACE FUNCTION upsert_job(job JSONB)
RETURNS VOID AS $$
BEGIN
IF (job->>'phone_number' IS NOT NULL 
    AND job->>'employee_name' IS NOT NULL 
    AND job->>'address' IS NOT NULL) THEN
    INSERT INTO jobs AS origin VALUES(
        (job->>'id')::INTEGER,
        job->>'employee_name'::TEXT,
        job->>'address'::TEXT,
        job->>'phone_number'::TEXT
    ) ON CONFLICT (id) DO UPDATE SET
        employee_name = COALESCE(EXCLUDED.employee_name, origin.employee_name),
        address = COALESCE(EXCLUDED.address, origin.address),
        phone_number = COALESCE(EXCLUDED.phone_number, origin.phone_number);
ELSIF (job->>'phone_number' IS NOT NULL AND (job->>'employee_name' IS NULL AND job->>'address' IS NULL)) THEN
    UPDATE jobs SET phone_number=job->>'phone_number'::TEXT
    WHERE id=(job->>'id')::INTEGER;
END IF;

END;
$$ LANGUAGE PLPGSQL SECURITY DEFINER;

关于postgresql - postgres upsert 的部分更新违反了约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40669555/

相关文章:

database - 编写一个 postgresql 查询,返回以 'sa' 开头并以 's' 结尾的名称列表

PostgreSQL 和 OS X Lion 权限问题 .bash_profile

java - 由于多个 Java 错误,无法编译自动生成的 JOOQ 代码

java - 无法将 [B 字段 fr.mypackage.MyClass.data 设置为 java.lang.String

java - ?不会替换为 $$ 中的值

sql - 获取时间跨度内的 session 持续时间平均值

java - 使用 findAll() Spring JPA PostgreSQL 时出现无限循环

postgresql - 使用自定义 postgres 扩展时外部数据库请求失败

postgresql - 如何语法检查 PostgreSQL 配置文件?

java - 类型为“没有时区的时间戳”的数据库中的UTC时间作为响应显示在UTC中