【问题标题】:How to know which column has changed on UPDATE?如何知道 UPDATE 上哪一列发生了变化?
【发布时间】:2020-09-21 10:42:58
【问题描述】:

在这样的声明中:

update tab1 set (col1,col2)=(val1,val2) returning "?"

我发送整行以更新新值,RETURNING * 返还整行,但有没有办法检查在其他列保持不变时哪些列发生了变化?

我知道UPDATE 会重写值,但也许有一些内置函数可以进行这种比较?

【问题讨论】:

  • 可能是更新的触发器?
  • 你能举个例子吗?
  • 如果您自己提供一些数据会很有帮助:1) 创建表语句,2) 示例数据和 3) 重现您的场景的查询。只是为了避免我们误解您的问题。
  • 有办法的。你到底怎么样send whole row for update on new values。请显示您当前的代码。以及“upsert”如何发挥作用。一开始,你只说“更新”。
  • edit将所有定义信息放入问题中。评论不是地方。简短的版本很好,只要主要问题相同。

标签: sql postgresql sql-update


【解决方案1】:

基本上,您需要更新行的 pre-UPDATE 值来比较。这有点困难,因为 RETURNING 只返回 post-UPDATE 状态。但可以解决。见:

所以这是基本的技巧:

WITH input(col1, col2) AS (
   SELECT 1, text 'post_up'  -- "whole row"
   )
, pre_upd AS (
   UPDATE tab1 x
   SET    (col1, col2) = (i.col1, i.col2)
   FROM   input i
   JOIN   tab1  y USING (col1)
   WHERE  x.col1 = y.col1
   RETURNING y.*
   )
TABLE  pre_upd
UNION ALL
TABLE  input;

db小提琴here

这是假设您的示例中的col1PRIMARY KEY。我们需要一些方法来明确地识别行。

请注意,这对于并发写入之间的竞争条件是安全的。为了安全起见,您需要做更多的事情。请参阅上面的相关答案。

我在上面的 CTE 中添加的显式转换为 text 是多余的,因为无论如何 text 是字符串文字的默认类型。 (像 integer 是简单数字文字的默认值。)对于其他数据类型,可能需要显式转换。见:

还要注意,所有更新都会写入一个新的行版本,即使根本没有任何变化。通常,您希望使用适当的WHERE 子句来抑制这种代价高昂的空更新。见:

在“传递整行”时,您必须检查所有可能更改的列才能实现这一点。

【讨论】:

  • 看起来一般(并且希望更安全和更容易)这可以通过查询当前语句在应用程序代码中完成,然后使用“返回 *”比较代码中的值。我希望有一些更简单的解决方案......
  • @IvanKolyhalov:在应用程序代码中执行此操作可简化 SQL 中的逻辑。 但是如果您首先获取更新候选者,则需要两次往返服务器,然后进行实际更新。这扩大了干扰并发的时间窗口。用于防御竞争条件的行锁(或更重的枪)必须保持(更长)时间,这可能会妨碍写入繁重的用例的性能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 1970-01-01
  • 2015-08-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多