postgresql INSERT ON CONFLICT DO UPDATE与RETURNING语句一起使用时返回意外值[closed]

tpxzln5u  于 2023-06-05  发布在  PostgreSQL
关注(0)|答案(1)|浏览(328)

**已关闭。**此问题为not reproducible or was caused by typos。目前不接受答复。

此问题是由打印错误或无法再重现的问题引起的。虽然类似的问题在这里可能是on-topic,但这个问题的解决方式不太可能帮助未来的读者。
5天前关闭。
Improve this question
我有一个users表,其中包含idnamecreated_atupdated_atcreated_atupdated_at都具有默认为当前时间戳的默认值。我尝试在此表中添加如下内容。

INSERT INTO users (
    id,
    name,
    created_at,
    updated_at
) VALUES (
    '0e22e5b9-3826-401c-824b-ba12b1b4eb0a'
    'Some User',
     NOW(),
     NOW()
) ON CONFLICT (
    id
) DO UPDATE SET
    name = excluded."name",
    updated_at = excluded."updated_at"
RETURNING
    "id",
    "name",
    "created_at",
    "updated_at";

我想确保当插入发生时,“created_at”和“updated_at”都被设置,但是当有冲突并且只发生更新时,那么只有“updated_at”被更新,我想返回最后一行。我希望如果我有一个现有的行
| id|姓名|创建于|更新_at|
| - -----|- -----|- -----|- -----|
| 1| 0e22e5b9-3826-401c-824b-ba12b1b4eb0a|一些名字|2023年3月1日|
两天后我更新了名称,我会从return语句中得到类似这样的结果,其中只有updated_at发生了变化
| id|姓名|创建于|更新_at|
| - -----|- -----|- -----|- -----|
| 1| 0e22e5b9-3826-401c-824b-ba12b1b4eb0a| Abcd名称|2023年3月1日|
但是我得到的结果是created_atupdated_at都被更改了
| id|姓名|创建于|更新_at|
| - -----|- -----|- -----|- -----|
| 1| 0e22e5b9-3826-401c-824b-ba12b1b4eb0a| Abcd名称|2023年3月3日|
此问题仅与查询运行后的返回值有关,表中的实际值仍然正确。我假设这个行为与RETURNING命令如何与upsert查询交互有关,但是我在任何地方都找不到关于这个行为的文档。我知道有解决办法,但我只是想知道为什么这个特定的方法不起作用。这是PostreSQL的错误还是预期行为?

fafcakar

fafcakar1#

您所遇到的行为是PostgreSQL在使用RETURNING子句与INSERT ...ON CONFLICT ...DO UPDATE语句组合时的预期行为。此行为并不特定于RETURNING子句,而是ON CONFLICT ...DO UPDATE语句的工作方式。
当发生冲突并触发DO UPDATE子句时,SET子句中指定的所有列都将被更新,无论它们是否在EXCLUDED行中实际更改。这是DO UPDATE子句的默认行为。
在本例中,created_at和updated_at都在SET子句中指定,因此即使created_at保持不变,它仍然会使用excluded.“created_at”中的值进行更新,这与本例中的NOW()相同。
要实现所需的行为,其中created_at在更新期间保持不变,您可以按如下方式修改查询:

INSERT INTO users (
    id,
    name,
    created_at,
    updated_at
) VALUES (
    '0e22e5b9-3826-401c-824b-ba12b1b4eb0a',
    'Some User',
     NOW(),
     NOW()
) ON CONFLICT (id) DO UPDATE SET
    name = excluded."name",
    updated_at = excluded."updated_at",
    created_at = users.created_at -- Keep the original value of created_at
RETURNING
    "id",
    "name",
    "created_at",
    "updated_at";

通过显式设置created_at = users.created_at,可以确保created_at列在更新期间保留其原始值,并且只有updated_at列得到更新。
这种行为不是bug,而是PostgreSQL实现ON CONFLICT ...DO UPDATE语句时的一种设计选择。它旨在提供在冲突期间更新列的灵活性,即使值没有更改。

相关问题