【问题标题】:PostgreSQL update if condition is true, but return id based on another condition如果条件为真,PostgreSQL 更新,但根据另一个条件返回 id
【发布时间】:2020-11-24 21:45:35
【问题描述】:

抱歉,如果我遗漏了什么,但我进行了一些搜索,但找不到明显的答案。我有一个系统,其中多个工作人员共享一个 postgres 表,这些工作人员需要根据 ID 更新一些行以表示某些操作已完成。因此,两个工作人员会尝试更新相同的行是非常合理的。

我的表格的简化版本如下所示,三个字段分别是 int、bool 和 timestamp:

+----+-------+--------------------------+
| id | done  |      done_timestamp      |
+----+-------+--------------------------+
|  1 | false | null                     |
|  2 | false | null                     |
|  3 | true  | 2020-11-24T21:23:04.008Z |
+----+-------+--------------------------+

我有一个更新函数,我想用它来更新所有传入的行,但不是任何已经标记为完成的行。它看起来像这样:

UPDATE operations AS o
SET done = TRUE,
done_timestamp = o2.done_timestamp
FROM (VALUES (1, NOW()), (2, NOW()), (3, NOW())) AS o2(id, done_timestamp)
WHERE o2.id = o.id AND NOT o.done;

到目前为止一切顺利。我正在更新三行中的 2 行,而我没有更新第 3 行,因为有些事情已经完成了,我不想在那里更新时间戳。这是我在桌子上得到的:

+----+------+--------------------------+
| id | done |      done_timestamp      |
+----+------+--------------------------+
|  1 | true | 2020-11-24T21:38:29.871Z |
|  2 | true | 2020-11-24T21:38:29.871Z |
|  3 | true | 2020-11-24T21:23:04.008Z |
+----+------+--------------------------+

最后一个难题是我想返回所有已更新的 ID,以便我可以执行一些错误检查。我的第一个想法是添加 RETURNING 语句,但这样做,我只返回此查询已更新的 id,而不是所有已完成的行。查询如下:

UPDATE operations AS o
SET done = TRUE,
done_timestamp = o2.done_timestamp
FROM (VALUES (1, NOW()), (2, NOW()), (3, NOW())) AS o2(id, done_timestamp)
WHERE o2.id = o.id AND NOT o.done
RETURNING o.id;

有没有人有一个好的方法来返回与我传入的 id 匹配的行的 所有 id,并且也被标记为已完成,而不仅仅是那些已经完成的在我的更新查询中特别更新?

提前致谢, F

【问题讨论】:

  • psql 是 Postgres 命令行客户端,真的和问题无关。您想知道“如果条件为 ...,则 Postgresql 更新”。请更改标题以使这个问题更清楚。
  • 啊,抱歉。我是 Postgres 的新手,并认为 psql 只是一个缩写,以使其不那么冗长。已更新。
  • 谢谢。 Postgres 将是名称的“官方”缩写版本。

标签: sql postgresql sql-update


【解决方案1】:

据我所知,您需要欺骗您的服务器才能做到这一点。通过将布尔检查移动到 CASE 语句中,让它认为没有更新;如果 done = TRUE 已经存在,请将其设置为已存在的相同时间戳。

注意:这会导致对表进行不必要的更新,并且会导致查询速度变慢,但看起来 Postgres 不支持任何类型的 MERGE,它会像在其他一些数据库中那样为您返回 ID .

UPDATE operations AS o
SET done_ts = case when done = false then o2.done_ts else o.done_ts end,
    done = TRUE
FROM (VALUES (1, NOW()), (2, NOW()), (3, NOW())) AS o2(id, done_ts)
WHERE o2.id = o.id 
RETURNING o.id;

【讨论】:

  • 是的,我可以看到它是如何工作的,尽管我不得不承认我希望有一个更优雅的解决方案,而不必更新这些行。尤其是因为在实际系统中,我必须使用更多的列来做类似的事情。
【解决方案2】:

未对您的数据进行测试,但使用 [CTE]s(https://www.postgresql.org/docs/current/sql-select.html#SQL-WITH) 的可能解决方案:

WITH val(id, done_timestamp) AS 
        (VALUES (1, NOW()), (2, NOW()), (3, NOW())) , 
updated AS 
    (UPDATE operations AS o
        SET done = TRUE, done_timestamp = val.done_timestamp
    WHERE val.id = o.id AND NOT o.done) 
SELECT id FROM val;

【讨论】:

  • 如果我错了,请纠正我,但是即使它们不在表格中,这个解决方案也不会返回 id 吗?
  • 然后在SELECT 中加入与operations 对应的任何过滤逻辑。
猜你喜欢
  • 1970-01-01
  • 2016-09-30
  • 1970-01-01
  • 2012-01-22
  • 2016-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多